The XML CD Bookshelf

The XML Bookshelf

XML in a Nutshell, 2nd Edition
By Elliotte Rusty Harold and W. Scott Means
XSLT
By Doug Tidwell
XML Schema
By Eric van der Vlist
SAX2
By David Brownell
Java & XML, 2nd Edition
By Brett McLaughlin
Java and XSLT
By Eric M. Burke
Perl & XML
By Erik T. Ray and Jason McIntosh
Search All Books
Master Index
Copyright
Credits

The XML CD Bookshelf, Version 1.0, is Copyright © 2002 by O'Reilly & Associates, Inc. All rights reserved.
Questions, comments, and suggestions to bookquestions@oreilly.com.

This product is printed with 100% recycled electrons.


The XML CD Bookshelf

The XCL CD BookshelfSearch this book

Copyright © 2002 by O'Reilly & Associates, Inc. All rights reserved.

The XML CD Bookshelf, Version 1.0
By O'Reilly & Associates, Inc.
ISBN: 0-596-00335-8
Published by O'Reilly & Associates, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

Manufacturing History

November 2002: Version 1.0

This CD-ROM is intended for use by one individual. As such, you may make copies for your own personal use. However, you may not provide copies to others, or make this reference library available to others over a LAN or other network. You may not reprint, offer for sale, or otherwise re-use material from this CD without the explicit written permission of O'Reilly & Associates, Inc. If you would like to discuss licensing for multiple users, contact service@oreilly.com.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles at safari.oreilly.com. For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. You can purchase print editions of these books directly from O'Reilly & Associates, Inc. or from bookstores that carry O'Reilly & Associates books.

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this CD, and O'Reilly & Associates, Inc., was aware of the trademark claim, the designations have been printed in caps or initial caps.

While every precaution has been taken in the preparation of this CD, the publisher assumes no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

The XML CD Bookshelf, Version 1.0, is Copyright © 2002 by O'Reilly & Associates, Inc. All rights reserved.


The XML CD Bookshelf

The XML CD BookshelfSearch this book

The CD

Project Manager
Ellie Cutler
Technical Development and Tools
Erik Ray, Ellie Cutler
Graphics and Design
Ellie Volckhausen, Emma Colby
Production and Testing
Tony Castro, Ellie Cutler, Greg Dickerson, Julie Drouin, Paul LaBrier,
Chris Olson, Becki Maisch, Madeleine Newell
CD-ROM Search Engine
QuestAgent Pro version 4.0.9 from JObjects.

The Books

XML in a Nutshell, 2nd Edition
Elliotte Rusty Harold and W. Scott Means
XSLT
Doug Tidwell
XML Schema
Eric van der Vlist
SAX2
David Brownell
Java & XML, 2nd Edition
Brett McLaughlin
Java and XSLT
Eric M. Burke
Perl & XML
Erik T. Ray and Jason McIntosh

The XML CD Bookshelf, Version 1.0, is Copyright © 2002 by O'Reilly & Associates, Inc. All rights reserved.


XML CD Bookshelf: Index

 The XML CDSearch this CD

Master Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Symbols & Numbers

Symbols

- (hyphen) in PYX notation: 4.5. XML::PYX (Perl & XML)
- (minus sign):
for ranges in user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
XPath arithmetic operator: 9.6.1. Numbers (XML in a Nutshell)
( ) (parenthesis) in PYX notation: 4.5. XML::PYX (Perl & XML)
` ' single quotes (see single quotes)
\- special character: 6.4.1. Special Characters (XML Schema)
[ ] (square brackets): 2.5. Entities (Perl & XML)
& (ampersand): 2.1.1.4. Entity references and constants (Java & XML)
16.2. XPointer (Java & XML)
* (asterisk)
CSS universal selector: 12.4.1. The Universal Selector (XML in a Nutshell)
node test: 22.3.3. Node Tests (XML in a Nutshell)
suffix for element names: 3.2.4. The Number of Children (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
XPath arithmetic operator: 9.6.1. Numbers (XML in a Nutshell)
XPath wildcard: 9.2.5. Wildcards (XML in a Nutshell)
@ at-sign: 3.2.4.1. Selecting attributes (XSLT)
8.2. XPath (Perl & XML)
3.2.6.2. Axis roll call (XSLT)
@* at-sign and asterisk: 3.2.5. Using Wildcards (XSLT)
XPath wildcard: 9.2.5. Wildcards (XML in a Nutshell)
@ (attribute name prefix): 2.3.3.2. Looping (Java & XML)
\ (backslash) escape character: 6.2. The Simplest Possible Patterns (XML Schema)
special characters: 6.4.1. Special Characters (XML Schema)
user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
^ caret: 4.6.1. Implementing a String Replace Function (XSLT)
for negating lists in user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
: (colon), use within names: 20.3.1. Global Syntax Structures (XML in a Nutshell)
:: double colon: 3.2.6. Axes (XSLT)
axis-node test separator: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
using boolean tests in XPath: 8.2. XPath (Perl & XML)
// double slash operator: 3.2.5. Using Wildcards (XSLT)
selecting from all descendants: 9.3.2. Selecting from Descendants with // (XML in a Nutshell)
.. double period: 3.2.6.2. Axis roll call (XSLT)
selecting parent element: 9.3.3. Selecting the Parent Element with .. (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
== (equal to), testing string equality: 3.3.2. XMLReader Feature Flags (SAX2)
= (equals sign), CSS attribute value selector: 12.4.3. Attribute Selectors (XML in a Nutshell)
/ (forward slash): 2.1.1.2. Elements (Java & XML)
2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
well-formed documents and: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
XPath expressions: 8.2. XPath (Perl & XML)
3.1.1. The Root Node (XSLT)
3.2.3. Relative and Absolute Expressions (XSLT)
building compound location paths: 9.3.1. Building Compound Location Paths from Location Steps with / (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
root location path: 9.2.1. The Root Location Path (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
# hash mark: 5.1.3. A Stylesheet That Uses the id( ) Function (XSLT)
!= (not equal to), testing string equality: 3.3.2. XMLReader Feature Flags (SAX2)
( ) (parentheses) group character: 6.4.4. Oring and Grouping (XML Schema)
in element declarations: 3.2.6. Parentheses (XML in a Nutshell)
% (percent sign) in parameter entities: 3.3.2. XMLReader Feature Flags (SAX2)
4.3.1. The DeclHandler Interface (SAX2)
. period: 3.2.6.2. Axis roll call (XSLT)
selecting context node: 9.3.4. Selecting the Context Node with . (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
splitting property names on: 7.2.3. Creating XML with JDOM (Java & XML)
wildcard character: 6.4.2. Wildcard (XML Schema)
+ (plus sign)
CSS sibling element selector: 12.4.2. Matching Descendants, Children, and Siblings (XML in a Nutshell)
suffix for element names: 3.2.4. The Number of Children (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
XPath arithmetic operator: 9.6.1. Numbers (XML in a Nutshell)
? (question mark):
in PYX notation: 4.5. XML::PYX (Perl & XML)
suffix for element names: 3.2.4. The Number of Children (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
recurrence modifier: 2.2.1.1. Elements (Java & XML)
; (semicolon): 2.1.1.4. Entity references and constants (Java & XML)
3.1. XML Parsers (Perl & XML)
# (sharp sign), CSS attribute selector: 12.4.3. Attribute Selectors (XML in a Nutshell)
\{ special character: 6.4.1. Special Characters (XML Schema)
[] (square brackets), CSS attribute selector: 12.4.3. Attribute Selectors (XML in a Nutshell)
for user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
~= (tilde equals sign), CSS attribute selector: 12.4.3. Attribute Selectors (XML in a Nutshell)
| vertical bar: 4.6.1. Implementing a String Replace Function (XSLT)
multiple XPath matches: 9.2.6. Multiple Matches with | (XML in a Nutshell)
or character: 6.4.4. Oring and Grouping (XML Schema)

Numbers

80/20 rule for application requirements: 1.4.2. SAX2 (SAX2)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

A4-sized pages, PDF files for: 9.1. About the Toot-O-Matic (XSLT)
9.3.7. PDF Files (XSLT)
generating: 9.5.6. Generating the PDF Files (XSLT)
icons for: 9.3.1. Menu Panel (XSLT)
A (attribute) in PYX notation: 4.5. XML::PYX (Perl & XML)
abbreviated location paths: 9.2. Location Paths (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
abbreviated syntax: 3.2.6.1. Unabbreviated syntax (XSLT)
absolute expressions: 3.2.3. Relative and Absolute Expressions (XSLT)
absolute location paths: 9.2.1. The Root Location Path (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
building compound location paths: 9.3.1. Building Compound Location Paths from Location Steps with / (XML in a Nutshell)
id( ) and: 9.7.1. Node-Set Functions (XML in a Nutshell)
absolute locator in XPath: 8.2. XPath (Perl & XML)
absolute units of length (CSS properties): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
abstract attribute: 11.2.2. Defining Complex Types (XML Schema)
16.9.1. Abstract Elements and Types (XML in a Nutshell)
complex type definitions and: 12.2.3.3. Abstract complex types (XML Schema)
defining head elements: 12.1.2.2. Abstract elements (XML Schema)
substitution groups and: 12.2.2.3. Abstract elements (XML Schema)
AbstractDOMAdapter class: A.4.2.1. AbstractDOMAdapter (Java & XML)
abstraction layers: 9.1. API or Abstraction (Java & XML)
Academia Sinica Computing Centre: A.3.4. Schematron (XML Schema)
acceptNode( ): 6.3.2.1. NodeIterator (Java & XML)
accessDatabase( ) method: 8.3.2. Accessing a Database with an Extension Element (XSLT)
accessibility: 9.3.1. Menu Panel (XSLT)
accessors, polymorphic: 11.2.1. Defining Simple Types (XML Schema)
active pseudoclass: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
ActiveState: 1.4. A Myriad of Modules (Perl & XML)
actuate attribute (XLink): 10.2.2. xlink:actuate (XML in a Nutshell)
Adapter design pattern: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
addAttribute( ) function: 5.1.1. The AttributesImpl Class (SAX2)
addBook.pl: 14.1.2. Entering the Books (Java & XML)
14.1.3. Providing a Listing of Available Books (Java & XML)
addBooks.html: 14.1.2. Entering the Books (Java & XML)
addContent( ): 7.2.3. Creating XML with JDOM (Java & XML)
7.3.1. Storing XML (Java & XML)
addEvent( ): 11.3.1. A Shared Handler (Java & XML)
11.3.1. A Shared Handler (Java & XML)
Thread, starting: 11.3.1. A Shared Handler (Java & XML)
addHandler( ): 11.2.3. Writing the Server (Java & XML)
AddItemServlet: 15.2.3. Marshalling and Unmarshalling (Java & XML)
add_links( ) method: 7.3. XML::DOM (Perl & XML)
7.3. XML::DOM (Perl & XML)
add_parser( ) function: 5.7.1. XML::SAX::ParserFactory (Perl & XML)
addresses (see postal addresses)
addresses (code points): 3.9.2. Unicode Encodings (Perl & XML)
Adobe Zapf Dingbats font, Unicode block for: 26.3.26. Dingbats (XML in a Nutshell)
adornments, schema: 14.2. The W3C XML Schema Annotation Element (XML Schema)
AElfred Java parser: 1.4.1. SAX1 (SAX2)
1.6.1. Ælfred2 (SAX2)
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags (SAX2)
after pseudoelement: 12.4.5. Pseudoelement Selectors (XML in a Nutshell)
#all value (block attribute): 12.2.2.1. Block attribute (XML Schema)
12.2.2.1. Block attribute (XML Schema)
alphabetizing (see sorting)
alternate pseudoattribute: 7.2.1.4. The alternate and title pseudoattributes (XML in a Nutshell)
12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
always validating parsers: 2.4.1. SAX2 Feature Flags (SAX2)
ambiguous content models: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
American National Standards Institute (ANSI): 2.1. A Brief History of XML (Perl & XML)
& ampersand: 4.6.1. Implementing a String Replace Function (XSLT)
D.13. How Do I Control Angle Brackets and Quote Marks in My Output? (XSLT)
controlling: D.13. How Do I Control Angle Brackets and Quote Marks in My Output? (XSLT)
escaping: 4.6.1. Implementing a String Replace Function (XSLT)
& (ampersands): 2.5. Entities (Perl & XML)
2.5. Entities (Perl & XML)
3.8. XML::Writer (Perl & XML)
3.8. XML::Writer (Perl & XML)
parsers and: 3.1. XML Parsers (Perl & XML)
3.1. XML Parsers (Perl & XML)
well-formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
&amp; (ampersand) entity reference: 2.5. Entity References (XML in a Nutshell)
3.4. General Entity Declarations (XML in a Nutshell)
declaring: 20.4.2. Validity Constraints (XML in a Nutshell)
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets (XML in a Nutshell)
XML specification for: 20.3.1. Global Syntax Structures (XML in a Nutshell)
&apos; (apostrophe) entity reference: 2.5. Entity References (XML in a Nutshell)
3.4. General Entity Declarations (XML in a Nutshell)
declaring: 20.4.2. Validity Constraints (XML in a Nutshell)
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets (XML in a Nutshell)
XML specification for: 20.3.1. Global Syntax Structures (XML in a Nutshell)
&apos; entity, quoting attribute values and: D.1. How Do I Put Quotes Inside an Attribute Value? (XSLT)
&amp: 16.2. XPointer (Java & XML)
as entity reference: 2.1.1.4. Entity references and constants (Java & XML)
&apos (entity reference): 2.1.1.4. Entity references and constants (Java & XML)
&quot (entity reference): 2.1.1.4. Entity references and constants (Java & XML)
&gt (entity reference): 2.1.1.4. Entity references and constants (Java & XML)
&lt (entity reference): 2.1.1.4. Entity references and constants (Java & XML)
&gt; entity: 4.2.1. The <xsl:if> Element (XSLT)
&gt; (greater-than) entity reference: 2.5. Entity References (XML in a Nutshell)
3.4. General Entity Declarations (XML in a Nutshell)
declaring: 20.4.2. Validity Constraints (XML in a Nutshell)
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets (XML in a Nutshell)
XML specification for: 20.3.1. Global Syntax Structures (XML in a Nutshell)
&lt; entity: 4.2.1. The <xsl:if> Element (XSLT)
&lt; (less-than) entity reference: 2.5. Entity References (XML in a Nutshell)
3.4. General Entity Declarations (XML in a Nutshell)
declaring: 20.4.2. Validity Constraints (XML in a Nutshell)
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets (XML in a Nutshell)
XML specification for: 20.3.1. Global Syntax Structures (XML in a Nutshell)
&quot; entity, quoting attribute values and: D.1. How Do I Put Quotes Inside an Attribute Value? (XSLT)
&quot; (straight double quotes) entity reference: 2.5. Entity References (XML in a Nutshell)
3.4. General Entity Declarations (XML in a Nutshell)
declaring: 20.4.2. Validity Constraints (XML in a Nutshell)
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets (XML in a Nutshell)
XML specification for: 20.3.1. Global Syntax Structures (XML in a Nutshell)
ancestor axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
7.4.1. The document( ) Function and Sorting (XSLT)
ancestor nodes: 6.1. XML Trees (Perl & XML)
ancestor-or-self axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
angle brackets (< >): 2.2. Markup, Elements, and Structure (Perl & XML)
2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
controlling: D.13. How Do I Control Angle Brackets and Quote Marks in My Output? (XSLT)
parsers and: 3.1. XML Parsers (Perl & XML)
surrounding tags: 1.2.2.10. Tags versus elements (XSLT)
well-formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
annotation elements in schemas: 16.2.2. Annotations (XML in a Nutshell)
anonymous types: 16.4. Complex Types (XML in a Nutshell)
anonymous vs. named datatypes: 7.2.2. Named Versus Anonymous Types (XML Schema)
ANSI (American National Standards Institute): 2.1. A Brief History of XML (Perl & XML)
Antenna House XSL Formatter: 13.3.2. Generating the Finished Document (XML in a Nutshell)
ANY element type: 3.2.9. ANY (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
##any wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
anySimpleType type: 4.7. What About anySimpleType? (XML Schema)
Apache
Cocoon v2 project: 4.5. XML Pipelines (SAX2)
Software License: 1.6.2. Crimson (SAX2)
XML project: 1.6.3. Xerces (SAX2)
Apache Axis: 12.2.1.3. What's Axis? (Java & XML)
Apache Cocoon (see Cocoon)
Apache SOAP: 12.2. Setting Up (Java & XML)
WSDL, limited functionality with: 13.3. WSDL (Java & XML)
Apache Xerces: 1.3.2. A Parser (Java & XML)
parser: 3.1.1. Obtaining a Parser (Java & XML)
Apache XML Project
Cocoon (XSLT processor): 8.3. Stylesheet Processors (XML in a Nutshell)
FOP (formatting program): 13.3.2. Generating the Finished Document (XML in a Nutshell)
Xalan (XSLT processor): 8.3. Stylesheet Processors (XML in a Nutshell)
Xerces-J (parser): 2.10. Checking Documents for Well-Formedness (XML in a Nutshell)
16.2. Schema Basics (XML in a Nutshell)
Apache's XNI: B.3.2. APIs (XML Schema)
APIs (application program interfaces): 1.2.1. Low-Level APIs (Java & XML)
use cases: 15.1.4. Use Cases (Java & XML)
APIs (Application Programming Interfaces): 1. The Simple API for XML (SAX2)
consumers and producers: 2.1. Producers and Consumers (SAX2)
high-level, flexibility of: 1.2.2. Data Structure Flexibility (SAX2)
JAXP (Java API for XML Processing): 1.4.5. Sun's Java API for XML Processing (JAXP) (SAX2)
types of XML: 1.1. Types of XML APIs (SAX2)
APL programming language symbols, Unicode block for: 26.3.22. Miscellaneous Technical (XML in a Nutshell)
appendChild( ): 6.1.1. Creating a New DOM Tree (Java & XML)
appendChild( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
appendChild method for Node class (DOM): 7.2.4.2. Methods (Perl & XML)
appendData( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
appendData method for CharacterData class (DOM): 7.2.7.2. Methods (Perl & XML)
Apple Computer and character sets: 5.7. Platform-Dependent Character Sets (XML in a Nutshell)
applet module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
application helpers (XML): 9.1. XML Modules (Perl & XML)
Application Programming Interface (see API)
application/xml MIME media type: 5.1. Character-Set Metadata (XML in a Nutshell)
apply-templates construct: 2.3.3.1. Template matching (Java & XML)
Arabic Unicode block: 26.3.10. Arabic (XML in a Nutshell)
architecture, JDOM: 8.1. Helpful JDOM Internals (Java & XML)
arcrole attribute (XLink): 10.4.2.3. Arc roles (XML in a Nutshell)
arcs: 10.4.2. Arcs (XML in a Nutshell)
multiple arcs from one arc element: 10.4.2.1. Multiple arcs from one arc element (XML in a Nutshell)
role attributes: 10.4.2.3. Arc roles (XML in a Nutshell)
title attributes: 10.4.2.2. Arc titles (XML in a Nutshell)
arithmetic operators in XPath: 9.6.1. Numbers (XML in a Nutshell)
Armenian Unicode block: 26.3.8. Armenian (XML in a Nutshell)
arrays in Perl: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
arrows (<-- -->) enclosing comments: 3.1.5. Comment Nodes (XSLT)
Arrows Unicode block: 26.3.20. Arrows (XML in a Nutshell)
ASCII character encodings: 1.6. XML Gotchas (Perl & XML)
2.6. Unicode, Character Sets, and Encodings (Perl & XML)
ASCII character set: 26.1.1. ASCII (XML in a Nutshell)
associations: 4.1.1. Setting Properties and Features (Java & XML)
asterisk (*) wildcard: 3.2.5. Using Wildcards (XSLT)
Astral Planes: 4.1.3. Internationalization Concerns (SAX2)
at-sign (@)
abbreviating attribute axis: 3.2.6.2. Axis roll call (XSLT)
for selecting attributes: 3.2.4.1. Selecting attributes (XSLT)
at-sign and asterisk (@*) wildcard: 3.2.5. Using Wildcards (XSLT)
at-sign characters (@): 8.2. XPath (Perl & XML)
<!ATTLIST> declaration: 3.3. Attribute Declarations (XML in a Nutshell)
atomic datatypes, deriving list datatypes from: 5.2. Derivation By List (XML Schema)
atoms in patterns: 6.3. Quantifying (XML Schema)
character classes and: 6.4.3. Character Classes (XML Schema)
group character: 6.4.4. Oring and Grouping (XML Schema)
or character: 6.4.4. Oring and Grouping (XML Schema)
special characters: 6.4.1. Special Characters (XML Schema)
user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
wildcard character: 6.4.2. Wildcard (XML Schema)
ATTLIST declaration: 3.3. Attribute Declarations (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
xml:lang attribute and: 5.11.3. ATTLIST Declarations of xml:lang (XML in a Nutshell)
xmlns attribute and: 4.2.5. Attribute Declarations for xmlns (XML in a Nutshell)
ATTLIST keyword: 2.2.1.2. Attributes (Java & XML)
attlist_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers (Perl & XML)
Attr class (DOM): 7.2.9.1. Properties (Perl & XML)
Attr interface: A.2.1.1. Attr (Java & XML)
Attr interface (DOM): 18.4.2.4. Attr (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
attributes for: 24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
attribute axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
Attribute class: A.4.1.1. Attribute (Java & XML)
statement: 7.2.3. Creating XML with JDOM (Java & XML)
Attribute class, JDOM: 7.2.3. Creating XML with JDOM (Java & XML)
attribute declarations: 3.3. Attribute Declarations (XML in a Nutshell)
attribute groups and: 16.2.5.1. Attribute groups (XML in a Nutshell)
schemas and: 16.2.5. Attribute Declarations (XML in a Nutshell)
xmlns attribute and: 4.2.5. Attribute Declarations for xmlns (XML in a Nutshell)
attribute groups: 7.4.1.2. Element and attribute groups (XML Schema)
defining: 13.1.1.2. Attributes (XML Schema)
redefining: 8.2.2. Redefinition of Element and Attribute Groups (XML Schema)
by extension: 8.2.2.1. Extension (XML Schema)
by restriction: 8.2.2.2. Restriction (XML Schema)
rules for, vs. complex types: 8.2.2.2. Restriction (XML Schema)
Attribute Information Items: B.5. Attribute Information Items (SAX2)
Attribute interface: A.1.2. The Attributes Interface (SAX2)
attribute list declarations: 2.10. Declaring Elements and Attributes (Perl & XML)
attribute nodes (XPath): 3.1.3. Attribute Nodes (XSLT)
B.1.3. Attribute Nodes (XSLT)
attribute types: 3.3.1. Attribute Types (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
enumeration: 3.3.1.4. Enumeration (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
vs. NOTATION: 3.3.1.10. NOTATION (XML in a Nutshell)
NOTATION: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
attribute value templates: 3.3. Attribute Value Templates (XSLT)
5.1.3. A Stylesheet That Uses the id( ) Function (XSLT)
attribute values
ensuring validity of: 20.4.2. Validity Constraints (XML in a Nutshell)
external entities and: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
no < allowed in: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
templates for: 8.9. Attribute Value Templates (XML in a Nutshell)
attribute values, placing quotes inside: D.1. How Do I Put Quotes Inside an Attribute Value? (XSLT)
attributeDecl( ) callback function: 4.3.1. The DeclHandler Interface (SAX2)
attributeFormDefault attribute: 10.3. To Qualify Or Not to Qualify? (XML Schema)
attributeFormDefault attribute (xs:schema element): 16.3.2. Controlling Qualification (XML in a Nutshell)
AttributeList interface: A.1.1.1. AttributeList [deprecated] (Java & XML)
A.1.1. The AttributeList Interface (SAX2)
AttributeListImpl class: A.1.3.1. AttributeListImpl (Java & XML)
AttributeListImpl interface: A.2.1. The AttributeListImpl Interface (SAX2)
ATTRIBUTE_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
attributes: 2.1.1.3. Attributes (Java & XML)
2.2.1.2. Attributes (Java & XML)
9.3.2.1. Working with the factory (Java & XML)
2.3.3. The Attributes Interface (SAX2)
accessing: 25.1. The org.xml.sax Package (XML in a Nutshell)
converting to elements: D.2. How Do I Convert All Attributes to Elements? (XSLT)
creating classified lists of: 2.2. Our First Schema (XML Schema)
default declarations for: 3.3.2. Attribute Defaults (XML in a Nutshell)
normal attribute values and: 20.4.2. Validity Constraints (XML in a Nutshell)
default namespaces and: 4.2.4. Setting a Default Namespace with the xmlns Attribute (XML in a Nutshell)
20.3.4. Namespaces (XML in a Nutshell)
defining
globally: 2.2. Our First Schema (XML Schema)
locally: 3.1. Working From the Structure of the Instance Document (XML Schema)
3.2.2. Russian Doll and Object-Oriented Design (XML Schema)
using xs:ID and xs:IDREF types: 9.1. and (XML Schema)
in PYX notation: 4.5. XML::PYX (Perl & XML)
in XML: 2.2. Markup, Elements, and Structure (Perl & XML)
declaring: 2.10. Declaring Elements and Attributes (Perl & XML)
indexes: 2.3.3.2. Attribute lookup by index (SAX2)
location steps: 9.2.3. Attribute Location Steps (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
matching multiple: 9.2.6. Multiple Matches with | (XML in a Nutshell)
namespaces and: 2.6. Namespaces and SAX2 (SAX2)
2.6.2. Element and Attribute Naming with Namespaces (SAX2)
naming: 2.3.3.1. Attribute lookup by name (SAX2)
2.6.1. What Namespaces Do to XML (SAX2)
2.6.2.2. Attribute naming (SAX2)
nodes
default template for: 8.7.1. The Default Template Rule for Text and Attribute Nodes (XML in a Nutshell)
XPath data model and: 22.1. The XPath Data Model (XML in a Nutshell)
notation: 20.4.2. Validity Constraints (XML in a Nutshell)
of <xsl:sort> element: 6.1.2.2. Attributes (XSLT)
qualified vs. unqualified: 10.3. To Qualify Or Not to Qualify? (XML Schema)
quotation marks, and: 2.1.1.3. Attributes (Java & XML)
rule governing quoting: 1.2.2.3. All attributes must be quoted (XSLT)
selecting: 12.4.3. Attribute Selectors (XML in a Nutshell)
3.2.4.1. Selecting attributes (XSLT)
special: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
unique names for: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
vs. child elements: 2.3. Attributes (XML in a Nutshell)
vs. elements: 2.1.1.3. Attributes (Java & XML)
XLink: 10.7.2. XLink Attributes (XML Schema)
XML: 10.7.1. XML Attributes (XML Schema)
in XML declarations: 2.9. The XML Declaration (XML in a Nutshell)
XML elements and: 2.3. Attributes (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.3. Document Body (XML in a Nutshell)
attributes attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
Attributes atts element: 2.3.4. Essential ContentHandler Callbacks (SAX2)
Attributes instance: 3.3.5. Element Callbacks (Java & XML)
Attributes interface: 3.3.5. Element Callbacks (Java & XML)
A.1.1.2. Attributes (Java & XML)
Attributes interface (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
Attributes method, using DeclHandler interface: 4.3.1. The DeclHandler Interface (SAX2)
attributes property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
Attributes.getLocalName( ) function: B.5. Attribute Information Items (SAX2)
Attributes.getType( ) function: 2.3.3.3. Other attributes issues (SAX2)
Attributes.getURI( ) function: B.5. Attribute Information Items (SAX2)
AttributesImpl class: A.1.3.2. AttributesImpl (Java & XML)
2.3.4. Essential ContentHandler Callbacks (SAX2)
3.5.2.2. Turning objects into SAX events (SAX2)
5.1.1. The AttributesImpl Class (SAX2)
A.2.2. The AttributesImpl Class (SAX2)
AttributesImpl class (SAX): 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
awk program working with XML::PYX: 4.5. XML::PYX (Perl & XML)
axes: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6. Axes (XSLT)
XPath: B.3. XPath Axes (XSLT)
Axis SOAP toolkit: 12.2.1.3. What's Axis? (Java & XML)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

background-color property (CSS): 12.9. Colors (XML in a Nutshell)
Backus-Naur Form (BNF) grammar: 23.3. XSLT Functions (XML in a Nutshell)
bare name XPointers: 11.3. Bare Names (XML in a Nutshell)
base alphabetical glyphs: 3.9.2. Unicode Encodings (Perl & XML)
base attribute, referencing simple type definitions with: 7.3.1. Creation of Simple Content Models (XML Schema)
base classes (handlers): 5.5. A Handler Base Class (Perl & XML)
XML::Handler::YAWriter module and: 5.6. XML::Handler::YAWriter as a Base Handler Class (Perl & XML)
base64 encoding, predefined datatype for: 4.3.3.5. Binary string-encoded datatypes (XML Schema)
base module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
base URI: 7.3. Invoking the document( ) Function (XSLT)
base URIs: B.2.2. Base URIs, xml:base, and Locator Data (SAX2)
baseName variable: 7.3.2. Loading XML (Java & XML)
basic forms module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
basic tables module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
BasicLatin Unicode block: 6.5.1.1. Unicode blocks (XML Schema)
Bean Scripting Framework (BSF): 8.1.2.2. Example: Writing extensions in other languages (XSLT)
BeanSerializer class: 12.4.1. Custom Parameter Types (Java & XML)
BEEP: 6.2.2. Roles for Java in XML Messaging (SAX2)
before pseudoelement: 12.4.5. Pseudoelement Selectors (XML in a Nutshell)
Berjon, Robin: 5.7. XML::SAX: The Second Generation (Perl & XML)
Berners-Lee, Tim: 1.2.1. XML's Heritage (XSLT)
best matching rules: 2.12. Transformations (Perl & XML)
bidirectional text module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
big-endian UCS-2: 5.5.1. UCS-2 and UTF-16 (XML in a Nutshell)
binary string-encoded datatypes: 4.3.3.5. Binary string-encoded datatypes (XML Schema)
binding
data: 1.1.4. Data Binding (XML Schema)
early vs. late: A.1.4. Early Binding Tools (XML Schema)
binding element: 13.3. WSDL (Java & XML)
binding prefixes to URIs: 4.2.2. Binding Prefixes to URIs (XML in a Nutshell)
biztalk.org (DTD repository): 3.10. Locating Standard DTDs (XML in a Nutshell)
BLOB (Binary Large Object), storing text as: 3.1.2.2. Providing entity text (SAX2)
block areas (XSL-FO): 13.1. XSL Formatting Objects (XML in a Nutshell)
flowing content into pages: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
splitting: 13.1. XSL Formatting Objects (XML in a Nutshell)
block attribute: 11.2.2. Defining Complex Types (XML Schema)
complex type definitions and: 12.2.3.1. Blocking complex types (XML Schema)
substitution groups and: 12.2.2.1. Block attribute (XML Schema)
block-container element (XSL-FO): 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
block element (XSL-FO): 13.1. XSL Formatting Objects (XML in a Nutshell)
13.3.1. Flowing Content into the Pages (XML in a Nutshell)
block elements (CSS display property): 12.5.2. Block Elements (XML in a Nutshell)
blockDefault attribute: 12.2.2.1. Block attribute (XML Schema)
%Block.extra entity reference: 7.3.2. Mixing Your Applications into XHTML (XML in a Nutshell)
%Block.mix; entity reference: 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
blocks, Unicode: 6.4.3.2. Unicode character classes (XML Schema)
Blueberry: 1.8. What XML Are We Talking About? (SAX2)
4.1.3. Internationalization Concerns (SAX2)
BNF (Backus-Naur Form) grammar: 23.3. XSLT Functions (XML in a Nutshell)
BOM (byte order mark): 3.9.4.3. Byte order marks (Perl & XML)
boolean datatype: 3.4. XPath Datatypes (XSLT)
boolean feature flags: 3.3. Configuring XMLReader Behavior (SAX2)
5.1.3. The NamespaceSupport Class (SAX2)
boolean( ) function: C. XSLT and XPath Function Reference (XSLT)
boolean operations in XPath: 8.2. XPath (Perl & XML)
B.4.2. Boolean Operators (XSLT)
boolean values: 3.3.2. XMLReader Feature Flags (SAX2)
converting datatypes to: 4.2.1.1. Converting to boolean values (XSLT)
boolean( ) (XPath): 9.7.3. Boolean Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
Booleans
used by SAX features: 19.2. SAX Features and Properties (XML in a Nutshell)
XPath expressions: 9.6.3. Booleans (XML in a Nutshell)
22.2. Data Types (XML in a Nutshell)
XPath functions: 9.7.3. Boolean Functions (XML in a Nutshell)
Borden, Jonathan: 14. Resource Directory Description Language (RDDL) (XML in a Nutshell)
border-color property (CSS): 12.9. Colors (XML in a Nutshell)
border-width property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
Bosak, Jon: 1.4.1. SAX1 (SAX2)
1.4. The Evolution of XML (XML in a Nutshell)
Bourret, Ronald: 1.1.4. Data Binding (XML Schema)
A.3.2. The W3C XML Schema Family (XML Schema)
branching elements: 4.2.1. The <xsl:if> Element (XSLT)
Bray, Tim: 1.4.1. SAX1 (SAX2)
7.4.1.3. Unique Particle Attribution Rule (XML Schema)
1.4. The Evolution of XML (XML in a Nutshell)
14. Resource Directory Description Language (RDDL) (XML in a Nutshell)
Brian, Dan: 6.4. XML::SimpleObject (Perl & XML)
British Summer Time (BST): 4.5.1. The Realm of ISO 8601 (XML Schema)
Brown University Scholarly Technology Group: 3.1.4. Validating a Document (XML in a Nutshell)
browser-dependent styling: 10.3.3. Browser-Dependent Styling (Java & XML)
browsers (see web browsers)
BSF (Bean Scripting Framework): 8.1.2.2. Example: Writing extensions in other languages (XSLT)
BST (British Summer Time): 4.5.1. The Realm of ISO 8601 (XML Schema)
buf[] character array in LexicalHandler class: 4.2. The LexicalHandler Interface (SAX2)
builder classes: 7.1.3. Input and Output (Java & XML)
BuilderErrorHandler class: A.4.3.1. BuilderErrorHandler (Java & XML)
buildTree( ): 3.2.1. Instantiating a Reader (Java & XML)
3.2.3. Using an InputSource (Java & XML)
throwable exceptions: 3.2.2. Parsing the Document (Java & XML)
buildZipFile function: 9.5.8. Generating the Zip File (XSLT)
built-in template rules: 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
8.7. The Built-in Template Rules (XML in a Nutshell)
2.4.5. Built-in Template Rules (XSLT)
Bullard, Claude L.: 14.1. What's at the End of a Namespace URL? (XML in a Nutshell)
byte order mark: 5.5.1. UCS-2 and UTF-16 (XML in a Nutshell)
byte order mark (BOM): 3.9.4.3. Byte order marks (Perl & XML)
bytes, in XML text: 1.2.3. Memory Consumption with SAX and DOM (SAX2)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

\c character class: 6.4.3.1. Classical Perl character classes (XML Schema)
C0 controls in ASCII character sets: 26.1.1. ASCII (XML in a Nutshell)
C1 controls in ISO-8859 character sets: 26.1.2.1. C1 controls (XML in a Nutshell)
C programming language: 1.1. Why Use Perl with XML? (Perl & XML)
1.4.4. Is SAX2 a "Standard"? (SAX2)
parsers, writing in: 3.2. XML::Parser (Perl & XML)
XML::LibXML module and: 3.5. XML::LibXML (Perl & XML)
call-back functions, using Subs parsing styles: 3.2.2. Parsing Styles (Perl & XML)
callback methods: 3.2.2. Parsing the Document (Java & XML)
documents, start and end: 3.3.2. The Beginning and the End of a Document (Java & XML)
callbacks (event): 4. Consuming SAX2 Events (SAX2)
CallFilter class: 6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
CallWriter class: 6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
Canonical XML: 1.4. The Evolution of XML (XML in a Nutshell)
17.2.1. What You Get Is Not What You Saw (XML in a Nutshell)
B.3. Document Information Item (SAX2)
and namespace prefixes: 10.2. Namespace Declarations (XML Schema)
cards: 10.3.4. WAP and XML (Java & XML)
caret (^) for escaping characters: 4.6.1. Implementing a String Replace Function (XSLT)
Cascading Style Sheets (CSS): 1.4. The Evolution of XML (XML in a Nutshell)
12. Cascading Style Sheets (CSS) (XML in a Nutshell)
12. Cascading Style Sheets (CSS) (XML in a Nutshell)
(see also stylesheets)
color properties: 12.9. Colors (XML in a Nutshell)
display property: 12.5. The Display Property (XML in a Nutshell)
font properties: 12.7. Font Properties (XML in a Nutshell)
selectors: 12.4. Selectors (XML in a Nutshell)
style rules for: 12.2. CSS Syntax (XML in a Nutshell)
syntax of: 12.2. CSS Syntax (XML in a Nutshell)
text properties: 12.8. Text Properties (XML in a Nutshell)
units of length: 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
vs. transformation-based presentation: 6.6. Transformation and Presentation (XML in a Nutshell)
vs. XSL-FO: 13.5. Choosing Between CSS and XSL-FO (XML in a Nutshell)
web browser support for: 13.5. Choosing Between CSS and XSL-FO (XML in a Nutshell)
XML documents and: 12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
Cascading Style Sheets Level 1 (CSS1): 7.2. Direct Display of XML in Browsers (XML in a Nutshell)
12.1. The Three Levels of CSS (XML in a Nutshell)
case-order attribute, <xsl:sort> element and: 6.1.2.2. Attributes (XSLT)
case sensitivity in elements: 0.. Conventions Used in This Book (XML in a Nutshell)
2.1.1.2. Elements (Java & XML)
2.2.1.2. Case sensitivity (XML in a Nutshell)
case statement (see <xsl:choose> element)
Castor: 15.2. Castor (Java & XML)
AddItemServlet: 15.2.3. Marshalling and Unmarshalling (Java & XML)
classes, generating: 15.2.2. Source Generation (Java & XML)
installation: 15.2.1. Installation (Java & XML)
marshalling and unmarshalling: 15.2.3. Marshalling and Unmarshalling (Java & XML)
categories, Unicode: 6.4.3.2. Unicode character classes (XML Schema)
CD class: 12.4.1. Custom Parameter Types (Java & XML)
CD Orderer client: 13.4.5. Writing a Client (Java & XML)
testing: 13.4.5. Writing a Client (Java & XML)
CDAdder class: 12.3.3. An RPC Client (Java & XML)
updated version: 12.4.1. Custom Parameter Types (Java & XML)
CDATA attribute: 2.2.1.2. Attributes (Java & XML)
4.3.1. The DeclHandler Interface (SAX2)
CDATA (Character DATA) section: 2.8. Processing Instructions and Other Markup (Perl & XML)
6.1. XML Trees (Perl & XML)
CDATA class: A.4.1.2. CDATA (Java & XML)
CDATA section marker: 2.1.1.5. Unparsed data (Java & XML)
CDATA sections: 2.6. CDATA Sections (XML in a Nutshell)
2.6. CDATA Sections (XML in a Nutshell)
3. XPath: A Syntax for Describing Needles and Haystacks (XSLT)
3.1.4. Text Nodes (XSLT)
attribute type: 3.3.1.1. CDATA (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
LexicalHandler interface and: 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
CDATASection class (DOM): 7.2.11. CDATASection (Perl & XML)
CDATASection interface: A.2.1.2. CDATASection (Java & XML)
CDATASection interface (DOM): 18.4.2.9. CDATASection (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
CDATA_SECTION_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
CDCatalog class: 12.3.2.1. Code artifacts (Java & XML)
updated: 12.4.1. Custom Parameter Types (Java & XML)
CDCatalog deployment descriptors: 12.3.2.2. Deployment descriptors (Java & XML)
CDF (Channel Definition Format): 6.1. Rich Site Summary: RSS (SAX2)
CDLister class: 12.3.3. An RPC Client (Java & XML)
CDs-R-Us Messaging Service: 13.4.1. A Messaging Service (Java & XML)
imported classes: 13.4.1. A Messaging Service (Java & XML)
ceiling( ) function: C. XSLT and XPath Function Reference (XSLT)
ceiling( ) (XPath): 9.7.4. Number Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
chameleon schemas: 13.1.3. Splitting Schema Components (XML Schema)
channel element: 14.3.2. Creating an XML RSS Document (Java & XML)
channels: 14.3.1. Rich Site Summary (Java & XML)
chapterTen.xml: 10.4.1. Creating an XSP Page (Java & XML)
char buf[] character array: 2.3.4. Essential ContentHandler Callbacks (SAX2)
character classes: 6.4.3. Character Classes (XML Schema)
character data: 2.2. Elements, Tags, and Character Data (XML in a Nutshell)
2.7. Comments (XML in a Nutshell)
(see also CDATA entries)
comments in: 2.7. Comments (XML in a Nutshell)
parsed: 2.5. Entity References (XML in a Nutshell)
3.1.1. A Simple DTD Example (XML in a Nutshell)
character data characters (Unicode): 26.1. Character Tables (XML in a Nutshell)
Character data node type: 6.1. XML Trees (Perl & XML)
character encodings: 1.6. XML Gotchas (Perl & XML)
2.6. Unicode, Character Sets, and Encodings (Perl & XML)
3.9. Character Sets and Encodings (Perl & XML)
5. Internationalization (XML in a Nutshell)
autodetection of: 20.3.1. Global Syntax Structures (XML in a Nutshell)
character entities: 2.5. Entities (Perl & XML)
Character Information Items: B.8. Character Information Items (SAX2)
character references: 5.10. Character References (XML in a Nutshell)
3.1.2. Element Nodes (XSLT)
for HTML 4.0 entity sets: 26.2. HTML4 Entity Sets (XML in a Nutshell)
predefined: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
20.4.1. Well-Formedness Constraints (XML in a Nutshell)
character sets: 5. Internationalization (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
ASCII: 26.1.1. ASCII (XML in a Nutshell)
converting between: 5.8. Converting Between Character Sets (XML in a Nutshell)
Cp1252: 26.1.2.1. C1 controls (XML in a Nutshell)
ISO: 5.6. ISO Character Sets (XML in a Nutshell)
metadata: 5.1. Character-Set Metadata (XML in a Nutshell)
names of: 26. Character Sets (XML in a Nutshell)
national standard: 5.6. ISO Character Sets (XML in a Nutshell)
platform-dependent: 5.7. Platform-Dependent Character Sets (XML in a Nutshell)
Unicode: 5.5. Unicode (XML in a Nutshell)
XML-defined: 5.4. XML-Defined Character Sets (XML in a Nutshell)
XML document default: 5.9. The Default Character Set for XML Documents (XML in a Nutshell)
character tables for Unicode: 26.1. Character Tables (XML in a Nutshell)
CharacterData class (DOM): 7.2.7. CharacterData (Perl & XML)
A.2.1.3. CharacterData (Java & XML)
18.4.2.5. CharacterData (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
attributes for: 24.2. Object Reference (XML in a Nutshell)
methods for: 24.2. Object Reference (XML in a Nutshell)
characters( ): 3.3.6. Element Data (Java & XML)
3.8. XML::Writer (Perl & XML)
5.7.2.1. Content event handlers (Perl & XML)
2.3.2. Example: Elements and Text (SAX2)
19.1. The ContentHandler Interface (XML in a Nutshell)
character arrays and: 3.5.4. Getting Ahead of the Data (Java & XML)
ignorableWhitespace( ) parameters and: 4.1.1. Other ContentHandler Methods (SAX2)
SAX event handlers: 5.1. SAX Event Handlers (Perl & XML)
startCDATA( )/endCDATA( ) functions and: 4.2. The LexicalHandler Interface (SAX2)
using filters with: 19.3. Filters (XML in a Nutshell)
whitespace reported by: 3.3.6. Element Data (Java & XML)
charset pseudoattribute: 7.2.1.3. The charset pseudoattribute (XML in a Nutshell)
12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
Chemical Markup Language (CML): 1.4.1. SAX1 (SAX2)
child( ) method: 6.4. XML::SimpleObject (Perl & XML)
child axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
child elements: 2.2.2.1. Parents and children (XML in a Nutshell)
3.2.2. Child Elements (XML in a Nutshell)
determining the number of: 3.2.4. The Number of Children (XML in a Nutshell)
location steps: 9.2.2. Child Element Location Steps (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
in narrative-oriented documents: 6.2. Narrative Document Structures (XML in a Nutshell)
vs. attributes: 2.3. Attributes (XML in a Nutshell)
childNodes attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
childNodes property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
children( ) method: 6.4. XML::SimpleObject (Perl & XML)
children elements: 2.2. Our First Schema (XML Schema)
recursive schemas and: 3.2.1. Depth Versus Modularity? (XML Schema)
Chinese characters: 2.6. Unicode, Character Sets, and Encodings (Perl & XML)
using subcodes for dialects: 5.11.2. Subcodes (XML in a Nutshell)
UTF-8/UTF-16 encodings: 5.5.2. UTF-8 (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
choices in element declarations: 3.2.5. Choices (XML in a Nutshell)
Clark, James: 3.2. XML::Parser (Perl & XML)
1.4.1. SAX1 (SAX2)
7.4.1.3. Unique Particle Attribution Rule (XML Schema)
A.1.3. Intrusive Modeling Tools (XML Schema)
A.3.3. The RELAX NG Family (XML Schema)
A.7. RELAX NG (XML Schema)
1.4. The Evolution of XML (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
class instances, sharing: 11.4.2. Shared Instances (Java & XML)
class libraries, extension functions and: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
class paths, installing SAX 2.0: 1.7. Installing a SAX2 Parser (SAX2)
classes: 0.6. Acknowledgments (Java & XML)
2.2.1. How Do the Parts Fit Together? (SAX2)
AbstractDOMAdapter: A.4.2.1. AbstractDOMAdapter (Java & XML)
Attribute: 7.2.3. Creating XML with JDOM (Java & XML)
7.2.3. Creating XML with JDOM (Java & XML)
A.4.1.1. Attribute (Java & XML)
AttributeListImpl: A.1.3.1. AttributeListImpl (Java & XML)
AttributesImpl: A.1.3.2. AttributesImpl (Java & XML)
BeanSerializer: 12.4.1. Custom Parameter Types (Java & XML)
builder classes: 8.2.2. Building with Custom Classes (Java & XML)
BuilderErrorHandler: A.4.3.1. BuilderErrorHandler (Java & XML)
builders: 7.1.3. Input and Output (Java & XML)
CD: 12.4.1. Custom Parameter Types (Java & XML)
CDAdder: 12.3.3. An RPC Client (Java & XML)
updated version: 12.4.1. Custom Parameter Types (Java & XML)
CDATA: A.4.1.2. CDATA (Java & XML)
CDCatalog: 12.3.2.1. Code artifacts (Java & XML)
updated: 12.4.1. Custom Parameter Types (Java & XML)
CDLister: 12.3.3. An RPC Client (Java & XML)
Comment: 7.3.1. Storing XML (Java & XML)
A.4.1.3. Comment (Java & XML)
DataWriter: 4.3.2. XMLWriter (Java & XML)
DefaultHandler: 4.2.3. The DefaultHandler Class (Java & XML)
A.1.3.3. DefaultHandler (Java & XML)
DefaultMutableTreeNode: 3.2. SAX Readers (Java & XML)
DefaultsHandler: 9.3.1.1. The road to SAX 2.0 (Java & XML)
DefaultTreeModel: 3.2. SAX Readers (Java & XML)
DocType: A.4.1.4. DocType (Java & XML)
Document: A.4.1.5. Document (Java & XML)
DocumentBuilder: 13.4.5. Writing a Client (Java & XML)
A.3.1.1. DocumentBuilder (Java & XML)
DocumentBuilderFactory: 9.3.1. Updating the Standards (Java & XML)
A.3.1.2. DocumentBuilderFactory (Java & XML)
DOMAdapter: A.4.2.2. DOMAdapter (Java & XML)
DOMBuilder: 7.1.3. Input and Output (Java & XML)
A.4.3.2. DOMBuilder (Java & XML)
performance concerns: 7.5.3. DOMBuilder (Java & XML)
7.5.3. DOMBuilder (Java & XML)
DOMException: A.2.1.8. DOMException (Java & XML)
DOMResult: A.3.3. Package: javax.xml.transform.dom (Java & XML)
DOMSerializer: 5.2.3. DOMSerializer (Java & XML)
warning: 5.2.4. The Results (Java & XML)
DOMSource: A.3.3. Package: javax.xml.transform.dom (Java & XML)
Element: A.4.1.6. Element (Java & XML)
SetAttribute( ): 7.2.3. Creating XML with JDOM (Java & XML)
subclassing: 8.2. JDOM and Factories (Java & XML)
EntityRef: 8.1.2. The EntityRef Class (Java & XML)
A.4.1.7. EntityRef (Java & XML)
Enumeration: 11.3.1. A Shared Handler (Java & XML)
exception: 2.5.1. SAX2 Exception Classes (SAX2)
FactoryConfigurationError: A.3.1.3. FactoryConfigurationError (Java & XML)
FormattingNodeFilter: 6.3.2.1. NodeIterator (Java & XML)
generating: 15.1.1. Class Generation (Java & XML)
HandlerBase: 9.2.1. Starting with SAX (Java & XML)
9.2.1. Starting with SAX (Java & XML)
A.1.1.8. HandlerBase (Java & XML)
handlers, XML-RPC: 11.2.2. Writing the Handler (Java & XML)
Hashtable: 7.2.1. Java Properties Files (Java & XML)
HelloServer: 11.2.3. Writing the Server (Java & XML)
InputSource: 3.2.3. Using an InputSource (Java & XML)
A.1.1.9. InputSource (Java & XML)
ItemSearcher: 6.3.2.1. NodeIterator (Java & XML)
JDOM, for: 7.1. The Basics (Java & XML)
JDOMException: A.4.1.8. JDOMException (Java & XML)
JTreeContentHandler: 3.3. Content Handlers (Java & XML)
LexicalHandler: 4.4.1. LexicalHandler (Java & XML)
Locator: 3.3.1. The Document Locator (Java & XML)
A.1.1.10. Locator (Java & XML)
LocatorImpl: A.1.3.4. LocatorImpl (Java & XML)
MalformedURLException: 11.2.4. Writing the Client (Java & XML)
Namespace: 8.1.3. The Namespace Class (Java & XML)
A.4.1.9. Namespace (Java & XML)
NamespaceSupport: A.1.3.5. NamespaceSupport (Java & XML)
OrderProcessor: 13.4.5. Writing a Client (Java & XML)
OutputKeys: A.3.2.2. OutputKeys (Java & XML)
ParserAdapter: 3.5.1. My Parser Doesn't Support SAX 2.0 (Java & XML)
A.1.3.6. ParserAdapter (Java & XML)
ParserConfigurationExeption: A.3.1.4. ParserConfigurationException (Java & XML)
ParserFactory: 9.2.1. Starting with SAX (Java & XML)
9.2.1.1. A look at the SAXParserFactory class (Java & XML)
A.1.3.7. ParserFactory (Java & XML)
ProcessingInstruction: A.4.1.10. ProcessingInstruction (Java & XML)
PropsToXML: 7.2. PropsToXML (Java & XML)
SAX API: 3.1.2. Getting the SAX Classes and Interfaces (Java & XML)
SAXBuilder: 7.1.3. Input and Output (Java & XML)
A.4.3.4. SAXBuilder (Java & XML)
SAXException: 3.3.2. The Beginning and the End of a Document (Java & XML)
A.1.1.12. SAXException (Java & XML)
SAXNotRecognizedException: A.1.1.13. SAXNotRecognizedException (Java & XML)
SAXNotSupportedException: A.1.1.14. SAXNotSupportedException (Java & XML)
SAXParseException: A.1.1.15. SAXParseException (Java & XML)
SAXParser: 9.2.1.2. Working with the SAXParser class (Java & XML)
A.3.1.5. SAXParser (Java & XML)
SAXParserFactory: 9.3.1. Updating the Standards (Java & XML)
A.3.1.6. SAXParserFactory (Java & XML)
SAXTreeViewer: 3.2. SAX Readers (Java & XML)
3.2.1. Instantiating a Reader (Java & XML)
Scheduler: 11.3.1. A Shared Handler (Java & XML)
SchedulerClient: 11.3.3. A Useful Client (Java & XML)
SerializerTest: 5.2.1. Getting a DOM Parser (Java & XML)
SimpleDateFormat: 11.3.1. A Shared Handler (Java & XML)
SimpleXPathViewer: 8.3.4. Endgame (Java & XML)
SOAPMappingRegistry: 12.4.1. Custom Parameter Types (Java & XML)
StreamResult: 9.3.2.2. Transforming XML (Java & XML)
StreamSource: 9.3.2.2. Transforming XML (Java & XML)
TestXMLProperties: 7.3.4. Backtracking (Java & XML)
Text: 8.1.1. The Text Class (Java & XML)
A.4.1.11. Text (Java & XML)
Thread: 11.3.1. A Shared Handler (Java & XML)
Transformer: A.3.2.7. Transformer (Java & XML)
TransformerFactory: 9.3.2.1. Working with the factory (Java & XML)
A.3.2.8. TransformerFactory (Java & XML)
UpdateItemServlet: 6.1.1. Creating a New DOM Tree (Java & XML)
6.3.3. Range (Java & XML)
XML parsers: 3.1.1. Obtaining a Parser (Java & XML)
XMLFilter: 4.3.1. XMLFilters (Java & XML)
A.1.1.16. XMLFilter (Java & XML)
XMLFilterImpl: 4.3.1. XMLFilters (Java & XML)
A.1.3.8. XMLFilterImpl (Java & XML)
XMLOutputter: 7.2.4. Outputting XML with JDOM (Java & XML)
XMLProperties: 7.3. XMLProperties (Java & XML)
incompatible versions: 7.3.3. Taking a Test Drive (Java & XML)
skeleton: 7.3. XMLProperties (Java & XML)
testing: 7.3.3. Taking a Test Drive (Java & XML)
XMLReaderAdapter: A.1.3.9. XMLReaderAdapter (Java & XML)
XMLReaderFactory: A.1.3.10. XMLReaderFactory (Java & XML)
XmlRpc: 11.2.3. Writing the Server (Java & XML)
XmlRpcClient: 11.2.4. Writing the Client (Java & XML)
11.2.4. Writing the Client (Java & XML)
XmlRpcServer: 11.2.2. Writing the Handler (Java & XML)
XMLUtils: 13.4.5. Writing a Client (Java & XML)
XMLWriter: 4.3.2. XMLWriter (Java & XML)
XPathDisplayNode: 8.3.3. Providing Support for XPath (Java & XML)
XSLTProcessor: 14.2.2. XSLT from a Servlet (Java & XML)
XSLTProcessorFactory: 14.2.2. XSLT from a Servlet (Java & XML)
"classical" Perl character classes: 6.4.3.1. Classical Perl character classes (XML Schema)
ClassNotFoundException, setDriver( ): 11.2.3. Writing the Server (Java & XML)
client application, XML-RPC: 11.2.4. Writing the Client (Java & XML)
client-side image map module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
cloneNode( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
7.2.4.2. Methods (Perl & XML)
closing tags: 2.1.1.2. Elements (Java & XML)
CML (Chemical Markup Language): 1.4.1. SAX1 (SAX2)
co-occurrence constraints: 9.4. Using and As Co-occurrence Constraints (XML Schema)
Cocoon: 6.3.5. HTML (Java & XML)
8.3. Stylesheet Processors (XML in a Nutshell)
10.1.4. Making the Decision (Java & XML)
browser-dependent styling: 10.3.3. Browser-Dependent Styling (Java & XML)
configuration: 10.2.2. Configuring the Servlet Engine (Java & XML)
online resources, source and binaries: 10.2.1. Source Code or Binaries (Java & XML)
pipeline framework: 4.5. XML Pipelines (SAX2)
production usage: 10.1.3. Production Presence (Java & XML)
Tomcat configuration: 10.2.2. Configuring the Servlet Engine (Java & XML)
context: 10.2.2. Configuring the Servlet Engine (Java & XML)
WML, outputting: 10.3.4. WAP and XML (Java & XML)
cocoon.properties file: 10.3.3. Browser-Dependent Styling (Java & XML)
Codd, E. F.: 11.3. Defining Nil (Null) Values (XML Schema)
code artifacts: 12.3.2.1. Code artifacts (Java & XML)
code points: 5. Internationalization (XML in a Nutshell)
in Unicode: 3.9.2. Unicode Encodings (Perl & XML)
unassigned: 26.1. Character Tables (XML in a Nutshell)
code samples and CDATA sections: 2.6. CDATA Sections (XML in a Nutshell)
collapsed strings: 4.3.3. Collapsed Strings (XML Schema)
color property (CSS): 12.9. Colors (XML in a Nutshell)
comma-separated-value lists, not described by XML Schema: 5.2.1. List Datatypes (XML Schema)
command-line XSLT processors: 8.3.1. Command-Line Processors (XML in a Nutshell)
command lines, installing SAX 2.0: 1.7. Installing a SAX2 Parser (SAX2)
comment( ): 3.8. XML::Writer (Perl & XML)
5.7.2.3. Lexical event handlers (Perl & XML)
4.2. The LexicalHandler Interface (SAX2)
location step: 9.2.4. The comment( ), text( ), and processing-instruction( ) Location Steps (XML in a Nutshell)
node test: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
B.2. XPath Node Tests (XSLT)
22.3.3. Node Tests (XML in a Nutshell)
Comment class: 7.3.1. Storing XML (Java & XML)
A.4.1.3. Comment (Java & XML)
Comment class (DOM): 7.2.13. Comment (Perl & XML)
Comment Information Items: B.9. Comment Information Items (SAX2)
Comment interface: A.2.1.4. Comment (Java & XML)
Comment interface (DOM): 18.4.2.6. Comment (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
Comment node type: 6.1. XML Trees (Perl & XML)
comment nodes
default template for: 8.7.3. The Default Template Rule for Comment and Processing Instruction Nodes (XML in a Nutshell)
XPath data model and: 22.1. The XPath Data Model (XML in a Nutshell)
3.1.5. Comment Nodes (XSLT)
B.1.5. Comment Nodes (XSLT)
comment SAX method: 5.1. SAX Event Handlers (Perl & XML)
COMMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
comments: 1.4.3. SAX2 Extensions (SAX2)
adding to schemas, using xs:annotation element: 14.2. The W3C XML Schema Annotation Element (XML Schema)
IGNORE directive acting as: 3.8. Conditional Inclusion (XML in a Nutshell)
LexicalHandler interface and: 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
loopholes in XML specification: 17.2.1. What You Get Is Not What You Saw (XML in a Nutshell)
using comment( ) to report characters inside: 4.2. The LexicalHandler Interface (SAX2)
vs. annotations in schemas: 16.2.2. Annotations (XML in a Nutshell)
XML 1.0: 14.4. XML 1.0 Comments (XML Schema)
in XML documents: 2.7. Comments (XML in a Nutshell)
name="comments-for-XML">
2.8. Processing Instructions and Other Markup (Perl & XML)
selecting: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
communications protocols and XML: 15.1.2. Communications (XML in a Nutshell)
compareDocumentOrder( ): 6.4.2. Node Comparisons (Java & XML)
compareTreePosition( ): 6.4.2. Node Comparisons (Java & XML)
6.4.2. Node Comparisons (Java & XML)
comparison operators in XPath: 9.6.2. Strings (XML in a Nutshell)
complements of character classes: 6.4.3.3. User-defined character classes (XML Schema)
complex content models: 2.2. Our First Schema (XML Schema)
asymmetry of derivation methods: 7.4.2.3. Asymmetry of these two methods (XML Schema)
creating: 7.4.1. Creation of Complex Content (XML Schema)
empty content models as: 7.6.1.2. As complex content models (XML Schema)
defining: 2.2. Our First Schema (XML Schema)
derivation by extension: 7.4.2.1. Derivation by extension (XML Schema)
7.7. Back to Our Library (XML Schema)
derivation by restriction: 7.4.2.2. Derivation by restriction (XML Schema)
7.7. Back to Our Library (XML Schema)
11.2.2. Defining Complex Types (XML Schema)
deriving from mixed contents: 7.5.2.3. Derivation between complex and mixed content models (XML Schema)
example of: 7.7. Back to Our Library (XML Schema)
complex datatypes: 2.2. Our First Schema (XML Schema)
abstract attribute and: 12.2.3.3. Abstract complex types (XML Schema)
block attribute and: 12.2.3.1. Blocking complex types (XML Schema)
creating: 7. Creating Complex Datatypes (XML Schema)
vs. deriving: 7.2.3. Creation Versus Derivation (XML Schema)
with complex content models: 7.4.1. Creation of Complex Content (XML Schema)
with empty content models: 7.6.1. Creation of Empty Content Models (XML Schema)
with mixed content models: 7.5.1. Creating Mixed Content Models (XML Schema)
with simple content models: 7.3.1. Creation of Simple Content Models (XML Schema)
defining: 2.2. Our First Schema (XML Schema)
11.2.2. Defining Complex Types (XML Schema)
globally: 13.1.1.1. Elements (XML Schema)
extensibility features of different styles: 13.1.1.1. Elements (XML Schema)
final attribute and: 12.2.3.2. Final complex types (XML Schema)
included schemas: 8.1. Schema Inclusion (XML Schema)
example of: 8.4. Simplifying the Library (XML Schema)
redefining: 8.2.1. Redefining of Simple and Complex Types (XML Schema)
complex types: 16.4. Complex Types (XML in a Nutshell)
attribute groups and: 16.2.5.1. Attribute groups (XML in a Nutshell)
deriving
by extension: 16.8.2.1. Deriving by extension (XML in a Nutshell)
by restriction: 16.8.2.2. Deriving by restriction (XML in a Nutshell)
final attribute and: 16.9.2. The Final Attribute (XML in a Nutshell)
occurrence constraints: 16.4.1. Occurrence Constraints (XML in a Nutshell)
xs:complexContent element and: 16.5.1. complexContent (XML in a Nutshell)
composite fields, defining: 9.2.2. Composite Fields (XML Schema)
compositors (element roles): 7.4.1.1. Compositors and particles (XML Schema)
compound objects: 3.5.4. The XMLFilter Interface (SAX2)
Comprehensive Perl Archive Network (see CPAN)
concat( ) function: 4.6.1.1. Procedural design (XSLT)
9.7. XPath Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
C. XSLT and XPath Function Reference (XSLT)
extensions and: 8.1.1.1. Example: Generating multiple output files (XSLT)
generating text and: D.12. How Do I Generate Some Text? (XSLT)
conditional sections: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
parameter entity nesting and: 20.4.2. Validity Constraints (XML in a Nutshell)
Consistent Declaration Rule: 7.4.1.4. Consistent Declaration Rule (XML Schema)
co-occurrence constraints as workaround to: 9.4. Using and As Co-occurrence Constraints (XML Schema)
xsi:type as workaround against: 11.2.2. Defining Complex Types (XML Schema)
constant type data values: 2.1.1.4. Entity references and constants (Java & XML)
constrained child nodes: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
constraints: 2.2. Constraints (Java & XML)
co-occurrence: 9.4. Using and As Co-occurrence Constraints (XML Schema)
composite fields and: 9.2.2. Composite Fields (XML Schema)
key elements and: 9.2.3. Keys (XML Schema)
namespace: 20.4.3. Namespace Constraints (XML in a Nutshell)
testing for uniqueness: 9.2.1. Uniqueness (XML Schema)
validity: 20.4.2. Validity Constraints (XML in a Nutshell)
well-formedness: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
constructors, JDOM Element class: 8.2.1. Creating a Factory (Java & XML)
consumers: 2.1. Producers and Consumers (SAX2)
SAX-to-DOM: 4.4.1. SAX-to-DOM Consumers (SAX2)
XMLFilterImpl class and: 4.5.1. The XMLFilterImpl Class (SAX2)
container nodes: 11.6. Points (XML in a Nutshell)
in XML trees: 6.1. XML Trees (Perl & XML)
contains( ) function: 5.2.3.3. Solution #2: Use the XPath contains( ) function (XSLT)
C. XSLT and XPath Function Reference (XSLT)
9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
content, element
any type: 16.8. Allowing Any Content (XML in a Nutshell)
empty elements: 16.5. Empty Elements (XML in a Nutshell)
mixed: 16.7. Mixed Content (XML in a Nutshell)
simple: 16.6. Simple Content (XML in a Nutshell)
content handlers: 3.3. Content Handlers (Java & XML)
for SAX: 5.7.2. SAX2 Handler Interface (Perl & XML)
content models: 2.2.1.1. Elements (Java & XML)
2.10. Declaring Elements and Attributes (Perl & XML)
2.2. Our First Schema (XML Schema)
7. Creating Complex Datatypes (XML Schema)
allowing two for same element: 11.2.2. Defining Complex Types (XML Schema)
co-occurrence constraints and: 9.4. Using and As Co-occurrence Constraints (XML Schema)
deterministic vs. nondeterministic: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
open schemas and: 13.2. The Need for Open Schemas (XML Schema)
relationship between child text/element nodes and: 7.2.1. Content Models (XML Schema)
reusable attributes/elements, creating: 7.4.1.2. Element and attribute groups (XML Schema)
8. Creating Building Blocks (XML Schema)
unordered, limitations on: 7.4.1.5. Limitations on unordered content models (XML Schema)
content nodes (DOM Core): 18.4.2. Content Nodes (XML in a Nutshell)
content specifications in element declarations: 3.2. Element Declarations (XML in a Nutshell)
ContentHandler class: 2.2.1. How Do the Parts Fit Together? (SAX2)
2.2.3.2. Concerns when writing XML text (SAX2)
2.6.4. ContentHandler and Prefix Mappings (SAX2)
4.1. More About ContentHandler (SAX2)
A.1.3. The ContentHandler Interface (SAX2)
callbacks: 2.3.4. Essential ContentHandler Callbacks (SAX2)
4.1.1. Other ContentHandler Methods (SAX2)
DOM documents, building: 4.4.1. SAX-to-DOM Consumers (SAX2)
events: 2.3. Basic ContentHandler Events (SAX2)
example of elements and text: 2.3.2. Example: Elements and Text (SAX2)
internationalization and: 4.1.3. Internationalization Concerns (SAX2)
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
ContentHandler instance: 3.5.3. The Misplaced Locator (Java & XML)
ContentHandler interface: A.1.1.3. ContentHandler (Java & XML)
implementation: 3.3. Content Handlers (Java & XML)
ContentHandler interface (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
methods called by parser: 19.1. The ContentHandler Interface (XML in a Nutshell)
overriding methods in: 19.1. The ContentHandler Interface (XML in a Nutshell)
vs. java.net.ContentHandler class: 19.1. The ContentHandler Interface (XML in a Nutshell)
ContentHandler.characters( ) function: B.8. Character Information Items (SAX2)
ContentHandler.endElement( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
ContentHandler.ignorableWhitespace( ) function: B.8. Character Information Items (SAX2)
ContentHandler.processingInstruction( ): B.6. Processing Instruction Information Items (SAX2)
ContentHandler.skippedEntity( ) function: B.3. Document Information Item (SAX2)
ContentHandler.startDocument( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
ContentHandler.startElement( ) function: B.4. Element Information Items (SAX2)
B.4. Element Information Items (SAX2)
AttributesImpl class and: 5.1.1. The AttributesImpl Class (SAX2)
parameters for element names: 2.6.2.1. Element naming (SAX2)
ContentHandler.startPrefixMapping( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
Content.skippedEntity( ) function: B.7. Unexpanded Entity Reference Information Items (SAX2)
contents.xml: 3.1.3. Have an XML Document on Hand (Java & XML)
10.3.3. Browser-Dependent Styling (Java & XML)
10.3.3. Browser-Dependent Styling (Java & XML)
WAP stylesheet reference: 10.3.4. WAP and XML (Java & XML)
context: 3.2.1. The Context (XSLT)
XPath: B.6. The XPath Context (XSLT)
context lists: 3.4. Putting Parsers to Work (Perl & XML)
context node: 9.2. Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.1. The Context (XSLT)
B.6. The XPath Context (XSLT)
attribute location steps and: 9.2.3. Attribute Location Steps (XML in a Nutshell)
changing, using here( ) and origin( ): 11.7.5. Relative XPointers (XML in a Nutshell)
child element location steps and: 9.2.2. Child Element Location Steps (XML in a Nutshell)
compound location paths and: 9.3. Compound Location Paths (XML in a Nutshell)
selecting, using a period (.): 9.3.4. Selecting the Context Node with . (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
unabbreviated location paths: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
context position: 3.2.1. The Context (XSLT)
B.6. The XPath Context (XSLT)
context size: 3.2.1. The Context (XSLT)
B.6. The XPath Context (XSLT)
conversions (encoding): 3.9.4. Encoding Conversion (Perl & XML)
converter program for stream processing: 4.4. Stream Applications (Perl & XML)
Coordinated Universal Time (UTC): 4.5.1. The Realm of ISO 8601 (XML Schema)
4.5.1. The Realm of ISO 8601 (XML Schema)
Coptic Unicode block: 26.3.6. Greek and Coptic (XML in a Nutshell)
copy-of construct: 2.3.3.3. Copying (Java & XML)
core handler interfaces: 3.3. Content Handlers (Java & XML)
count(@*) &gt; 0 expression: 3.5.2. The Stylesheet (XSLT)
count( ) function: C. XSLT and XPath Function Reference (XSLT)
9.7.1. Node-Set Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
counting words with regular expressions: 6.5.1.2. Counting words (XML Schema)
coupling (loose): 6.2.1. XML/Internet Versus Older Technologies (SAX2)
Cover, Robin: 3.10. Locating Standard DTDs (XML in a Nutshell)
Cowan, John: A.3.2. The W3C XML Schema Family (XML Schema)
Cp1252 character set: 5.7.1. Cp1252 (XML in a Nutshell)
26.1.2.1. C1 controls (XML in a Nutshell)
CPAN (Comprehensive Perl Archive Network): 0.1. Assumptions (Perl & XML)
1.1. Why Use Perl with XML? (Perl & XML)
modules, finding: 3.2. XML::Parser (Perl & XML)
XML programming tool and: 9.3.2. Further Ruminations on DBI and SAX (Perl & XML)
createAttribute( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createAttribute method: 7.2.1.2. Methods (Perl & XML)
createAttributeNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createAttributeNS method: 7.2.1.2. Methods (Perl & XML)
createCDATASection( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createCDATASection method: 7.2.1.2. Methods (Perl & XML)
createComment( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createComment method: 7.2.1.2. Methods (Perl & XML)
createDocType( ): 6.1.1. Creating a New DOM Tree (Java & XML)
createDocument( ): 6.1.1. Creating a New DOM Tree (Java & XML)
6.2. Namespaces (Java & XML)
createDocument( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createDocumentFragment( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createDocumentFragment method: 7.2.1.2. Methods (Perl & XML)
createDocumentType( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createElement( ): 6.1.1. Creating a New DOM Tree (Java & XML)
createElement( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createElement method: 7.2.1.2. Methods (Perl & XML)
createElementNS( ): 6.2. Namespaces (Java & XML)
createElementNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createElementNS method: 7.2.1.2. Methods (Perl & XML)
createEntityReference( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createEntityReference method: 7.2.1.2. Methods (Perl & XML)
createProcessingInstruction( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createProcessingInstruction method: 7.2.1.2. Methods (Perl & XML)
createRange( ): 6.3.3. Range (Java & XML)
createTextNode( ): 6.1.1. Creating a New DOM Tree (Java & XML)
createTextNode( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
createTextNode method: 7.2.1.2. Methods (Perl & XML)
createXMLReader( ) (SAX): 19. Simple API for XML (SAX) (XML in a Nutshell)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
SAX filters and: 19.3. Filters (XML in a Nutshell)
createXMLRepresentation( ): 7.2.3. Creating XML with JDOM (Java & XML)
7.2.3. Creating XML with JDOM (Java & XML)
creating
cross-references: 5. Creating Links and Cross-References (XSLT)
links: 5. Creating Links and Cross-References (XSLT)
using id( ) function: 5.1. Generating Links with the id( ) Function (XSLT)
using key( ) function: 5.2. Generating Links with the key( ) Function (XSLT)
creation methods and namespaces: 6.2. Namespaces (Java & XML)
Crimson parser: 1.4.5. Sun's Java API for XML Processing (JAXP) (SAX2)
1.6.2. Crimson (SAX2)
4.4.1. SAX-to-DOM Consumers (SAX2)
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags (SAX2)
cross-references
creating: 5. Creating Links and Cross-References (XSLT)
in stylesheet: 5.1.3. A Stylesheet That Uses the id( ) Function (XSLT)
CSS (Cascading Style Sheets): 1.1. The Design of XSLT (XSLT)
illustrated in sample stylesheet: 3.5.2. The Stylesheet (XSLT)
CSS3 (Cascading Style Sheets Level 3): 12.1. The Three Levels of CSS (XML in a Nutshell)
CSV (Comma Separated Values) files, turning into SAX events: 3.5.2.1. Turning CSV files into SAX events (SAX2)
Currency Symbols Unicode block: 26.3.18. Currency Symbols (XML in a Nutshell)
current( ) function: C. XSLT and XPath Function Reference (XSLT)
current node: 2.3.4. Walking Through Our Example (XSLT)
7.4.2. Implementing Lookup Tables (XSLT)
select attribute and: 4.2.3.1. <xsl:for-each> example (XSLT)
in XPath expressions: 8.2. XPath (Perl & XML)
current( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
custom data structures, turning SAX event into: 4.4.4. Turning SAX Events into Custom Data Structures (SAX2)
custom parsing style: 3.2.2. Parsing Styles (Perl & XML)
CVS, online resources: 10.2.1. Source Code or Binaries (Java & XML)
Cyrillic Unicode block: 26.3.7. Cyrillic (XML in a Nutshell)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

d attribute, pie charts and: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
\d character class: 6.4.3.1. Classical Perl character classes (XML Schema)
data (see elements)
elements, contained in: 3.3.6. Element Data (Java & XML)
textual: 3.3.6. Element Data (Java & XML)
XML document headers, in: 2.1.1. XML 1.0 (Java & XML)
data attribute
CharacterData interface: 24.2. Object Reference (XML in a Nutshell)
ProcessingInstruction interface: 24.2. Object Reference (XML in a Nutshell)
data binding: 1.2.2. High-Level APIs (Java & XML)
15. Data Binding (Java & XML)
Castor framework: 15.2. Castor (Java & XML)
classes, generating: 15.2.2. Source Generation (Java & XML)
installation: 15.2.1. Installation (Java & XML)
marshalling and unmarshalling: 15.2.3. Marshalling and Unmarshalling (Java & XML)
class generation: 15.1.1. Class Generation (Java & XML)
JAXB: 15.4. JAXB (Java & XML)
marshalling and unmarshalling: 15.1.2. Unmarshalling (Java & XML)
use cases: 15.1.4. Use Cases (Java & XML)
Zeus framework: 15.3. Zeus (Java & XML)
classes, generating: 15.3.2. Class Generation (Java & XML)
installation: 15.3.1. Installation (Java & XML)
marshalling and unmarshalling: 15.3.3. Marshalling and Unmarshalling (Java & XML)
data format, XML: 1.2. Portable Data (XML in a Nutshell)
15. XML as a Data Format (XML in a Nutshell)
data model for XPath: 3.1. The XPath Data Model (XSLT)
data modeling: 3.5.2.3. Data modeling concerns (SAX2)
data-oriented DTDs: 3.9.1. Data-Oriented DTDs (XML in a Nutshell)
data parameter for processingInstruction( ) function: 4.1.1. Other ContentHandler Methods (SAX2)
data parameters: 2.8. Processing Instructions and Other Markup (Perl & XML)
data property
CharacterData class (DOM): 7.2.7.1. Properties (Perl & XML)
ProcessingInstruction class (DOM): 7.2.12.1. Properties (Perl & XML)
data structures: 1.1. Types of XML APIs (SAX2)
custom, turning events into: 4.4.4. Turning SAX Events into Custom Data Structures (SAX2)
flexibility: 1.2.2. Data Structure Flexibility (SAX2)
SAX events, turning into: 4.4. Turning SAX Events into Data Structures (SAX2)
data structures, URI mappings, storage in: 3.3.4. Namespace Callbacks (Java & XML)
data-type attribute, <xsl:sort> element and: 6.1.2.2. Attributes (XSLT)
data types, user-defined in SOAP: 12.1.2. Encoding (Java & XML)
Database Interface (DBI): 9.3.2. Further Ruminations on DBI and SAX (Perl & XML)
databases, accessing with extension element: 8.3.2. Accessing a Database with an Extension Element (XSLT)
databases and XML: 1.1.1. What XML Is Not (XML in a Nutshell)
15.1.4. Data Storage/Retrieval (XML in a Nutshell)
dataElement( ) Writer method: 3.8. XML::Writer (Perl & XML)
Data::Grove::Visitor module: 8.1. Tree Climbers (Perl & XML)
datatypes: 4. Using Predefined Simple Datatypes (XML Schema)
3.4. XPath Datatypes (XSLT)
B.5. Datatypes (XSLT)
adding constraints on: 5.5. Back to Our Library (XML Schema)
applying new restrictions to: 5.1.2. Multiple Restrictions and Fixed Attribute (XML Schema)
complex (see complex datatypes)
converting to boolean values: 4.2.1.1. Converting to boolean values (XSLT)
date: 4.5. Date and Time Datatypes (XML Schema)
decimal: 4.4.1. Decimal Types (XML Schema)
facets for: 5.1.1.6. Decimals (XML Schema)
float: 4.4.2. Float Datatypes (XML Schema)
lexical and value spaces: 4.1. Lexical and Value Spaces (XML Schema)
list (see list datatypes)
numeric: 4.4. Numeric Datatypes (XML Schema)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
predefined: 4. Using Predefined Simple Datatypes (XML Schema)
used in schemas: 4.8. Back to Our Library (XML Schema)
reference guide of: 16. Datatype Reference Guide (XML Schema)
restricting: 7.3.2.2. Derivation by restriction (XML Schema)
simple (see simple datatypes)
string: 4.3. String Datatypes (XML Schema)
supported by XML DTDs: 5.1.1. The ID, IDREF, and IDREFs Datatypes (XSLT)
time: 4.5. Date and Time Datatypes (XML Schema)
DataWriter class: 4.3.2. XMLWriter (Java & XML)
date datatypes: 4.5. Date and Time Datatypes (XML Schema)
constraining when no time zones are needed: 6.6. Back to Our Library (XML Schema)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
regular expressions and: 6.5.3. Datetimes (XML Schema)
datetime datatypes (see date datatypes time datatypes)
<db:accessDatabase> element: 8.3.2. Accessing a Database with an Extension Element (XSLT)
<dbaccess> element: 8.3.2. Accessing a Database with an Extension Element (XSLT)
DBD::MySQL module: 9.3.2. Further Ruminations on DBI and SAX (Perl & XML)
DBD::Oracle module: 9.3.2. Further Ruminations on DBI and SAX (Perl & XML)
DBD::Pg module: 9.3.2. Further Ruminations on DBI and SAX (Perl & XML)
DBI (Database Interface): 9.3.2. Further Ruminations on DBI and SAX (Perl & XML)
DCD (Document Content Description for XML) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
DDML (Document Definition Markup Language or XSchema) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
debug parsing style: 3.2.2. Parsing Styles (Perl & XML)
decimal character references: 5.10. Character References (XML in a Nutshell)
decimal datatypes, facets for: 5.1.1.6. Decimals (XML Schema)
decimal format patterns: 23.2. XSLT Elements (XML in a Nutshell)
23.3. XSLT Functions (XML in a Nutshell)
decimal types: 4.4.1. Decimal Types (XML Schema)
deck: 10.3.4. WAP and XML (Java & XML)
declaration handlers for SAX: 5.7.2. SAX2 Handler Interface (Perl & XML)
declarations: 1.2.2.7. XML declarations (XSLT)
attribute: 3.3. Attribute Declarations (XML in a Nutshell)
commenting out: 3.8. Conditional Inclusion (XML in a Nutshell)
DeclHandler interface and: 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
default: 3.3.2. Attribute Defaults (XML in a Nutshell)
20.4.2. Validity Constraints (XML in a Nutshell)
document type: 3.1.2. The Document Type Declaration (XML in a Nutshell)
element (see element declarations)
element type (see element type declarations)
encoding: 5.2. The Encoding Declaration (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
external
including: 16.8.1.1. Including external declarations (XML in a Nutshell)
modifying: 16.8.1.2. Modifying external declarations (XML in a Nutshell)
general entity: 3.4. General Entity Declarations (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
namespace: 10.2. Namespace Declarations (XML Schema)
notation: 3.6. External Unparsed Entities and Notations (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
names must be unique: 20.4.2. Validity Constraints (XML in a Nutshell)
unparsed entities and: 20.4.2. Validity Constraints (XML in a Nutshell)
standalone: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
text (see text declarations)
declarations (XML): 1.6. XML Gotchas (Perl & XML)
2.7. The XML Declaration (Perl & XML)
elements: 2.10. Declaring Elements and Attributes (Perl & XML)
DeclHandler interface: 4.4.2. DeclHandler (Java & XML)
A.1.2.1. DeclHandler (Java & XML)
4.3.1. The DeclHandler Interface (SAX2)
A.3.1. The DeclHandler class (SAX2)
DOM documents, building: 4.4.1. SAX-to-DOM Consumers (SAX2)
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
DeclHandler interface (SAX): 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
DeclHandler.attributeDecl( ) function: 2.3.3.3. Other attributes issues (SAX2)
DeclHandler.externalEntityDexl( ) function: B.7. Unexpanded Entity Reference Information Items (SAX2)
decorators: 8.3. Wrappers and Decorators (Java & XML)
default declarations for attributes: 3.3.2. Attribute Defaults (XML in a Nutshell)
XML specification for: 20.4.2. Validity Constraints (XML in a Nutshell)
default namespaces: 10.1. Namespaces Present Two Challenges to Schema Languages (XML Schema)
JDOM: 8.1.3. The Namespace Class (Java & XML)
importing schemas with no namespaces: 10.9. Importing Schemas with No Namespaces (XML Schema)
not applied to
attributes: 10.3. To Qualify Or Not to Qualify? (XML Schema)
10.4. Disruptive Attributes (XML Schema)
XPath expressions: 10.5. Namespaces and XPath Expressions (XML Schema)
setting: 4.2.4. Setting a Default Namespace with the xmlns Attribute (XML in a Nutshell)
20.3.4. Namespaces (XML in a Nutshell)
using target namespaces as: 10.3. To Qualify Or Not to Qualify? (XML Schema)
default templates for XML document nodes: 8.7.1. The Default Template Rule for Text and Attribute Nodes (XML in a Nutshell)
default values, XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
DefaultHandler class: 4.2.3. The DefaultHandler Class (Java & XML)
9.3.1.1. The road to SAX 2.0 (Java & XML)
A.1.3.3. DefaultHandler (Java & XML)
2.2.1. How Do the Parts Fit Together? (SAX2)
2.3.1. The DefaultHandler Class (SAX2)
3.1.1. The XMLReader Interface (SAX2)
A.2.3. The DefaultHandler Class (SAX2)
DefaultHandler class (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
DefaultMutableTreeNode class: 3.2. SAX Readers (Java & XML)
DefaultTreeModel class: 3.2. SAX Readers (Java & XML)
deferred DOM: 5.4.1. Memory, Performance, and Deferred DOMs (Java & XML)
deleteContents( ): 6.3.3. Range (Java & XML)
deleteData( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
deleteData method for CharacterData class (DOM): 7.2.7.2. Methods (Perl & XML)
delimiters (<!): 3.7.1. DTDs (Perl & XML)
deployment descriptors: 12.3.2.2. Deployment descriptors (Java & XML)
depth-first order in stacks: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
derivation methods: 5. Creating Simple Datatypes (XML Schema)
asymmetry of: 7.4.2.3. Asymmetry of these two methods (XML Schema)
controlling usage of: 12.2. Controlling Derivations (XML Schema)
by extension
of complex content complex types: 7.4.2.1. Derivation by extension (XML Schema)
7.7. Back to Our Library (XML Schema)
of empty content complex types: 7.6.2.1. Derivation by extension (XML Schema)
of mixed content complex types: 7.5.2.1. Derivation by extension (XML Schema)
of simple content complex types: 7.3.2.1. Derivation by extension (XML Schema)
importance of order: 5.4.1. Beware of the Order (XML Schema)
by list: 5.2. Derivation By List (XML Schema)
by restriction: 5.1. Derivation By Restriction (XML Schema)
of complex content complex types: 7.4.2.2. Derivation by restriction (XML Schema)
7.7. Back to Our Library (XML Schema)
11.2.2. Defining Complex Types (XML Schema)
12.1.4.2. Restricting substitution groups (XML Schema)
of empty content complex types: 7.6.2.2. Derivation by restriction (XML Schema)
of mixed content complex types: 7.5.2.2. Derivation by restriction (XML Schema)
of simple content complex types: 7.3.2.2. Derivation by restriction (XML Schema)
by union: 5.3. Derivation By Union (XML Schema)
11.2.1. Defining Simple Types (XML Schema)
derived types: 16.2.5. Attribute Declarations (XML in a Nutshell)
controlling: 16.9. Controlling Type Derivation (XML in a Nutshell)
deriving
by extension: 16.8.2.1. Deriving by extension (XML in a Nutshell)
by restriction: 16.8.2.2. Deriving by restriction (XML in a Nutshell)
using: 16.8.2.3. Using derived types (XML in a Nutshell)
xs:complexContent element and: 16.5.1. complexContent (XML in a Nutshell)
Derkson, Enno: 7.3. XML::DOM (Perl & XML)
descendant axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
descendant nodes: 6.1. XML Trees (Perl & XML)
descendant-or-self axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
deserializing: 4.4. Turning SAX Events into Data Structures (SAX2)
design
of XML documents, for tutorials: 9.4. XML Document Design (XSLT)
procedural: 4.6.1.1. Procedural design (XSLT)
recursive: 4.6.1.2. Recursive design (XSLT)
7.2.1.1. Recursive design (XSLT)
Toot-O-Matic and: 9.2. Design Goals (XSLT)
design tools, affecting runtime: 1.2.2. Data Structure Flexibility (SAX2)
destination formats: 2.1. A Brief History of XML (Perl & XML)
deterministic content models: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
Devanagari Unicode block: 26.3.11. Devanagari (XML in a Nutshell)
developerWorks web site (IBM): 9. Case Study: The Toot-O-Matic (XSLT)
diacritical marks: 3.9.2. Unicode Encodings (Perl & XML)
diagnostics: 2.5.3. Errors and Diagnostics (SAX2)
<dict> element: 8.2. XPath (Perl & XML)
digital signatures, specification for: 1.2.4.11. XML digital signatures (XSLT)
Dingbats Unicode block: 26.3.26. Dingbats (XML in a Nutshell)
disable-output-escaping attribute: D.13. How Do I Control Angle Brackets and Quote Marks in My Output? (XSLT)
display property (CSS): 12.5. The Display Property (XML in a Nutshell)
div (XPath arithmetic operator): 9.6.1. Numbers (XML in a Nutshell)
DocBook: 2.1. A Brief History of XML (Perl & XML)
8.4. Optimized Tree Processing (Perl & XML)
10.1. Perl and XML Namespaces (Perl & XML)
6.4. DocBook (XML in a Nutshell)
converting XML to HTML: 2.12. Transformations (Perl & XML)
4.4. Stream Applications (Perl & XML)
entity sets and: 5.10. Character References (XML in a Nutshell)
tree-based processing and: 8.4. Optimized Tree Processing (Perl & XML)
doctype( ) Writer method: 3.8. XML::Writer (Perl & XML)
doctype attribute (Document interface): 24.2. Object Reference (XML in a Nutshell)
DocType class: A.4.1.4. DocType (Java & XML)
DOCTYPE declaration: 3.1.2. The Document Type Declaration (XML in a Nutshell)
20.4.2. Validity Constraints (XML in a Nutshell)
DOCTYPE keyword: 2.5. Entities (Perl & XML)
doctype property: 7.2.1.1. Properties (Perl & XML)
doctype_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers (Perl & XML)
Document class: A.4.1.5. Document (Java & XML)
Document class in DOM: 7.2.1. Document (Perl & XML)
Document Content Description for XML (DCD) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
Document Definition Markup Language or XSchema (DDML) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
document element (see root element)
root element: 1.2.2.1. An XML document must be contained in a single element (XSLT)
document elements: 2.5. Entities (Perl & XML)
2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
document( ) function: 7. Combining XML Documents (XSLT)
C. XSLT and XPath Function Reference (XSLT)
examples of: 7.2. The document( ) Function (XSLT)
invoking: 7.3. Invoking the document( ) Function (XSLT)
sorting with: 7.4.1. The document( ) Function and Sorting (XSLT)
Document Information Items: B.3. Document Information Item (SAX2)
document instance: 3.7. Document Validation (Perl & XML)
Document interface: 5.2.3.3. Working with nodes (Java & XML)
A.2.1.5. Document (Java & XML)
Document interface (DOM): 18.4.2.1. Document (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
attributes for: 24.2. Object Reference (XML in a Nutshell)
methods for: 24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
document locator: 3.3.1. The Document Locator (Java & XML)
document model modules, defining parameter entity references with: 7.3.3. Mixing Your Own XHTML (XML in a Nutshell)
Document node: 5.2.3.3. Working with nodes (Java & XML)
Document Object Model (see DOM)
3.5. XML::LibXML (Perl & XML)
1.1. Types of XML APIs (SAX2)
0.. What This Book Covers (XML in a Nutshell)
1.2.3. DOM and SAX (XSLT)
document prologs in DTD handlers: 5.2. DTD Handlers (Perl & XML)
Document Schema Definition Language (DSDL): B.2. ISO: DSDL (XML Schema)
Document Style Sheet and Semantics Language (DSSSL): 1.4. The Evolution of XML (XML in a Nutshell)
6.6. Transformation and Presentation (XML in a Nutshell)
document type declaration: 3.1.2. The Document Type Declaration (XML in a Nutshell)
20.4.2. Validity Constraints (XML in a Nutshell)
Document Type Declarations (see DTDs)
Document Type Definitions (see DTDs)
Document Type Definitions (DTDs): 1.2. W3C XML Schema (XML Schema)
A.3.1. The DTD Family (XML Schema)
sample application: A.5. XML DTDs (XML Schema)
Document Type Descriptions (see DTDs)
document validation: 3.7. Document Validation (Perl & XML)
document( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
DocumentBuilder class: 13.4.5. Writing a Client (Java & XML)
A.3.1.1. DocumentBuilder (Java & XML)
DocumentBuilderFactory class: A.3.1.2. DocumentBuilderFactory (Java & XML)
JAXP v. 1.1: 9.3.1. Updating the Standards (Java & XML)
documentElement attribute (Document interface): 24.2. Object Reference (XML in a Nutshell)
documentElement property: 7.2.1.1. Properties (Perl & XML)
DocumentFragment class (DOM): 7.2.2. DocumentFragment (Perl & XML)
DocumentFragment interface: A.2.1.6. DocumentFragment (Java & XML)
DocumentFragment interface (DOM): 18.4.2.2. DocumentFragment (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
DOCUMENT_FRAGMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
DocumentHandler interface: A.1.1.4. DocumentHandler (Java & XML)
5.2. SAX1 Support (SAX2)
A.1.4. The DocumentHandler Interface (SAX2)
documenting
schemas: 14. Documenting Schemas (XML Schema)
XML vocabularies: 1.1.2. Documentation (XML Schema)
DOCUMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
DocumentRange interface: 6.3.3. Range (Java & XML)
documents (see XML documents)
documents (XML): 1.5. Keep in Mind... (Perl & XML)
DocumentType class (DOM), properties of: 7.2.3. DocumentType (Perl & XML)
DocumentType interface: A.2.1.7. DocumentType (Java & XML)
DocumentType interface (DOM): 18.4.1.1. DocumentType (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
DOCUMENT_TYPE_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
does_element_exist( ) function: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
doGet( ): 6.1.2. Modifying a DOM Tree (Java & XML)
DOM Core: 24. DOM Reference (XML in a Nutshell)
content nodes: 18.4.2. Content Nodes (XML in a Nutshell)
structural nodes: 18.4.1. Structural Nodes (XML in a Nutshell)
structure of: 18.2. Structure of the DOM Core (XML in a Nutshell)
DOM (Document Object Model): 1.2.1. Low-Level APIs (Java & XML)
5. DOM (Java & XML)
3.5. XML::LibXML (Perl & XML)
7. DOM (Perl & XML)
1.1. Types of XML APIs (SAX2)
0.. What This Book Covers (XML in a Nutshell)
18. Document Object Model (DOM) (XML in a Nutshell)
1.2.3. DOM and SAX (XSLT)
advanced topics: 6. Advanced DOM (Java & XML)
building partial: 4.4.3. Building a Partial DOM (SAX2)
class interface reference: 7.2. DOM Class Interface Reference (Perl & XML)
common problems: 6.5. Gotcha! (Java & XML)
consumer classes: 4.4.1. SAX-to-DOM Consumers (SAX2)
deferred DOM: 5.4.1. Memory, Performance, and Deferred DOMs (Java & XML)
document structure: 18.3.3. Relating Document Structure to Nodes (XML in a Nutshell)
event production and: 3.5.1. DOM-to-SAX Event Production (and DOM4J, JDOM) (SAX2)
generic vs. specific interfaces: 18.2.1. Generic Versus Specific DOM Interfaces (XML in a Nutshell)
HTML, and: 6.3.5. HTML (Java & XML)
IDL (Interface Definition Language) notation and: 18.1.1. DOM Notation (XML in a Nutshell)
JAXP
methods, Level 2: 9.3.1.3. Updates to the DOM (Java & XML)
Version 1.0, using with: 9.2.2. Dealing with DOM (Java & XML)
versions supported under: 9.1.2. The Old and the New (Java & XML)
Level 3: 6.4. DOM Level 3 (Java & XML)
bootstrapping: 6.4.3. Bootstrapping (Java & XML)
Level 2 modules: 6.3. DOM Level 2 Modules (Java & XML)
Events: 6.3.4.1. Events (Java & XML)
online resources: 6.3.6. Odds and Ends (Java & XML)
Range: 6.3.3. Range (Java & XML)
Style: 6.3.4.3. Style (Java & XML)
Traversal: 6.3.2. Traversal (Java & XML)
Views: 6.3.4.2. Views (Java & XML)
Level 1 vs. Level 2: 18.1. DOM Foundations (XML in a Nutshell)
24. DOM Reference (XML in a Nutshell)
memory consumption with SAX: 1.2.3. Memory Consumption with SAX and DOM (SAX2)
memory requirements with XML documents: 5.4.1. Memory, Performance, and Deferred DOMs (Java & XML)
module specifications, Levels 1 and 2: 6.3.1. Branching Out (Java & XML)
namespace declarations, and: 6.2. Namespaces (Java & XML)
namespace support, Level 2: 6.2. Namespaces (Java & XML)
Node interface: 24. DOM Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
online resources: 5.1. The Document Object Model (Java & XML)
organization in levels: 5.1. The Document Object Model (Java & XML)
overloading options, lack of: 6.2. Namespaces (Java & XML)
parsers: 5.2.1. Getting a DOM Parser (Java & XML)
Level 2, online sources: 6.3. DOM Level 2 Modules (Java & XML)
output: 5.2.2. DOM Parser Output (Java & XML)
throwing SAX exceptions: 5.4.3. DOM Parsers Throwing SAX Exceptions (Java & XML)
parsing a document with: 18.6. Parsing a Document with DOM (XML in a Nutshell)
reference: 24. DOM Reference (XML in a Nutshell)
risks, memory and data: 5.4.1. Memory, Performance, and Deferred DOMs (Java & XML)
sample application: 18.7. A Simple DOM Application (XML in a Nutshell)
SAX, contrasted with: 5.1. The Document Object Model (Java & XML)
5.1.3.1. SAX is sequential (Java & XML)
specification for: 1.2.4.5. Document Object Model (DOM) Level 1 (XSLT)
standardization: 7.4.2. SAX and DOM as Standards (Java & XML)
strengths/weaknesses: 18.1.2. DOM Strengths and Weaknesses (XML in a Nutshell)
structures, comparing: 6.4.2. Node Comparisons (Java & XML)
TrAX and: 23.4. TrAX (XML in a Nutshell)
tree, vs. XPath conceptual tree: 3.1. The XPath Data Model (XSLT)
trees
creating: 6.1.1. Creating a New DOM Tree (Java & XML)
memory requirements: 5.1.3.3. Why use SAX at all? (Java & XML)
modifying: 6.1.2. Modifying a DOM Tree (Java & XML)
mutability: 6.1. Changes (Java & XML)
serialization: 5.2. Serialization (Java & XML)
unappended nodes: 6.5.2. Creating, Appending, and Inserting (Java & XML)
web publishing frameworks, support by: 10.1.2. Integration with Other XML Tools and APIs (Java & XML)
wrong document exception: 6.5.1. The Dreaded WRONG DOCUMENT Exception (Java & XML)
XML::DOM module: 7.3. XML::DOM (Perl & XML)
DOM Level 3: 1.4. The Evolution of XML (XML in a Nutshell)
18. Document Object Model (DOM) (XML in a Nutshell)
18.6. Parsing a Document with DOM (XML in a Nutshell)
DOM Level 3 specification, W3C XML Schema's impact on: B.1.2. DOM (XML Schema)
dom-node property: 4.1.2.4. DOM nodes and literal strings (Java & XML)
DOM trees: 4.4.1. SAX-to-DOM Consumers (SAX2)
constructing with SAX 1.0: 1.4.2. SAX2 (SAX2)
pruning noise data from: 4.4.2. Pruning Noise Data from a DOM Tree (SAX2)
SAX events, turning into: 3.5.1.1. Turning DOM trees into SAX events (SAX2)
DOMAdapter class: A.4.2.2. DOMAdapter (Java & XML)
DOMBuilder class: 7.1.3. Input and Output (Java & XML)
A.4.3.2. DOMBuilder (Java & XML)
performance concerns: 7.5.3. DOMBuilder (Java & XML)
DOMException class: A.2.1.8. DOMException (Java & XML)
DOMException interface: 24.2. Object Reference (XML in a Nutshell)
DOMFaultListener: 12.4.2. Better Error Handling (Java & XML)
DOMImplementation interface: A.2.1.9. DOMImplementation (Java & XML)
18.4.2.1. Document (XML in a Nutshell)
18.5. The DOMImplementation Interface (XML in a Nutshell)
methods for: 24.2. Object Reference (XML in a Nutshell)
DOMit servlet: 1.2.3.1.1. A sample DOM tree (XSLT)
DOM4J: 4.4.1. SAX-to-DOM Consumers (SAX2)
trees, turning into SAX events: 3.5.1.2. Turning DOM4J trees into SAX events (SAX2)
DOMOutputter: 7.1.3. Input and Output (Java & XML)
8.1.3. The Namespace Class (Java & XML)
A.4.4. Package: org.jdom.output (Java & XML)
DOMResult class: A.3.3. Package: javax.xml.transform.dom (Java & XML)
DOMSerializer class: 5.2.3. DOMSerializer (Java & XML)
skeleton: 5.2.3.1. Getting started (Java & XML)
warning: 5.2.4. The Results (Java & XML)
DOMSerializer.java: 5.2.3.1. Getting started (Java & XML)
DOMSource class: A.3.3. Package: javax.xml.transform.dom (Java & XML)
DOMSTRING_SIZE_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
doPost( ): 6.1.1. Creating a New DOM Tree (Java & XML)
6.3.3. Range (Java & XML)
namespaces, updating: 6.2. Namespaces (Java & XML)
DOS
filenames, turning into URIs: 3.1.3. Filenames Versus URIs (SAX2)
XML output, writing: 2.2.3.2. Concerns when writing XML text (SAX2)
double colon (::) used with axes: 3.2.6. Axes (XSLT)
double colon (::), using boolean tests in XPath: 8.2. XPath (Perl & XML)
double period (..) indicating parent axis: 3.2.6.2. Axis roll call (XSLT)
double quotes (" "): D.1. How Do I Put Quotes Inside an Attribute Value? (XSLT)
double slash (//) operator: 3.2.5. Using Wildcards (XSLT)
drivers for non-XML sources: 5.4. Drivers for Non-XML Sources (Perl & XML)
DSDL (Document Schema Definition Language): B.2. ISO: DSDL (XML Schema)
DSSSL (Document Style Sheet and Semantics Language): 1.4. The Evolution of XML (XML in a Nutshell)
6.6. Transformation and Presentation (XML in a Nutshell)
DTD-based validation: 2.4. Producer-Side Validation (SAX2)
DTD (Document Type Definition): 2.2.1. DTDs (Java & XML)
additional constraint models: 2.2.2.2. Multiple namespaces (Java & XML)
attributes: 2.2.1.2. Attributes (Java & XML)
contrasted with validation: 4.1.2.1. Validation (Java & XML)
ELEMENT definitions: 2.2.1.1. Elements (Java & XML)
entities: 2.2.1.3. Entities (Java & XML)
recurrence modifiers: 2.2.1.1. Elements (Java & XML)
RSS and: 14.3.1. Rich Site Summary (Java & XML)
DTDHandler interface: 4.2.2. Using a DTDHandler (Java & XML)
A.1.1.5. DTDHandler (Java & XML)
2.2.2. What Are the SAX2 Event Handlers? (SAX2)
3.1.1. The XMLReader Interface (SAX2)
4.3.2. The DTDHandler Interface (SAX2)
A.1.5. The DTDHandler Interface (SAX2)
and validation: 4.5.2. DTDHandler and Validation (Java & XML)
DOM documents, building: 4.4.1. SAX-to-DOM Consumers (SAX2)
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
DTDHandler interface (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
DTDHandler.notationDecl( ) function: 4.1.1. Other ContentHandler Methods (SAX2)
B.12. Notation Information Items (SAX2)
DTDHandler.unparsedEntityDexl( ) function: B.11. Unparsed Entity Information Items (SAX2)
DTDs: 1.1. The Benefits of XML (XML in a Nutshell)
3. Document Type Definitions (DTDs) (XML in a Nutshell)
attribute declarations: 3.3. Attribute Declarations (XML in a Nutshell)
data-oriented: 3.9.1. Data-Oriented DTDs (XML in a Nutshell)
defining general entity references: 5.10. Character References (XML in a Nutshell)
document permanence and: 6.5. Document Permanence (XML in a Nutshell)
element declarations: 3.2. Element Declarations (XML in a Nutshell)
external subsets (see external DTD subsets)
finding standard: 3.10. Locating Standard DTDs (XML in a Nutshell)
general entities: 3.5. External Parsed General Entities (XML in a Nutshell)
general entity declarations: 3.4. General Entity Declarations (XML in a Nutshell)
internal subsets (see internal DTD subsets)
namespaces and: 4.4. Namespaces and DTDs (XML in a Nutshell)
narrative-oriented: 3.9.2. Narrative-Oriented DTDs (XML in a Nutshell)
6.2. Narrative Document Structures (XML in a Nutshell)
parameter entities: 3.7. Parameter Entities (XML in a Nutshell)
rddl:resource elements and: 14.2. RDDL Syntax (XML in a Nutshell)
repository of: 3.10. Locating Standard DTDs (XML in a Nutshell)
specifying rules using: 1.3. How XML Works (XML in a Nutshell)
standalone attribute and: 2.9.2. standalone (XML in a Nutshell)
TEI (Text Encoding Initiative): 6.3. TEI (XML in a Nutshell)
validating XML documents: 3.1. Validation (XML in a Nutshell)
16.1.1. Schemas Versus DTDs (XML in a Nutshell)
for XHTML: 7.1.2. Three DTDs for XHTML (XML in a Nutshell)
for XLinks: 10.6. DTDs for XLinks (XML in a Nutshell)
XML specification for: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
DTDs (Document Type Declarations): 1.8. What XML Are We Talking About? (SAX2)
4.3. Exposing DTD Information (SAX2)
EntityReslover class, using: 3.4. The EntityResolver Interface (SAX2)
Information Items: B.10. Document Type Declaration Information Item (SAX2)
producer-side validation and: 2.4. Producer-Side Validation (SAX2)
subdocuments, including: 6.3. Including Subdocuments (SAX2)
DTDs (Document Type Definitions): 1.2. W3C XML Schema (XML Schema)
A.3.1. The DTD Family (XML Schema)
1.2.2.8. Document Type Definitions (DTDs) and XML Schemas (XSLT)
advantages of schemas over: 1.2.2.8. Document Type Definitions (DTDs) and XML Schemas (XSLT)
for XML, datatypes supported by: 5.1.1. The ID, IDREF, and IDREFs Datatypes (XSLT)
sample application: A.5. XML DTDs (XML Schema)
DTDs (Document Type Descriptions): 2. An XML Recap (Perl & XML)
2.5. Entities (Perl & XML)
3.7.1. DTDs (Perl & XML)
doctype property: 7.2.1.1. Properties (Perl & XML)
handlers: 5.2. DTD Handlers (Perl & XML)
5.7.2. SAX2 Handler Interface (Perl & XML)
well formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
Dublin Core: 7.4.2. Dublin Core (XML in a Nutshell)
RDF (Resource Description Framework) and: 4.1. The Need for Namespaces (XML in a Nutshell)
Dublin Core elements, allowed in schema annotations: 14.2. The W3C XML Schema Annotation Element (XML Schema)
duration, datatype for: 4.5.2. Datatypes (XML Schema)
dynamic content generation: 10. Web Publishing Frameworks (Java & XML)
Dynamic HTML: The Definitive Reference: 24.2. Object Reference (XML in a Nutshell)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

EBCDIC (Extended Binary Coded Decimal Interchange Code) encodings: 3.1.2.2. Providing entity text (SAX2)
EBNF (Extended Backus-Naur Form) grammar: 20.5. XML Document Grammar (XML in a Nutshell)
ECMAscript: 7.1. DOM and Perl (Perl & XML)
edit module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
editPersonalData.xslt: 6.3.1. Design (Java and XSLT)
6.3.2. XML and Stylesheets (Java and XSLT)
6.3.2. XML and Stylesheets (Java and XSLT)
EJB (Enterprise JavaBeans)
address class: 8.5. XSLT as a Code Generator (Java and XSLT)
dependent objects: 8.5. XSLT as a Code Generator (Java and XSLT)
XSLT, and: 4.3. XSLT and EJB (Java and XSLT)
Electric XML: 1.3.2. A Parser (Java & XML)
electronics symbols, Unicode block for: 26.3.22. Miscellaneous Technical (XML in a Nutshell)
element-available( ) function: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
C. XSLT and XPath Function Reference (XSLT)
element-available( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
Element class: A.4.1.6. Element (Java & XML)
setAttribute( ), JDOM: 7.2.3. Creating XML with JDOM (Java & XML)
subclassing: 8.2. JDOM and Factories (Java & XML)
Element constructor: 7.2.3. Creating XML with JDOM (Java & XML)
element content
any type: 16.8. Allowing Any Content (XML in a Nutshell)
empty elements: 16.5. Empty Elements (XML in a Nutshell)
mixed: 16.7. Mixed Content (XML in a Nutshell)
simple: 16.6. Simple Content (XML in a Nutshell)
ELEMENT declaration: 3.2. Element Declarations (XML in a Nutshell)
element declarations: 3.1.1. A Simple DTD Example (XML in a Nutshell)
3.2. Element Declarations (XML in a Nutshell)
abstract attribute: 16.9.1. Abstract Elements and Types (XML in a Nutshell)
complex types and: 16.4. Complex Types (XML in a Nutshell)
constructing: 20.4.2. Validity Constraints (XML in a Nutshell)
matching elements with DTD rules: 20.4.2. Validity Constraints (XML in a Nutshell)
occurrence constraints: 16.4.1. Occurrence Constraints (XML in a Nutshell)
schemas and: 16.2.1. Document Organization (XML in a Nutshell)
16.2.3. Element Declarations (XML in a Nutshell)
substitutionGroup attribute: 16.8.3. Substitution Groups (XML in a Nutshell)
element groups: 7.4.1.2. Element and attribute groups (XML Schema)
defining: 13.1.1.1. Elements (XML Schema)
13.1.1.1. Elements (XML Schema)
extensibility of, using substitution groups: 12.1. Substitution Groups (XML Schema)
redefining: 8.2.2. Redefinition of Element and Attribute Groups (XML Schema)
by extension: 8.2.2.1. Extension (XML Schema)
by restriction: 8.2.2.2. Restriction (XML Schema)
vs. substitution groups: 12.1.3. Traditional Declarations or Substitution Groups? (XML Schema)
Element interface: A.2.1.10. Element (Java & XML)
Element interface (DOM): 18.4.2.3. Element (XML in a Nutshell)
attribute for: 24.2. Object Reference (XML in a Nutshell)
methods for: 24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
ELEMENT keyword: 2.2.1.1. Elements (Java & XML)
element methods, JDOM: 8.2.1. Creating a Factory (Java & XML)
element names
matching: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
in namespaces and DTDs: 4.4. Namespaces and DTDs (XML in a Nutshell)
prefixes and: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
qualified: 20.3.4. Namespaces (XML in a Nutshell)
suffixes for: 3.2.4. The Number of Children (XML in a Nutshell)
unqualified: 20.3.4. Namespaces (XML in a Nutshell)
using choices in: 3.2.5. Choices (XML in a Nutshell)
using namespaces to resolve conflicts: 4.1. The Need for Namespaces (XML in a Nutshell)
Element node: 6.1. XML Trees (Perl & XML)
7.2.8. Element (Perl & XML)
element nodes
default template for: 8.7.2. The Default Template Rule for Element and Root Nodes (XML in a Nutshell)
XPath data model and: 22.1. The XPath Data Model (XML in a Nutshell)
element nodes (XPath): 3.1.2. Element Nodes (XSLT)
B.1.2. Element Nodes (XSLT)
element type declarations
duplicates types not allowed: 20.4.2. Validity Constraints (XML in a Nutshell)
uniqueness required for: 20.4.2. Validity Constraints (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
element types
changed from XML 1.0 Recommendation: 3.2.3. Where Have the Element Types Gone? (XML Schema)
defining: 11.2. Defining Element Types (XML Schema)
simple vs. complex: 2.2. Our First Schema (XML Schema)
xsi:type attribute and: 11.2. Defining Element Types (XML Schema)
elementDecl( ) function: 4.3.1. The DeclHandler Interface (SAX2)
element_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers (Perl & XML)
elementFormDefault attribute: 10.3. To Qualify Or Not to Qualify? (XML Schema)
elementFormDefault attribute (xs:schema element): 16.3.2. Controlling Qualification (XML in a Nutshell)
ELEMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
elements: 2.1.1.2. Elements (Java & XML)
1.2. XML Review (Java and XSLT)
2.2. Markup, Elements, and Structure (Perl & XML)
2.3.2. Example: Elements and Text (SAX2)
attributes: 2.1.1.3. Attributes (Java & XML)
attributes and: 2.3. Attributes (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
calculating the value of: 8.5. Calculating the Value of an Element with xsl:value-of (XML in a Nutshell)
case sensitivity: 2.1.1.2. Elements (Java & XML)
case sensitivity in: 0.. Conventions Used in This Book (XML in a Nutshell)
2.2.1.2. Case sensitivity (XML in a Nutshell)
child vs. parent: 3.3.6. Element Data (Java & XML)
complete reference to: A. XSLT Reference (XSLT)
complex types: 16.4. Complex Types (XML in a Nutshell)
consolidation of opening and closing tags: 2.1.1.2. Elements (Java & XML)
controlling derivations: 12.2.2. Elements (XML Schema)
controlling placement of: 16.7.2. Controlling Element Placement (XML in a Nutshell)
converting attributes to: D.2. How Do I Convert All Attributes to Elements? (XSLT)
creating classified lists of: 2.2. Our First Schema (XML Schema)
data contained in: 3.3.6. Element Data (Java & XML)
declaring: 2.10. Declaring Elements and Attributes (Perl & XML)
defining
globally: 2.2. Our First Schema (XML Schema)
locally: 3.1. Working From the Structure of the Instance Document (XML Schema)
3.2.2. Russian Doll and Object-Oriented Design (XML Schema)
using xs:ID and xs:IDREF types: 9.1. and (XML Schema)
documents and: 2.2. Elements, Tags, and Character Data (XML in a Nutshell)
empty1.2.2. XML Syntax (Java and XSLT)
2.2.1.1. Elements (Java & XML)
2.2.1.1. Empty elements (XML in a Nutshell)
enforcing uniqueness: 16.9.4. Uniqueness and Keys (XML in a Nutshell)
entity references in: 2.5. Entity References (XML in a Nutshell)
foreign attributes accepted by: 14.3. Foreign Attributes (XML Schema)
global (see global elements)
grouping: 6. Sorting and Grouping Elements (XSLT)
6.2. Grouping Nodes (XSLT)
Information Items: B.4. Element Information Items (SAX2)
JDOM, creation with: 7.2.3. Creating XML with JDOM (Java & XML)
linking: 5. Creating Links and Cross-References (XSLT)
listing, how to guide for: D.3. How Do I List All the Elements in an XML Document? (XSLT)
location steps: 9.2.2. Child Element Location Steps (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
mandatory closure: 2.1.1.2. Elements (Java & XML)
matching multiple: 9.2.6. Multiple Matches with | (XML in a Nutshell)
matching names: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
minOccurs/maxOccurs attributes: 16.4.1. Occurrence Constraints (XML in a Nutshell)
mixed content of: 2.2.3. Mixed Content (XML in a Nutshell)
naming: 2.6.1. What Namespaces Do to XML (SAX2)
with namespaces: 2.6.2. Element and Attribute Naming with Namespaces (SAX2)
naming conventions: 2.1.1.2. Elements (Java & XML)
in narrative-oriented documents: 6.2. Narrative Document Structures (XML in a Nutshell)
overlapping not allowed: 2.2.2.1. Parents and children (XML in a Nutshell)
prefixes and namespaces: 6.2. Namespaces (Java & XML)
Q names: 3.3.5. Element Callbacks (Java & XML)
reference guide of: 15. Elements Reference Guide (XML Schema)
root elements: 2.1.1.1. The root element (Java & XML)
rules governing: 1.2.2.1. An XML document must be contained in a single element (XSLT)
SAX callbacks: 3.3.5. Element Callbacks (Java & XML)
of schemas: 21.2. Schema Elements (XML in a Nutshell)
sorting: 6. Sorting and Grouping Elements (XSLT)
using <xsl:sort> element: 6.1. Sorting Data with <xsl:sort> (XSLT)
types: 2.2. Markup, Elements, and Structure (Perl & XML)
vs. attributes: 2.1.1.3. Attributes (Java & XML)
vs. tags: 1.2.2.10. Tags versus elements (XSLT)
XML specification for: 20.3.3. Document Body (XML in a Nutshell)
email panel (tutorials): 9.3.5. Email Panel (XSLT)
<Emphasis>Learning Perl, Third Edition<Default Para Font>: 0.1. Assumptions (Perl & XML)
<Emphasis>Namespaces in XML<Default Para Font>: 10. Controlling Namespaces (XML Schema)
empty content models: 2.2. Our First Schema (XML Schema)
creating: 7.6.1. Creation of Empty Content Models (XML Schema)
defining: 7.7. Back to Our Library (XML Schema)
derivation by extension: 7.6.2.1. Derivation by extension (XML Schema)
derivation by restriction: 7.6.2.2. Derivation by restriction (XML Schema)
simple vs. complex types for: 7.6.3. Simple or Complex Content Models for Empty Content Models? (XML Schema)
vs. null elements: 11.3. Defining Nil (Null) Values (XML Schema)
empty elements: 2.2.1.1. Empty elements (XML in a Nutshell)
in element declarations: 3.2.8. Empty Elements (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
NOTATION attributes not available for: 20.4.2. Validity Constraints (XML in a Nutshell)
schemas and: 16.5. Empty Elements (XML in a Nutshell)
web browser problems with: 7.1.3.2. Empty elements (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.3. Document Body (XML in a Nutshell)
EMPTY keyword: 2.2.1.1. Elements (Java & XML)
emptyTag( ) Writer method: 3.8. XML::Writer (Perl & XML)
empty templates: 3.5.3. Transforming Elements and Attributes (Java and XSLT)
encoding conversions: 3.9.4. Encoding Conversion (Perl & XML)
encoding declaration: 2.9.1. encoding (XML in a Nutshell)
5.2. The Encoding Declaration (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
encodeURL( ): 8.2.1. Servlet Session-Tracking API (Java and XSLT)
encoding, SOAP: 12.1.2. Encoding (Java & XML)
encodings (character): 2.6. Unicode, Character Sets, and Encodings (Perl & XML)
3.9. Character Sets and Encodings (Perl & XML)
encodings, datatypes for: 4.3.3.5. Binary string-encoded datatypes (XML Schema)
encodings, list of: 5.4. XML-Defined Character Sets (XML in a Nutshell)
Encryption, XML: 1.4. The Evolution of XML (XML in a Nutshell)
end( ) Writer methods: 3.8. XML::Writer (Perl & XML)
end-point( ): 11.6. Points (XML in a Nutshell)
end-tags: 2.2. Elements, Tags, and Character Data (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.3. Document Body (XML in a Nutshell)
end_cdata( ) event handler: 5.1. SAX Event Handlers (Perl & XML)
5.7.2.3. Lexical event handlers (Perl & XML)
endCDATA( ) function: 4.2. The LexicalHandler Interface (SAX2)
endDocument( ): 3.3.2. The Beginning and the End of a Document (Java & XML)
end_document( ) event handler: 5.1. SAX Event Handlers (Perl & XML)
5.7.2.1. Content event handlers (Perl & XML)
endDocument( ) function: 3.5.2.1. Turning CSV files into SAX events (SAX2)
4.1.1. Other ContentHandler Methods (SAX2)
endDocument( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
end_dtd( ) method: 5.7.2.3. Lexical event handlers (Perl & XML)
endDTD( ) function: 4.2. The LexicalHandler Interface (SAX2)
end_element( ) event handler: 5.1. SAX Event Handlers (Perl & XML)
5.7.2.1. Content event handlers (Perl & XML)
endElement( ) function: 2.3.2. Example: Elements and Text (SAX2)
2.3.4. Essential ContentHandler Callbacks (SAX2)
2.6.4. ContentHandler and Prefix Mappings (SAX2)
endElement( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
using filters with: 19.3. Filters (XML in a Nutshell)
end_entity( ) event handler: 5.7.2.3. Lexical event handlers (Perl & XML)
endEntity( ) function: 4.2. The LexicalHandler Interface (SAX2)
ending resources: 10.1. Simple Links (XML in a Nutshell)
end_prefix_mapping( ) method: 5.7.2.1. Content event handlers (Perl & XML)
endPrefixMapping( ) function: 2.6.3. Namespace Feature Flags (SAX2)
2.6.4. ContentHandler and Prefix Mappings (SAX2)
3.3.2. XMLReader Feature Flags (SAX2)
endPrefixMapping( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
endTag( ) Writer method: 3.8. XML::Writer (Perl & XML)
English, Joe: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
enterprise applications and XML: 15.1.1. Mixed Environments (XML in a Nutshell)
entities: 2.2.1.3. Entities (Java & XML)
1.6. XML Gotchas (Perl & XML)
2.5. Entities (Perl & XML)
DocumentType class (DOM): 7.2.3.1. Properties (Perl & XML)
external parsed: 3.5. External Parsed General Entities (XML in a Nutshell)
text declarations and: 5.3. Text Declarations (XML in a Nutshell)
external resolution: 5.3. External Entity Resolution (Perl & XML)
external unparsed: 3.6. External Unparsed Entities and Notations (XML in a Nutshell)
declaring notations: 20.4.2. Validity Constraints (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
parameter (see parameter entity references)
parsers and: 3.1. XML Parsers (Perl & XML)
resolve_entity( ) module and: 5.7.2.2. Entity resolver (Perl & XML)
well formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
ENTITIES attribute: 4.3.1. The DeclHandler Interface (SAX2)
unparsedEntityDecl( ) and: 4.3.2. The DTDHandler Interface (SAX2)
entities attribute (DocumentType interface): 24.2. Object Reference (XML in a Nutshell)
ENTITIES attribute type: 3.3.1.9. ENTITIES (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
ENTITY attribute: 4.3.1. The DeclHandler Interface (SAX2)
unparsedEntityDecl( ) and: 4.3.2. The DTDHandler Interface (SAX2)
ENTITY attribute type: 3.3.1.8. ENTITY (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
embedding unparsed entities in documents: 3.6.2. Embedding Unparsed Entities in Documents (XML in a Nutshell)
unparsed entities in DTD and: 20.4.2. Validity Constraints (XML in a Nutshell)
ENTITY declaration: 3.4. General Entity Declarations (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
absent from entity references: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
entity handling: 4.1.2.3. Interning and entities (Java & XML)
Entity interface: A.2.1.11. Entity (Java & XML)
Entity interface (DOM): 18.4.1.4. Entity (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
attributes for: 24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
ENTITY keyword: 2.2.1.3. Entities (Java & XML)
Entity node: 6.1. XML Trees (Perl & XML)
7.2.14. EntityReference (Perl & XML)
entity references: 2.1.1.4. Entity references and constants (Java & XML)
3.1. XML Parsers (Perl & XML)
2.5. Entity References (XML in a Nutshell)
2.5. Entity References (XML in a Nutshell)
3.4. General Entity Declarations (XML in a Nutshell)
3. XPath: A Syntax for Describing Needles and Haystacks (XSLT)
attribute values and external entities: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
JDOM builders, expansion under: 8.1.2. The EntityRef Class (Java & XML)
LexicalHandler interface and: 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
loopholes in XML specification: 17.2.1. What You Get Is Not What You Saw (XML in a Nutshell)
simplifying document parsing: 20.3.1. Global Syntax Structures (XML in a Nutshell)
unparsed entity names not allowed: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
web browser problems with: 7.1.3.3. Entity references (XML in a Nutshell)
with no entity declarations: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
XHTML 1.1 DTD: 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
XML specification for: 20.3.1. Global Syntax Structures (XML in a Nutshell)
entity sets
HTML 4.0: 26.2. HTML4 Entity Sets (XML in a Nutshell)
XHTML 1.0 DTD: 5.10. Character References (XML in a Nutshell)
26.2. HTML4 Entity Sets (XML in a Nutshell)
entity_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers (Perl & XML)
ENTITY_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
EntityRef class: 8.1.2. The EntityRef Class (Java & XML)
A.4.1.7. EntityRef (Java & XML)
EntityReference class: 7.2.14. EntityReference (Perl & XML)
EntityReference interface: A.2.1.12. EntityReference (Java & XML)
EntityReference interface (DOM): 18.4.2.7. EntityReference (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
entity_reference SAX method: 5.1. SAX Event Handlers (Perl & XML)
ENTITY_REFERENCE_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
EntityResolver interface: 3.3. Content Handlers (Java & XML)
4.2.1. Using an EntityResolver (Java & XML)
A.1.1.6. EntityResolver (Java & XML)
3.1.1. The XMLReader Interface (SAX2)
3.3.2. XMLReader Feature Flags (SAX2)
3.4. The EntityResolver Interface (SAX2)
A.1.6. The EntityResolver Interface (SAX2)
InputSource objects, creating with: 3.1.2.2. Providing entity text (SAX2)
EntityResolver interface (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
EntityResolver objects: 6.3. Including Subdocuments (SAX2)
enumerated values in attributeDecl( ): 4.3.1. The DeclHandler Interface (SAX2)
enumerateItem( ): 15.2.2. Source Generation (Java & XML)
enumeration attribute type: 3.3.1.4. Enumeration (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
assigning a value to: 20.4.2. Validity Constraints (XML in a Nutshell)
vs. NOTATION attribute type: 3.3.1.10. NOTATION (XML in a Nutshell)
enumeration facet element: 16.6.2.3. Enumerations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
envelope, SOAP: 12.1.1. The Envelope (Java & XML)
equals( ): 8.1.3. The Namespace Class (Java & XML)
equalsNode( ): 6.4.2. Node Comparisons (Java & XML)
Ericsson WapIDE: 10.1. Wireless Technologies (Java and XSLT)
error( ) exception handler: 5.7.2.4. Error event handlers and catching exceptions (Perl & XML)
error checking/handling, functions for: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
error handlers for SAX: 5.7.2. SAX2 Handler Interface (Perl & XML)
error handling: 1.2.1. Stream-Based Processing (SAX2)
2.4. Producer-Side Validation (SAX2)
diagnostics and: 2.5.3. Errors and Diagnostics (SAX2)
producer-side validation and: 2.4.2. Handling Validity Errors (SAX2)
error handling, SOAP: 12.4.2. Better Error Handling (Java & XML)
error messages and schema extensions: B.3.3. Schema Extensions: Error Messages (XML Schema)
error( ) method: 2.5.2. ErrorHandler Interface (SAX2)
ErrorHandler class: 2.2.1. How Do the Parts Fit Together? (SAX2)
2.5.2. ErrorHandler Interface (SAX2)
2.5.2. ErrorHandler Interface (SAX2)
2.5.3. Errors and Diagnostics (SAX2)
3.1.1. The XMLReader Interface (SAX2)
A.1.7. The ErrorHandler Interface (SAX2)
Locator class and: 4.1.2. The Locator Interface (SAX2)
producer-side validation, handling errors: 2.4.2. Handling Validity Errors (SAX2)
ErrorHandler interface: 3.4. Error Handlers (Java & XML)
A.1.1.7. ErrorHandler (Java & XML)
ErrorHandler interface (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
ErrorHandler object: 6.3. Including Subdocuments (SAX2)
ErrorHandler.error( ) function: 2.6.3. Namespace Feature Flags (SAX2)
ErrorListener interface: 9.3.2.1. Working with the factory (Java & XML)
A.3.2.1. ErrorListener (Java & XML)
errors
document not well-formed: 3.4.4. Breaking the Data (Java & XML)
during parsing: 3.3.2. The Beginning and the End of a Document (Java & XML)
ErrorHandler interface (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
fatal and non-fatal: 3.4.2. Nonfatal Errors (Java & XML)
SAXException class and: 25.1. The org.xml.sax Package (XML in a Nutshell)
validity: 1.3. How XML Works (XML in a Nutshell)
3. Document Type Definitions (DTDs) (XML in a Nutshell)
3.1.4. Validating a Document (XML in a Nutshell)
version of XML and: 3.4.4. Breaking the Data (Java & XML)
well-formedness: 1.3. How XML Works (XML in a Nutshell)
2.10. Checking Documents for Well-Formedness (XML in a Nutshell)
3. Document Type Definitions (DTDs) (XML in a Nutshell)
errors, detecting using validation: 1.1.1. Validation (XML Schema)
escape character (\): 6.2. The Simplest Possible Patterns (XML Schema)
special characters: 6.4.1. Special Characters (XML Schema)
user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
escape sequences: 2.5. Entity References (XML in a Nutshell)
escaping characters: 2.1.1.4. Entity references and constants (Java & XML)
EUC-JP encoding: 5.4. XML-Defined Character Sets (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
eval block, using XML::Parser module: 3.2.1. Example: Well-Formedness Checker Revisited (Perl & XML)
event-based parsers: 17.1.2. Treating XML as Events (XML in a Nutshell)
event handlers (see handlers)
event pipelines: 2.2.3.1. Event pipelines (SAX2)
event processing (parser): 3.3. Stream-Based Versus Tree-Based Processing (Perl & XML)
stream parsing styles and: 3.2.2. Parsing Styles (Perl & XML)
event producers: 3.1. Pull Mode Event Production with XMLReader (SAX2)
B.1. Event Producer Issues (SAX2)
defining custom: 1.2.1. Stream-Based Processing (SAX2)
DOM-to-SAX: 3.5.1. DOM-to-SAX Event Production (and DOM4J, JDOM) (SAX2)
push modes: 3.5.2. Push Mode Event Production (SAX2)
event streams: 4. Event Streams (Perl & XML)
applications: 4.4. Stream Applications (Perl & XML)
handlers: 4.2. Events and Handlers (Perl & XML)
working with: 4.1. Working with Streams (Perl & XML)
XML processing strategies and: 3.3. Stream-Based Versus Tree-Based Processing (Perl & XML)
XML::Parser module and: 4.6. XML::Parser (Perl & XML)
event streams, producing: 3.5.3. Producing Well-Formed Event Streams (SAX2)
EventConsumer interface: 4.5.4. The gnu.xml.pipeline Framework (SAX2)
events: 4.2. Events and Handlers (Perl & XML)
4. Consuming SAX2 Events (SAX2)
consumer issues: B.2. Event Consumer Issues (SAX2)
CSV files, turning into: 3.5.2.1. Turning CSV files into SAX events (SAX2)
custom data structures, turning into: 4.4.4. Turning SAX Events into Custom Data Structures (SAX2)
data structures, turning into: 4.4. Turning SAX Events into Data Structures (SAX2)
DOM trees, turning into: 3.5.1.1. Turning DOM trees into SAX events (SAX2)
objects, turning into: 3.5.2.2. Turning objects into SAX events (SAX2)
pipelines: 4.5. XML Pipelines (SAX2)
XMLFilterImpl class and: 4.5.1. The XMLFilterImpl Class (SAX2)
Events module: 6.3.4.1. Events (Java & XML)
eventsSorted, boolean member variable: 11.3.1. A Shared Handler (Java & XML)
examples
databases, accessing with extension elements: 8.3.2. Accessing a Database with an Extension Element (XSLT)
document( ) function, using: 7.2. The document( ) Function (XSLT)
extensions, writing in various scripting languages: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
JPEG files, generating: 8.3.1. Generating JPEG Files from XML Content (XSLT)
multiple output files, generating: 8.1.1.1. Example: Generating multiple output files (XSLT)
postal addresses
grouping: 6.2.1. Our First Attempt (XSLT)
sorting: 6.1.1. Our First Example (XSLT)
6.1.3. Another Example (XSLT)
"Hello World" sample document, converting into various formats: 2. The Obligatory Hello World Example (XSLT)
recursion, using: 4.6. Using Recursion to Do Most Anything (XSLT)
stylesheet emulating for loop: 4.7. A Stylesheet That Emulates a for Loop (XSLT)
stylesheet-generating stylesheet with for loop emulation: 4.8. A Stylesheet That Generates a Stylesheet That Emulates a for Loop (XSLT)
trigonometric functions, writing extension for: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
Examplotron schema language: A.3.5. Examplotron (XML Schema)
1.1. The Benefits of XML (XML in a Nutshell)
sample application: A.9. Examplotron (XML Schema)
Excel spreadsheets, converting to XML: 5.4. Drivers for Non-XML Sources (Perl & XML)
exception handling: 2.5. Exception Handling (SAX2)
classes: 2.5.1. SAX2 Exception Classes (SAX2)
ExceptionCodes (DOM): 24.2. Object Reference (XML in a Nutshell)
exceptions: 25.1. The org.xml.sax Package (XML in a Nutshell)
1.2.2. XML Document Rules (XSLT)
exceptions, catching: 4.1.2.2. Namespaces (Java & XML)
exclude-result-prefixes: 10.3.4. WAP and XML (Java & XML)
Exolab FTP site: 15.2.1. Installation (Java & XML)
expanded deployment descriptor: 6.3.4. Deployment (Java and XSLT)
expandEntities flag: 8.1.2. The EntityRef Class (Java & XML)
expat parser and support for character encodings: 26. Character Sets (XML in a Nutshell)
Expat parser library: 3.2. XML::Parser (Perl & XML)
using parsers and: 3.4. Putting Parsers to Work (Perl & XML)
expression operators: B.4.3. Expression Operators (XSLT)
expressions, XPath: 3. XPath: A Syntax for Describing Needles and Haystacks (XSLT)
EXSLT project: 8.3.2. Accessing a Database with an Extension Element (XSLT)
Extended Backus-Naur Form (EBNF) grammar: 20.5. XML Document Grammar (XML in a Nutshell)
Extended Binary Coded Decimal Interchange Code (EBCDIC) encodings: 3.1.2.2. Providing entity text (SAX2)
Extended Interfaces (DOM Core): 18.2. Structure of the DOM Core (XML in a Nutshell)
extended links: 10.4. Extended Links (XML in a Nutshell)
arc elements: 10.4.2. Arcs (XML in a Nutshell)
local resources: 10.4.3. Local Resources (XML in a Nutshell)
locator elements: 10.4.1. Locators (XML in a Nutshell)
title elements: 10.4.4. Title Elements (XML in a Nutshell)
XPointers and: 11.2. XPointers in Links (XML in a Nutshell)
Extensible Linking Language (XLink): 0.. What This Book Covers (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
Extensible Markup Language (see XML)
1.1. Why Use Perl with XML? (Perl & XML)
1. The Simple API for XML (SAX2)
1.2. XML Basics (XSLT)
extensible schemas: 13. Creating Extensible Schemas (XML Schema)
Extensible Stylesheet Language (see XSL)
Extensible Stylesheet Language for Transformations (see XSLT)
Extensible Stylesheet Language Transformations (see XSLT)
6.6. Transformation and Presentation (XML in a Nutshell)
Extensible Stylesheet Language (XSL): 1.4. The Evolution of XML (XML in a Nutshell)
extension, derivation by (see derivation methods, by extension)
extension elements: 8.1. Extension Elements, Extension Functions, and Fallback Processing (XSLT)
8.1. Extension Elements, Extension Functions, and Fallback Processing (XSLT)
(see also extensions for XSLT)
databases, accessing with: 8.3.2. Accessing a Database with an Extension Element (XSLT)
defining for processors: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element (XSLT)
multiple XSLT processors and: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
extension functions: 5.2.3.5. Solution #4: Use an extension function (XSLT)
8.1. Extension Elements, Extension Functions, and Fallback Processing (XSLT)
8.1.2. Extension Functions (XSLT)
8.1.2. Extension Functions (XSLT)
(see also extensions for XSLT)
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element (XSLT)
JPEG files and: 8.3.1. Generating JPEG Files from XML Content (XSLT)
writing in various scripting languages: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
extensions directory, adding to when installing SAX 2.0: 1.7. Installing a SAX2 Parser (SAX2)
extensions for XSLT: 8. Extending XSLT (XSLT)
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element (XSLT)
extensions, schema: B.3.3. Schema Extensions: Error Messages (XML Schema)
extent attributes (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
external declarations
including: 16.8.1.1. Including external declarations (XML in a Nutshell)
modifying: 16.8.1.2. Modifying external declarations (XML in a Nutshell)
external DTD subsets: 3.1.3. Internal DTD Subsets (XML in a Nutshell)
general entity references and: 5.10. Character References (XML in a Nutshell)
parameter entity references and: 3.7.1. Parameter Entity Syntax (XML in a Nutshell)
text declarations and: 5.3. Text Declarations (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
20.4.1. Well-Formedness Constraints (XML in a Nutshell)
external entity resolution: 5.3. External Entity Resolution (Perl & XML)
external general entities: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
parsed: 3.5. External Parsed General Entities (XML in a Nutshell)
attribute values and: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
no recursion allowed: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
SAX core feature: 25.3. SAX Features and Properties (XML in a Nutshell)
text declarations and: 5.3. Text Declarations (XML in a Nutshell)
unparsed: 3.6. External Unparsed Entities and Notations (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
declaring notations: 20.4.2. Validity Constraints (XML in a Nutshell)
external-parameter-entities: 4.1.2.3. Interning and entities (Java & XML)
external parameter entity references: 3.7.3. External DTD Subsets (XML in a Nutshell)
SAX core feature: 25.3. SAX Features and Properties (XML in a Nutshell)
external parsed entities, using instead of xs:include and xs:reference: 8.3.1. External Parsed Entities (XML Schema)
external subsets: 2.5. Entities (Perl & XML)
4.2. The LexicalHandler Interface (SAX2)
externalEntityDecl( ) callback function: 4.3.1. The DeclHandler Interface (SAX2)
extractContents( ): 6.3.3. Range (Java & XML)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

facet elements: 21.2. Schema Elements (XML in a Nutshell)
enumeration: 16.6.2.3. Enumerations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
fractionDigits: 16.6.2.4.2. Length and precision (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
length: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
maxExclusive: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
maxInclusive: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
maxLength: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
minExclusive: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
minInclusive: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
minLength: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
pattern: 16.6.2.5. Enforcing format (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
totalDigits: 16.6.2.4.2. Length and precision (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
whiteSpace: 16.6.2.1. Handling whitespace (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
facets: 5.1.1. Facets (XML Schema)
16.6.2. Facets (XML in a Nutshell)
fixed attribute and: 5.1.2.5. Fixed facets (XML Schema)
multiple restrictions and: 5.1.2. Multiple Restrictions and Fixed Attribute (XML Schema)
non-whitespace-collapsed strings: 5.1.1.2. Other strings (XML Schema)
restricting lexical space of simple datatypes: 6. Using Regular Expressions to Specify Simple Datatypes (XML Schema)
whitespace-collapsed strings: 5.1.1.1. Whitespace collapsed strings (XML Schema)
facets in element type descriptions: 2.11. Schemas (Perl & XML)
factories, JDOM: 8.2. JDOM and Factories (Java & XML)
creating: 8.2.1. Creating a Factory (Java & XML)
factory objects: 5.7. XML::SAX: The Second Generation (Perl & XML)
7.1. DOM and Perl (Perl & XML)
FactoryConfigurationError class: 9.1.1.3. Other exceptions and errors (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
A.3.1.3. FactoryConfigurationError (Java & XML)
fallback processing: 8.1.3. Fallback Processing (XSLT)
false( ) function: C. XSLT and XPath Function Reference (XSLT)
false( ) (XPath): 9.7.3. Boolean Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
fatal errors: 3.4.3. Fatal Errors (Java & XML)
handling: 1.2.1. Stream-Based Processing (SAX2)
fatal_error( ) exception handler: 5.7.2.4. Error event handlers and catching exceptions (Perl & XML)
fatalError( ) method: 2.5.2. ErrorHandler Interface (SAX2)
fault listeners: 12.3.2.2. Deployment descriptors (Java & XML)
fault.getString( ): 12.4.2. Better Error Handling (Java & XML)
feature flags: 2.4.1. SAX2 Feature Flags (SAX2)
namespaces: 2.6.3. Namespace Feature Flags (SAX2)
XMLReader class: 3.3.2. XMLReader Feature Flags (SAX2)
features: 4.1.1. Setting Properties and Features (Java & XML)
external-general-entities: 4.1.2.3. Interning and entities (Java & XML)
external-parameter-entities: 4.1.2.3. Interning and entities (Java & XML)
string-interning: 4.1.2.3. Interning and entities (Java & XML)
validation: 4.1.2.1. Validation (Java & XML)
feedback panel (tutorials): 9.3.4. Feedback Panel (XSLT)
Feynman, Richard: 9.1. The Tree Structure of an XML Document (XML in a Nutshell)
9.2.2. Child Element Location Steps (XML in a Nutshell)
9.4. Predicates (XML in a Nutshell)
9.5. Unabbreviated Location Paths (XML in a Nutshell)
9.7.1. Node-Set Functions (XML in a Nutshell)
File class: 5.3. Input and Output (Java and XSLT)
file path specification, parsing documents: 3.2.2. Parsing the Document (Java & XML)
file: (scheme): 2.4.1. SAX2 Feature Flags (SAX2)
filename extensions, XSLT stylesheets: 2.3. Another XSLT Example, Using XHTML (Java and XSLT)
filenames (platform-specific), converting to system IDs: 5.3.1. System Identifiers, Files, and URLs (Java and XSLT)
filename attribute, document( ) function and: 7.2. The document( ) Function (XSLT)
filenames: 3.1.3. Filenames Versus URIs (SAX2)
Filter deployment descriptor: 8.4.2. XSLT Transformation Filter (Java and XSLT)
FilterChain class: 8.4.1. Filter Overview (Java and XSLT)
Filter interface: 8.4.1. Filter Overview (Java and XSLT)
filters: 4.3.1. XMLFilters (Java & XML)
drawbacks: 8.4.3. Closing Thoughts on Filters (Java and XSLT)
event-based parsers and: 17.1.2. Treating XML as Events (XML in a Nutshell)
for stream processing: 4.4. Stream Applications (Perl & XML)
SAX: 19.3. Filters (XML in a Nutshell)
uses: 8.4.1. Filter Overview (Java and XSLT)
XSLT transformation: 8.4.2. XSLT Transformation Filter (Java and XSLT)
final attribute: 16.9.2. The Final Attribute (XML in a Nutshell)
complex type definitions and: 12.2.3.2. Final complex types (XML Schema)
extensible schemas and: 13.1.2. final and fixed Attributes (XML Schema)
simple type definitions and: 12.2.4. Simple Types (XML Schema)
substitution groups and: 12.2.2.2. Final elements (XML Schema)
finalDefault attribute
simple type definitions and: 12.2.4. Simple Types (XML Schema)
substitution groups and: 12.2.2.2. Final elements (XML Schema)
findnodes( ) method of Node class: 7.4. XML::LibXML (Perl & XML)
finite state machines (FSM): 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
firewalls
open vs. closed: A.1.2. Firewalls Against Diversity (XML Schema)
validating documents and: 1.1.1. Validation (XML Schema)
first-child pseudoclass: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
first-letter pseudoelement: 12.4.5. Pseudoelement Selectors (XML in a Nutshell)
first-line pseudoelement: 12.4.5. Pseudoelement Selectors (XML in a Nutshell)
firstChild attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
firstChild property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
#FIXED attribute: 4.3.1. The DeclHandler Interface (SAX2)
fixed attribute: 16.9.3. Setting fixed Facets (XML in a Nutshell)
controlling derivation of simple types: 12.2.4. Simple Types (XML Schema)
extensible schemas and: 13.1.2. final and fixed Attributes (XML Schema)
included in facets: 5.1.2.5. Fixed facets (XML Schema)
#FIXED default declaration: 3.3.2. Attribute Defaults (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
explicit value must match default value: 20.4.2. Validity Constraints (XML in a Nutshell)
flags: 2.4.1. SAX2 Feature Flags (SAX2)
namespaces: 2.6.3. Namespace Feature Flags (SAX2)
XMLReader class: 3.3.2. XMLReader Feature Flags (SAX2)
flat file text formats: 3.5.2.1. Turning CSV files into SAX events (SAX2)
flat schema design: 2.2. Our First Schema (XML Schema)
2.3.3. Flat Design, Global Components (XML Schema)
creating readable schemas: 14.1.3. When It's Similar, Show It (XML Schema)
recursive schema and: 3.2.1. Depth Versus Modularity? (XML Schema)
flexible schemas, creating: 13. Creating Extensible Schemas (XML Schema)
float datatypes: 4.4.2. Float Datatypes (XML Schema)
regular expressions and: 6.5.2. Numeric and Float Types (XML Schema)
floor( ) function: C. XSLT and XPath Function Reference (XSLT)
floor( ) (XPath): 9.7.4. Number Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
flow child element (XSL-FO): 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
%Flow.mix; entity reference: 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
flushAll( ): 5.4.2. A Stylesheet Cache (Java and XSLT)
flushing data: 8.4. Optimized Tree Processing (Perl & XML)
fo:block-container element: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
fo:block element: 13.1. XSL Formatting Objects (XML in a Nutshell)
13.3.1. Flowing Content into the Pages (XML in a Nutshell)
focus pseudoclass: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
fo:flow child element: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
fo:layout-master-set element: 13.2. The Structure of an XSL-FO Document (XML in a Nutshell)
13.3. Laying Out the Master Pages (XML in a Nutshell)
fo:list-block element: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
following axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
following-sibling axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
font-family property (CSS): 12.7. Font Properties (XML in a Nutshell)
font properties (CSS): 12.7. Font Properties (XML in a Nutshell)
font-size property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
font-stretch property (CSS): 12.7. Font Properties (XML in a Nutshell)
font-style property (CSS): 12.7. Font Properties (XML in a Nutshell)
font-variant property (CSS): 12.7. Font Properties (XML in a Nutshell)
font-weight property (CSS): 12.7. Font Properties (XML in a Nutshell)
Foobar Public Library: 14.1. The Foobar Public Library (Java & XML)
FOP (Apache XML Project formatting program): 13.3.2. Generating the Finished Document (XML in a Nutshell)
FOP tool: 9.3.7. PDF Files (XSLT)
9.5.6. Generating the PDF Files (XSLT)
fo:page-sequence element: 13.2. The Structure of an XSL-FO Document (XML in a Nutshell)
flowing content into pages: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
for-each construct: 2.3.3.2. Looping (Java & XML)
for loop, stylesheet emulating (sample program): 4.7. A Stylesheet That Emulates a for Loop (XSLT)
fo:region-after child element: 13.3. Laying Out the Master Pages (XML in a Nutshell)
fo:region-before child element: 13.3. Laying Out the Master Pages (XML in a Nutshell)
fo:region-body child element: 13.3. Laying Out the Master Pages (XML in a Nutshell)
fo:region-end child element: 13.3. Laying Out the Master Pages (XML in a Nutshell)
fo:region-start child element: 13.3. Laying Out the Master Pages (XML in a Nutshell)
Formal Public Identifiers (FPIs): 3.4. The EntityResolver Interface (SAX2)
SGML: 4.3.1. The DeclHandler Interface (SAX2)
format attribute, WML: 10.3.1. A WML Example (Java and XSLT)
format-number( ) function: 7.2.1.3. Using format-number( ) to control output (XSLT)
C. XSLT and XPath Function Reference (XSLT)
format-number( ) (XSLT): 9.7.2. String Functions (XML in a Nutshell)
23.2. XSLT Elements (XML in a Nutshell)
23.3. XSLT Functions (XML in a Nutshell)
formatting objects (see XSL-FO)
13. XSL Formatting Objects (XSL-FO) (XML in a Nutshell)
Formatting Objects to PDF tool (see FOP tool)
FormattingNodeFilter class: 6.3.2.1. NodeIterator (Java & XML)
forms module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
fo:root element: 13.2. The Structure of an XSL-FO Document (XML in a Nutshell)
forum.css: 7.2.1. XHTML Prototypes (Java and XSLT)
ForumServlet: 7.4. Servlet Implementation (Java and XSLT)
ForumServlet.java: 7.4. Servlet Implementation (Java and XSLT)
forward-only event streams: 1.3. Why Not to Choose SAX? (SAX2)
forward slash (/): 2.1.1.2. Elements (Java & XML)
fo:simple-page-master element: 13.3. Laying Out the Master Pages (XML in a Nutshell)
fo:table-and-caption element: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
fo:table element: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
FPIs (Formal Public Identifiers): 3.4. The EntityResolver Interface (SAX2)
fractionDigits facet element: 16.6.2.4.2. Length and precision (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
fragment identifiers: 11.1. XPointers on URLs (XML in a Nutshell)
frames module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
frameset DTD (XHTML): 7.1.2. Three DTDs for XHTML (XML in a Nutshell)
free-form XML: 2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
from attribute (XLink): 10.4.2. Arcs (XML in a Nutshell)
FSM (finite state machines): 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
ftp: (scheme): 2.4.1. SAX2 Feature Flags (SAX2)
function-available( ) function: C. XSLT and XPath Function Reference (XSLT)
function-available( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
functional programming languages: 1.1. The Design of XSLT (XSLT)
functions
in predicates: 3.2.7.2. Functions in predicates (XSLT)
reference guide listing: C. XSLT and XPath Function Reference (XSLT)
set of (context): 3.2.1. The Context (XSLT)
B.6. The XPath Context (XSLT)
XPath: 9.7. XPath Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
XSLT: 23.3. XSLT Functions (XML in a Nutshell)
functions attribute: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
Fundamental Interfaces (DOM Core): 18.2. Structure of the DOM Core (XML in a Nutshell)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

Galazka, Wojciech: 5.8. Converting Between Character Sets (XML in a Nutshell)
GCA (Graphic Communications Association): 2.1. A Brief History of XML (Perl & XML)
GCC Java (GCJ): 1.6.1. Ælfred2 (SAX2)
GCJ (GNU General Public License): 1.6.1. Ælfred2 (SAX2)
general entities: 2.5. Entities (Perl & XML)
external parsed: 3.5. External Parsed General Entities (XML in a Nutshell)
attribute values and: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
no recursion allowed: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
text declarations and: 5.3. Text Declarations (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
external unparsed: 3.6. External Unparsed Entities and Notations (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
declaring notations: 20.4.2. Validity Constraints (XML in a Nutshell)
internal: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
general entity declarations: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
declaring: 20.4.2. Validity Constraints (XML in a Nutshell)
general entity references: 3.4. General Entity Declarations (XML in a Nutshell)
(see also entity references)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
General Punctuation Unicode block: 26.3.17. General Punctuation (XML in a Nutshell)
Generalized Markup Language (GML): 2.1. A Brief History of XML (Perl & XML)
generate-id( ) function: 5.3. Generating Links in Unstructured Documents (XSLT)
C. XSLT and XPath Function Reference (XSLT)
grouping elements/nodes and: 6.2. Grouping Nodes (XSLT)
generate-id( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
generateRSSContentMethod( ): 14.3.2. Creating an XML RSS Document (Java & XML)
generating
JPEG files: 8.3.1. Generating JPEG Files from XML Content (XSLT)
9.5.7. Generating the JPEG Files (XSLT)
links (see links, creating)
main menu panel: 9.5.3. Generating the Main Menu Panel (XSLT)
output files: 8.1.1.1. Example: Generating multiple output files (XSLT)
panels: 9.5.5. Generating the Individual Panels (XSLT)
PDF files: 9.5.6. Generating the PDF Files (XSLT)
section indexes: 9.5.4. Generating the Section Indexes (XSLT)
zip files: 9.5.8. Generating the Zip File (XSLT)
generic coding in markup language: 2.1. A Brief History of XML (Perl & XML)
Geometric Shapes Unicode block: 26.3.24. Geometric Shapes (XML in a Nutshell)
get*( ) method: 3.3. Configuring XMLReader Behavior (SAX2)
GET requests: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
getAttribute( ): 9.3.2.1. Working with the factory (Java & XML)
getAttribute( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
getAttribute method for Element node: 7.2.8.2. Methods (Perl & XML)
getAttributeNode( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
getAttributeNode method for Element node: 7.2.8.2. Methods (Perl & XML)
getAttributeNodeNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
getAttributeNodeNS method for Element node: 7.2.8.2. Methods (Perl & XML)
getAttributeNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
getAttributeNS method for Element node: 7.2.8.2. Methods (Perl & XML)
getAttributes( ): 5.2.3.3. Working with nodes (Java & XML)
getAttributeValue( ): 7.3.4. Backtracking (Java & XML)
getBaseURI( ) function: 5.1.1. The AttributesImpl Class (SAX2)
getBody( ): 13.4.1. A Messaging Service (Java & XML)
getCause( ) method: 2.5.1. SAX2 Exception Classes (SAX2)
getChild( ): 7.2.3. Creating XML with JDOM (Java & XML)
getChildNodes( ): 5.2.3.3. Working with nodes (Java & XML)
getChildNodes( ) method: 3.5. XML::LibXML (Perl & XML)
7.3. XML::DOM (Perl & XML)
getChildren( ): 7.3.2. Loading XML (Java & XML)
getColumnNumber( ): 3.3.1. The Document Locator (Java & XML)
getColumnNumber( ) function: 4.1.2. The Locator Interface (SAX2)
getContent( ): 7.3.4. Backtracking (Java & XML)
7.3.4. Backtracking (Java & XML)
getDetailEntries( ): 12.4.2. Better Error Handling (Java & XML)
getDocument( ): 5.2.2. DOM Parser Output (Java & XML)
getDocumentElement( ): 5.2.3.3. Working with nodes (Java & XML)
getDTDHandler( ) function: 3.1.1. The XMLReader Interface (SAX2)
getElementById( ) (DOM): 18.4.2.1. Document (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
getElementById method: 7.2.1.2. Methods (Perl & XML)
getElementsByTagName( ) (DOM): 18.4.2.1. Document (XML in a Nutshell)
Document interface: 24.2. Object Reference (XML in a Nutshell)
Element interface: 24.2. Object Reference (XML in a Nutshell)
getElementsByTagName method: 7.2.1.2. Methods (Perl & XML)
7.2.8.2. Methods (Perl & XML)
7.3. XML::DOM (Perl & XML)
getElementsByTagNameNS( ): 6.2. Namespaces (Java & XML)
getElementsByTagNameNS( ) (DOM): 18.4.2.1. Document (XML in a Nutshell)
Document interface: 24.2. Object Reference (XML in a Nutshell)
Element interface: 24.2. Object Reference (XML in a Nutshell)
getElementsByTagNameNS method: 7.2.1.2. Methods (Perl & XML)
for Element node: 7.2.8.2. Methods (Perl & XML)
getEntityResolver( ) function: 3.1.1. The XMLReader Interface (SAX2)
getErrorHandler( ) function: 3.1.1. The XMLReader Interface (SAX2)
getException( ): 3.3.2. The Beginning and the End of a Document (Java & XML)
getException( ) function: 2.5.1. SAX2 Exception Classes (SAX2)
getException( ) (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
getFeature( ): 4.1.1. Setting Properties and Features (Java & XML)
9.3.1.2. Changes in SAX classes (Java & XML)
getFeature( ) function: 3.1.1. The XMLReader Interface (SAX2)
namespace feature flags: 2.6.3. Namespace Feature Flags (SAX2)
getFeature( ) (SAX): 19.2. SAX Features and Properties (XML in a Nutshell)
getFirstChild( ): 6.1.2. Modifying a DOM Tree (Java & XML)
getIndex( ) function: 2.3.3.1. Attribute lookup by name (SAX2)
getInstance( ): 11.4.2. Shared Instances (Java & XML)
getItem( ): 15.2.2. Source Generation (Java & XML)
getLength( ) function: 2.3.3.2. Attribute lookup by index (SAX2)
getLineNumber( ): 3.3.1. The Document Locator (Java & XML)
getLineNumber( ) function: 4.1.2. The Locator Interface (SAX2)
getListOfEvents( ): 11.3.1. A Shared Handler (Java & XML)
getLocalName( ) function, naming attributes: 2.6.2.2. Attribute naming (SAX2)
getLocalName(int index) method: 3.3.5. Element Callbacks (Java & XML)
getNamedItem class for Node class (DOM): 7.2.6.2. Methods (Perl & XML)
getNamedItem( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
getNamedItemNS class for Node class (DOM): 7.2.6.2. Methods (Perl & XML)
getNamedItemNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
getNamespace( ), JDOM: 8.1.3. The Namespace Class (Java & XML)
getNamespaceURI( ): 6.2. Namespaces (Java & XML)
getNodeName( ): 5.2.3.3. Working with nodes (Java & XML)
getNodeType( ): 5.2.3.3. Working with nodes (Java & XML)
getNodeValue( ): 5.4.2. Polymorphism and the Node Interface (Java & XML)
textual data, printing: 5.2.3.3. Working with nodes (Java & XML)
getNodeValue( ) method: 7.3. XML::DOM (Perl & XML)
getOutputProperties( ): 5.4.1. Templates API (Java and XSLT)
getOutputStream( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
8.4.2. XSLT Transformation Filter (Java and XSLT)
ServletResponse interface: 8.4.1. Filter Overview (Java and XSLT)
ServletResponseWrapper class: 8.4.1. Filter Overview (Java and XSLT)
getParameter( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
getParent( ): 4.3.1. XMLFilters (Java & XML)
getPersonalData( ): 6.3.3. Source Code (Java and XSLT)
getPrefix( ): 6.2. Namespaces (Java & XML)
6.2. Namespaces (Java & XML)
getPrefix( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
getProperty( ): 4.1.1. Setting Properties and Features (Java & XML)
7.2.3. Creating XML with JDOM (Java & XML)
9.3.1.2. Changes in SAX classes (Java & XML)
getProperty( ) function: 3.1.1. The XMLReader Interface (SAX2)
getProperty( ) (SAX): 19.2. SAX Features and Properties (XML in a Nutshell)
getPublicId( ) function: 4.1.2. The Locator Interface (SAX2)
getQName( ) function, naming attributes: 2.6.2.2. Attribute naming (SAX2)
getResource( ): 6.2.4. Servlet API Highlights (Java and XSLT)
getResourceAsStream( ): 6.2.4. Servlet API Highlights (Java and XSLT)
getRootElement( ): 7.3.2. Loading XML (Java & XML)
GetRSSChannelServlet.java: 14.3.2. Creating an XML RSS Document (Java & XML)
getServletInfo( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
getSystemId( ) function: 4.1.2. The Locator Interface (SAX2)
getText( ): 7.3.2. Loading XML (Java & XML)
getTextTrim( ): 7.3.2. Loading XML (Java & XML)
getTodaysShows( ): 4.1.2. Servlets as CGI Replacements (Java and XSLT)
getType( ) function: 2.3.3.1. Attribute lookup by name (SAX2)
getURI( ) function, naming attributes: 2.6.2.2. Attribute naming (SAX2)
getURI(int index) method: 3.3.5. Element Callbacks (Java & XML)
getValue( ) function: 2.3.3.1. Attribute lookup by name (SAX2)
getWriter( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
8.4.2. XSLT Transformation Filter (Java and XSLT)
getXMLReader( ): 9.3.1.1. The road to SAX 2.0 (Java & XML)
getXPath( ): 8.3.3. Providing Support for XPath (Java & XML)
global attributes: 2.3.3. Flat Design, Global Components (XML Schema)
13.1.1.2. Attributes (XML Schema)
defining: 13.1.1.2. Attributes (XML Schema)
in <Emphasis>Namespaces in XML<Default Para Font>: 10.3. To Qualify Or Not to Qualify? (XML Schema)
10.4. Disruptive Attributes (XML Schema)
limitations of: 6.6. Back to Our Library (XML Schema)
global definitions of complex datatypes: 7.2.2. Named Versus Anonymous Types (XML Schema)
creating simple content models: 7.3.1. Creation of Simple Content Models (XML Schema)
xs:redefine element and: 8.2. Schema Inclusion with Redefinition (XML Schema)
13.1.1.1. Elements (XML Schema)
13.1.1.1. Elements (XML Schema)
global elements: 2.3.3. Flat Design, Global Components (XML Schema)
13.1.1.1. Elements (XML Schema)
defining: 13.1.1.1. Elements (XML Schema)
13.1.1.1. Elements (XML Schema)
documenting schemas: 14.1.2. Think Globally (XML Schema)
substitution groups and: 12.1. Substitution Groups (XML Schema)
12.1.2.1. Using substitution groups (XML Schema)
global. naming convention: 7.2.3. XSLT Stylesheets (Java and XSLT)
global parameters: 4.4.3. Global Parameters (XSLT)
global properties list, Ant buildfiles: 3.6.2. Stylesheet Functionality (Java and XSLT)
global variables: 3.2.1. <xsl:variable> (Java and XSLT)
global variables, initializing: 9.5.2. Initializing Global Variables (XSLT)
globally defining components: 2.2. Our First Schema (XML Schema)
13.1.1. Global Components (XML Schema)
glossary: Glossary (XML Schema)
Glossary (XSLT)
Glue: 13.4.4. WSDL (Java & XML)
glyph areas (XSL-FO): 13.1. XSL Formatting Objects (XML in a Nutshell)
GML (Generalized Markup Language): 2.1. A Brief History of XML (Perl & XML)
GNOME project: 3.5. XML::LibXML (Perl & XML)
GNU Classpath Extensions project: 1.6.1. Ælfred2 (SAX2)
GNU General Public License (GCJ): 1.6.1. Ælfred2 (SAX2)
GNU pipeline framework: 4.5. XML Pipelines (SAX2)
GNUJAXP: 4.4.1. SAX-to-DOM Consumers (SAX2)
gnujaxp.jar file: 1.6.1. Ælfred2 (SAX2)
XMLWriter and: 2.2.3. XMLWriter: an Event Consumer (SAX2)
gnu.xml.pipeline framework: 4.5.4. The gnu.xml.pipeline Framework (SAX2)
gnu.xml.pipeline.CallFilter class: 6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
Goldfarb, Charles: 2.1. A Brief History of XML (Perl & XML)
1.2.1. XML's Heritage (XSLT)
1.4. The Evolution of XML (XML in a Nutshell)
6.1. SGML's Legacy (XML in a Nutshell)
Goodman, Danny: 24.2. Object Reference (XML in a Nutshell)
grammar-based XML Schema languages: A.2.2. Grammar-Based XML Schema Languages (XML Schema)
grammar for XML documents: 20.5. XML Document Grammar (XML in a Nutshell)
Graphic Communications Association (GCA): 2.1. A Brief History of XML (Perl & XML)
greater-than sign (>), CSS child element selector: 12.4.2. Matching Descendants, Children, and Siblings (XML in a Nutshell)
greater than operator (>): 4.2.1. The <xsl:if> Element (XSLT)
using (>) entity instead of: 4.2.1. The <xsl:if> Element (XSLT)
greater-than-or-equal operator (>=): 4.2.1. The <xsl:if> Element (XSLT)
4.2.1. The <xsl:if> Element (XSLT)
Greek Extended Unicode block: 26.3.16. Greek Extended (XML in a Nutshell)
Greek Unicode block: 26.3.6. Greek and Coptic (XML in a Nutshell)
greeting elements: 2.3.4. Walking Through Our Example (XSLT)
<xsl:template> for: 2.4.4. The <xsl:template> for <greeting> Elements (XSLT)
grep program working with XML::PYX: 4.5. XML::PYX (Perl & XML)
group character ( ) (parentheses): 6.4.4. Oring and Grouping (XML Schema)
grouping: 6. Sorting and Grouping Elements (XSLT)
(see also sorting)
elements/nodes: 6. Sorting and Grouping Elements (XSLT)
using <xsl:key> element: 6.2.4. The <xsl:key> Approach (XSLT)
using <xsl:variable> element: 6.2.3. Grouping with <xsl:variable> (XSLT)
XML documents: 7.4.3. Grouping Across Multiple Documents (XSLT)
groups
attribute (see attribute groups)
element (see element groups)
guided editing applications: 1.1.5. Guided Editing (XML Schema)
gun.xml.dom.Consumer class: 4.4.1. SAX-to-DOM Consumers (SAX2)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

Hampton, Kip: 5.7. XML::SAX: The Second Generation (Perl & XML)
Han Chinese characters: 2.6. Unicode, Character Sets, and Encodings (Perl & XML)
HandlerBase class: 9.2.1. Starting with SAX (Java & XML)
9.2.1. Starting with SAX (Java & XML)
A.1.1.8. HandlerBase (Java & XML)
HandlerBase interface: A.1.8. The HandlerBase Interface (SAX2)
handlers: 3.3. Content Handlers (Java & XML)
3.4. Putting Parsers to Work (Perl & XML)
4.2. Events and Handlers (Perl & XML)
2.1. Producers and Consumers (SAX2)
2.2.2. What Are the SAX2 Event Handlers? (SAX2)
base classes for: 5.5. A Handler Base Class (Perl & XML)
ContentHandler callbacks: 2.3.4. Essential ContentHandler Callbacks (SAX2)
DTD: 5.2. DTD Handlers (Perl & XML)
DTDHandler: 4.2.2. Using a DTDHandler (Java & XML)
EntityResolver: 4.2.1. Using an EntityResolver (Java & XML)
errors: 2.4. Producer-Side Validation (SAX2)
diagnostics and: 2.5.3. Errors and Diagnostics (SAX2)
validity (producer-side): 2.4.2. Handling Validity Errors (SAX2)
exception: 2.5. Exception Handling (SAX2)
implementation classes, registering: 3.3. Content Handlers (Java & XML)
instance, adding to: 11.4.2. Shared Instances (Java & XML)
lexical event: 5.7.2.3. Lexical event handlers (Perl & XML)
for SAX: 5.1. SAX Event Handlers (Perl & XML)
5.7.2. SAX2 Handler Interface (Perl & XML)
Scheduler class: 11.3.1. A Shared Handler (Java & XML)
stream parsing styles, using: 3.2.2. Parsing Styles (Perl & XML)
XML-RPC: 11.2.2. Writing the Handler (Java & XML)
11.3.1. A Shared Handler (Java & XML)
XML::Parser module and: 4.6. XML::Parser (Perl & XML)
hasAttribute( ) (DOM), Element interface: 24.2. Object Reference (XML in a Nutshell)
hasAttribute method for Element node: 7.2.8.2. Methods (Perl & XML)
hasAttributeNS( ) (DOM), Element interface: 24.2. Object Reference (XML in a Nutshell)
hasAttributeNS method for Element node: 7.2.8.2. Methods (Perl & XML)
hasAttributes( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
hasAttributes method for Node class (DOM): 7.2.4.2. Methods (Perl & XML)
hasChildNodes( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
hasChildNodes method for Node class (DOM): 7.2.4.2. Methods (Perl & XML)
hasFeature( ) (DOM): 18.5. The DOMImplementation Interface (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
hash mark (#): 5.1.3. A Stylesheet That Uses the id( ) Function (XSLT)
Hashtable class: 7.2.1. Java Properties Files (Java & XML)
head elements: 16.8.3. Substitution Groups (XML in a Nutshell)
head elements of substitution groups: 12.1.2.1. Using substitution groups (XML Schema)
defining, using abstract attribute: 12.1.2.2. Abstract elements (XML Schema)
headers: 2.1.1. XML 1.0 (Java & XML)
Hebrew Unicode block: 26.3.9. Hebrew (XML in a Nutshell)
height property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
Hello World sample document: 2. The Obligatory Hello World Example (XSLT)
converting into VRML file: 2.5.4. The Hello World VRML File (XSLT)
HelloClient.java: 11.2.4. Writing the Client (Java & XML)
HelloHandler class, registration: 11.2.3. Writing the Server (Java & XML)
HelloServer class: 11.2.3. Writing the Server (Java & XML)
helma.xmlrpc.XmlRpcClient class: 11.2.4. Writing the Client (Java & XML)
helma.xmlrpc.XmlRpcServer class: 11.2.2. Writing the Handler (Java & XML)
helper classes: 5.1. Helper Classes (SAX2)
helper modules: 9.2. XML::RSS (Perl & XML)
here( ): 11.7.6. here( ) (XML in a Nutshell)
hexadecimal character entities: 2.5. Entities (Perl & XML)
hexadecimal character references: 5.10. Character References (XML in a Nutshell)
hexadecimal encoding, predefined datatype for: 4.3.3.5. Binary string-encoded datatypes (XML Schema)
hidden elements (CSS display property): 12.5.4. Hidden Elements (XML in a Nutshell)
HIERARCHY_REQUEST_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
high-level APIs: 1.3.3. APIs (Java & XML)
1.1. Types of XML APIs (SAX2)
flexibility of: 1.2.2. Data Structure Flexibility (SAX2)
high surrogate (Java char values): 4.1.3. Internationalization Concerns (SAX2)
Home page WML: 10.3.3.3. WML prototypes and screen shots (Java and XSLT)
Home page XSLT: 10.3.3.5. XSLT stylesheets (Java and XSLT)
HomeJDOM.java: 7.3.3. JDOM XML Production (Java and XSLT)
HomeRenderer.java: 7.4. Servlet Implementation (Java and XSLT)
home.xml: 8.1. XSLT Page Layout Templates (Java and XSLT)
Hook schema language: 1.1. The Benefits of XML (XML in a Nutshell)
HotJava web browser: 1.6.2. Crimson (SAX2)
hover pseudoclass: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
href attribute (Xlink): 10.1. Simple Links (XML in a Nutshell)
href pseudoattribute: 7.2.1.1. The required href and type pseudoattributes (XML in a Nutshell)
12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
HTML
2.1. A Brief History of XML (Perl & XML)
4.0 entity sets: 26.2. HTML4 Entity Sets (XML in a Nutshell)
converting: 2.12. Transformations (Perl & XML)
8.3. XSLT (Perl & XML)
10.3. Converting XML to HTML with XSLT (Perl & XML)
example of a standard document: 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
HTML files, tutorials and: 9.1. About the Toot-O-Matic (XSLT)
9.3. Tutorial Layout (XSLT)
interface for XML data: 14.1.2. Entering the Books (Java & XML)
HTML Tidy tool: 3.5. Other Kinds of SAX2 Event Producers (SAX2)
moving to XHTML from: 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
SGML and: 1.4. The Evolution of XML (XML in a Nutshell)
structure of documents: 6.2. Narrative Document Structures (XML in a Nutshell)
vs. XHTML: 7.1. XHTML (XML in a Nutshell)
well-formed documents and: 3.1. XML Parsers (Perl & XML)
HTTP listener, creating: 11.2.3. Writing the Server (Java & XML)
HTTP protocol: 2.4.1. SAX2 Feature Flags (SAX2)
6.2.1. XML/Internet Versus Older Technologies (SAX2)
messaging and: 6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
HttpServlet class: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
HttpServletRequest interface: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
HttpServletRequestWrapper class: 8.4.1. Filter Overview (Java and XSLT)
HttpServletResponse interface: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
6.1.1. Splash Screen Servlet Example (Java and XSLT)
Hypertext Markup Language (see HTML)
HyperText Markup Language (HTML): 3.5. Other Kinds of SAX2 Event Producers (SAX2)
hypertext module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
hyphen (-) in PYX notation: 4.5. XML::PYX (Perl & XML)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

\i character class: 6.4.3.1. Classical Perl character classes (XML Schema)
i-mode protocol: 10.1. Wireless Technologies (Java and XSLT)
IANA (Internet Assigned Numbers Authority): 5.4. XML-Defined Character Sets (XML in a Nutshell)
7.2.1.1. The required href and type pseudoattributes (XML in a Nutshell)
IANA Internet encoding names: 3.1.2.2. Providing entity text (SAX2)
IBM
and character sets: 5.7. Platform-Dependent Character Sets (XML in a Nutshell)
Bean Scripting Framework and: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
developerWorks web site and: 9. Case Study: The Toot-O-Matic (XSLT)
IBM Alphaworks LotusXSL: 1.4.1.2. LotusXSL (Java and XSLT)
IBM SOAP4J: 12.2.1.1. What about IBM SOAP4J? (Java & XML)
IBM XML4J: 1.3.2. A Parser (Java & XML)
iconv library: 3.9.4.1. iconv and Text::Iconv (Perl & XML)
id( ) module: 8.2. XPath (Perl & XML)
ID attribute: 4.3.1. The DeclHandler Interface (SAX2)
ID attribute type: 3.3.1.5. ID (XML in a Nutshell)
bare name XPointers and: 11.3. Bare Names (XML in a Nutshell)
default for: 20.4.2. Validity Constraints (XML in a Nutshell)
in element type declarations: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
only one per element type: 20.4.2. Validity Constraints (XML in a Nutshell)
values must be unique: 20.4.2. Validity Constraints (XML in a Nutshell)
ID datatype: 5.1.1. The ID, IDREF, and IDREFs Datatypes (XSLT)
limitations of: 5.1.4. Limitations of IDs (XSLT)
id( ) function: C. XSLT and XPath Function Reference (XSLT)
creating links using: 5.1. Generating Links with the id( ) Function (XSLT)
limitations of: 5.1.4. Limitations of IDs (XSLT)
id( ) (XPath): 9.7.1. Node-Set Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
bare name XPointers and: 11.3. Bare Names (XML in a Nutshell)
identification of browsers: 8.3. Identifying the Browser (Java and XSLT)
identifiers: 4.3.3.1. Tokenss (XML Schema)
xs:ID and xs:IDREF types: 9.1. and (XML Schema)
identity transformations: 3.5.2. The Identity Transformation (Java and XSLT)
result file: 3.5.4. The Result File (Java and XSLT)
ideographic glyphs: 3.9.2. Unicode Encodings (Perl & XML)
IDL (Interface Definition Language) and DOM: 18.1.1. DOM Notation (XML in a Nutshell)
24. DOM Reference (XML in a Nutshell)
IDREF attribute: 4.3.1. The DeclHandler Interface (SAX2)
IDREF attribute type: 3.3.1.6. IDREF (XML in a Nutshell)
in element type declarations: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
matching IDs in document: 20.4.2. Validity Constraints (XML in a Nutshell)
IDREF datatype: 5.1.1. The ID, IDREF, and IDREFs Datatypes (XSLT)
IDREFS attribute: 4.3.1. The DeclHandler Interface (SAX2)
IDREFS attribute type: 3.3.1.7. IDREFS (XML in a Nutshell)
in element type declarations: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
IDREFS datatype: 5.1.1. The ID, IDREF, and IDREFs Datatypes (XSLT)
5.2.3. Stylesheets That Use the key( ) Function (XSLT)
key( ) function and: 5.2.3.1. The key( ) function and the IDREFS datatype (XSLT)
replacing: 5.2.3.2. Solution #1: Replace the IDREFS datatype (XSLT)
using recursion to process: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
IE (see Internet Explorer)
IETF (Internet Engineering Task Force): 1.4.1. SAX1 (SAX2)
if/else syntax, XSLT: 3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise> (Java and XSLT)
if-else statement: D.5. How Do I Implement an if-else Statement? (XSLT)
if statement (see <xsl:if> element)
if-then-else statement (see <xsl:choose> element)
iframe module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
ignorable_whitespace( ): 5.7.2.1. Content event handlers (Perl & XML)
ignorableWhitespace( ) callback function: 4.1.1. Other ContentHandler Methods (SAX2)
4.4.2. Pruning Noise Data from a DOM Tree (SAX2)
ignorableWhitespace( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
IGNORE directive: 3.8. Conditional Inclusion (XML in a Nutshell)
IIOP (Internet Inter-ORB Protocol): 11.1.1. What Is RMI? (Java & XML)
il8n (internationalization): 8.6. Internationalization with XSLT (Java and XSLT)
troubleshooting: 8.6.4. I18n Troubleshooting Checklist (Java and XSLT)
XSLT stylesheet design: 8.6.1. XSLT Stylesheet Design (Java and XSLT)
illegal characters (Unicode): 26.1. Character Tables (XML in a Nutshell)
image module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
immutable objects: 6.5.2.2. Tip 2: Immutable objects are thread-safe
implementation attribute (Document interface): 24.2. Object Reference (XML in a Nutshell)
#IMPLIED attribute: 4.3.1. The DeclHandler Interface (SAX2)
5.1.1. The AttributesImpl Class (SAX2)
#IMPLIED default declaration: 3.3.2. Attribute Defaults (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
imported schemas
namespace behavior of: 10.8. Namespace Behavior of Imported Components (XML Schema)
referencing other namespaces: 10.6. Referencing Other Namespaces (XML Schema)
splitting schema components: 13.1.3. Splitting Schema Components (XML Schema)
importNode( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
importNode method: 7.2.1.2. Methods (Perl & XML)
inbound links: 10.5. Linkbases (XML in a Nutshell)
INCLUDE directive: 3.8. Conditional Inclusion (XML in a Nutshell)
included schemas (see schemas, inclusion xs\include element)
indentLevel variable: 5.2.3.2. Launching serialization (Java & XML)
index variable: 4.7.1. Template Design (XSLT)
indexes, looking up attributes witg: 2.3.3.2. Attribute lookup by index (SAX2)
INDEX_SIZE_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
Indian languages, Unicode block for: 26.3.11. Devanagari (XML in a Nutshell)
Infoset (XML): 1.2.1. Stream-Based Processing (SAX2)
1.8. What XML Are We Talking About? (SAX2)
B. SAX2 and the XML Infoset (SAX2)
init( ): 6.3.3. Source Code (Java and XSLT)
stylesheets, location of: 6.3.6. Locating Stylesheets with Initialization Parameters (Java and XSLT)
TemplateServlet.java: 8.1. XSLT Page Layout Templates (Java and XSLT)
initialization parameters
stylesheets, location with: 6.3.6. Locating Stylesheets with Initialization Parameters (Java and XSLT)
initializing
global variables: 9.5.2. Initializing Global Variables (XSLT)
variables: 7.2.1.2. Generating output to initialize a variable (XSLT)
inline areas (XSL-FO): 13.1. XSL Formatting Objects (XML in a Nutshell)
inline elements (CSS display property): 12.5.1. Inline Elements (XML in a Nutshell)
inline-table value (display property): 12.5.5. Table Elements (XML in a Nutshell)
%Inline.extra entity reference: 7.3.2. Mixing Your Applications into XHTML (XML in a Nutshell)
%Inline.mix; entity reference: 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
input/output, XSLT processors: 5.3. Input and Output (Java and XSLT)
JAXP DOM I/O: 5.3.4. JAXP DOM I/O (Java and XSLT)
JAXP I/O design: 5.3.2. JAXP I/O Design (Java and XSLT)
JAXP SAX I/O: 5.3.5. JAXP SAX I/O (Java and XSLT)
JAXP stream I/O: 5.3.3. JAXP Stream I/O (Java and XSLT)
JDOM output, feeding into JAXP: 5.3.6. Feeding JDOM Output into JAXP (Java and XSLT)
system identifiers, files, and URLs: 5.3.1. System Identifiers, Files, and URLs (Java and XSLT)
input markup languages: 6.6. Transformation and Presentation (XML in a Nutshell)
input normalization: B.3. Document Information Item (SAX2)
input, when using XML::RSS module: 9.2.4. Input: User or File (Perl & XML)
InputSource class: 3.2.3. Using an InputSource (Java & XML)
A.1.1.9. InputSource (Java & XML)
3.1.2. The InputSource Class (SAX2)
A.1.9. The InputSource Class (SAX2)
entity text, providing: 3.1.2.2. Providing entity text (SAX2)
EntityResolver interface and: 3.4. The EntityResolver Interface (SAX2)
methods for: 3.1.2.1. Always provide absolute URIs (SAX2)
InputSource class (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
Inputsource.getEncoding( ) function: B.3. Document Information Item (SAX2)
InputStreamReader class, providing entity text: 3.1.2.2. Providing entity text (SAX2)
insertBefore( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
insertBefore method for Node class (DOM): 7.2.4.2. Methods (Perl & XML)
insertData( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
insertData method for CharacterData class (DOM): 7.2.7.2. Methods (Perl & XML)
instance documents: 16.1. Overview (XML in a Nutshell)
abstract attribute and: 16.9.1. Abstract Elements and Types (XML in a Nutshell)
adapting to constraints of xs:all (outside a group): 7.4.1.5.2. Adapting the structure of your document (XML Schema)
associating schemas with: 11.1. Associating Schemas with Instance Documents (XML Schema)
attributes of: 21.4. Instance Document Attributes (XML in a Nutshell)
co-occurrence constraints in: 9.4. Using and As Co-occurrence Constraints (XML Schema)
complex types and: 16.4. Complex Types (XML in a Nutshell)
controlling namespace qualification: 16.3.2. Controlling Qualification (XML in a Nutshell)
creating: 2.1. The Instance Document (XML Schema)
defining complex types in: 11.2.2. Defining Complex Types (XML Schema)
defining simple types in: 11.2.1. Defining Simple Types (XML Schema)
derived types in: 16.8.2.3. Using derived types (XML in a Nutshell)
element declarations and: 16.2.3. Element Declarations (XML in a Nutshell)
empty elements and: 16.5. Empty Elements (XML in a Nutshell)
locally defining components: 3.1. Working From the Structure of the Instance Document (XML Schema)
namespace issues with: 16.1.2. Namespace Issues (XML in a Nutshell)
null values in: 11.3. Defining Nil (Null) Values (XML Schema)
substitution groups and: 16.8.3. Substitution Groups (XML in a Nutshell)
instances (document): 3.7. Document Validation (Perl & XML)
instances, sharing: 11.4.2. Shared Instances (Java & XML)
integrated development environments, problems with: 9.1.1.5. Java IDEs (Java and XSLT)
Interface Definition Language (IDL) and DOM: 18.1.1. DOM Notation (XML in a Nutshell)
24. DOM Reference (XML in a Nutshell)
interface facility, working with multiple products: 1.1. Types of XML APIs (SAX2)
interfaces: 0.6. Acknowledgments (Java & XML)
2.2.1. How Do the Parts Fit Together? (SAX2)
APIs: 1.2.1. Low-Level APIs (Java & XML)
Attr: A.2.1.1. Attr (Java & XML)
AttributeList: A.1.1.1. AttributeList [deprecated] (Java & XML)
Attributes: 3.3.5. Element Callbacks (Java & XML)
A.1.1.2. Attributes (Java & XML)
BoardSummary: 7.3.1. Domain Classes (Java and XSLT)
A. Discussion Forum Code (Java and XSLT)
CDATASection: A.2.1.2. CDATASection (Java & XML)
CharacterData: A.2.1.3. CharacterData (Java & XML)
Comment: A.2.1.4. Comment (Java & XML)
ContentHandler: 3.3. Content Handlers (Java & XML)
1.2.4.1. SAX (Java and XSLT)
A.1.1.3. ContentHandler (Java & XML)
core handlers: 3.3. Content Handlers (Java & XML)
DeclHandler: 4.4.2. DeclHandler (Java & XML)
A.1.2.1. DeclHandler (Java & XML)
Document: 5.2.3.3. Working with nodes (Java & XML)
1.2.4.2. DOM (Java and XSLT)
5.3.4. JAXP DOM I/O (Java and XSLT)
A.2.1.5. Document (Java & XML)
DocumentFragment: A.2.1.6. DocumentFragment (Java & XML)
DocumentHandler: A.1.1.4. DocumentHandler (Java & XML)
DocumentRange: 6.3.3. Range (Java & XML)
DocumentType: A.2.1.7. DocumentType (Java & XML)
DOMImplementation: A.2.1.9. DOMImplementation (Java & XML)
DOMLocator: B. JAXP API Reference (Java and XSLT)
DTDHandler: 4.2.2. Using a DTDHandler (Java & XML)
4.5.2. DTDHandler and Validation (Java & XML)
A.1.1.5. DTDHandler (Java & XML)
Element: A.2.1.10. Element (Java & XML)
Entity: A.2.1.11. Entity (Java & XML)
EntityReference: A.2.1.12. EntityReference (Java & XML)
EntityResolver: 4.2.1. Using an EntityResolver (Java & XML)
A.1.1.6. EntityResolver (Java & XML)
ErrorHandler: 3.4. Error Handlers (Java & XML)
A.1.1.7. ErrorHandler (Java & XML)
2.5.2. ErrorHandler Interface (SAX2)
ErrorListener: 9.3.2.1. Working with the factory (Java & XML)
A.3.2.1. ErrorListener (Java & XML)
Filter: 8.4.1. Filter Overview (Java and XSLT)
handler interfaces: 0.6. Acknowledgments (Java & XML)
3.3. Content Handlers (Java & XML)
HTML for XML data: 14.1.2. Entering the Books (Java & XML)
JDOMFactory: 8.2.1. Creating a Factory (Java & XML)
A.4.3.3. JDOMFactory (Java & XML)
JDOMNode: 8.3.1. JDOMNode (Java & XML)
LexicalHandler: A.1.2.2. LexicalHandler (Java & XML)
Message: 7.3.1. Domain Classes (Java and XSLT)
A. Discussion Forum Code (Java and XSLT)
MessageSummary: 7.3.1. Domain Classes (Java and XSLT)
A. Discussion Forum Code (Java and XSLT)
NameNodeMap: A.2.1.13. NamedNodeMap (Java & XML)
Node: 5.2.3.3. Working with nodes (Java & XML)
A.2.1.14. Node (Java & XML)
NodeFilter: 6.3.2.1. NodeIterator (Java & XML)
NodeIterator: 6.3.2.1. NodeIterator (Java & XML)
NodeList: A.2.1.15. NodeList (Java & XML)
Notation: A.2.1.16. Notation (Java & XML)
org.xml.sax.Parser: 3.2. SAX Readers (Java & XML)
org.xml.sax.XMLReader: 3.2. SAX Readers (Java & XML)
3.2.1. Instantiating a Reader (Java & XML)
Parser: A.1.1.11. Parser (Java & XML)
ProcessingInstruction: A.2.1.17. ProcessingInstruction (Java & XML)
Result: A.3.2.3. Result (Java & XML)
SAXParser: 9.3.1.1. The road to SAX 2.0 (Java & XML)
Source: A.3.2.4. Source (Java & XML)
SourceLocator: A.3.2.5. SourceLocator (Java & XML)
Templates: A.3.2.6. Templates (Java & XML)
TemplatesHandler: B. JAXP API Reference (Java and XSLT)
Text: A.2.1.18. Text (Java & XML)
TreeWalker: 6.3.2.2. TreeWalker (Java & XML)
URIResolver: 9.3.2.1. Working with the factory (Java & XML)
A.3.2.9. URIResolver (Java & XML)
XMLReader: 3.2.2. Parsing the Document (Java & XML)
4.1.1. Setting Properties and Features (Java & XML)
A.1.1.17. XMLReader (Java & XML)
internal DTD subsets: 3.1.3. Internal DTD Subsets (XML in a Nutshell)
general entity references and: 5.10. Character References (XML in a Nutshell)
parameter entity references and: 3.7.1. Parameter Entity Syntax (XML in a Nutshell)
20.4.1. Well-Formedness Constraints (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
internal general entities: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
internal subsets: 2.5. Entities (Perl & XML)
4.2. The LexicalHandler Interface (SAX2)
internalEntityDecl( ) function: 4.3.1. The DeclHandler Interface (SAX2)
internalSubset attribute (DocumentType interface): 24.2. Object Reference (XML in a Nutshell)
internalSubset for DocumentType class (DOM): 7.2.3.1. Properties (Perl & XML)
International Phonetic Alphabet (IPA) Extensions Unicode block: 26.3.3. IPA Extensions (XML in a Nutshell)
International Standards Organization (ISO): 1.4.4. Is SAX2 a "Standard"? (SAX2)
internationalization: 4.1.3. Internationalization Concerns (SAX2)
5. Internationalization (XML in a Nutshell)
Internet: 1.1. Why Use Perl with XML? (Perl & XML)
6.2.1. XML/Internet Versus Older Technologies (SAX2)
Internet Assigned Numbers Authority (IANA): 5.4. XML-Defined Character Sets (XML in a Nutshell)
7.2.1.1. The required href and type pseudoattributes (XML in a Nutshell)
Internet Engineering Task Force (IETF): 1.4.1. SAX1 (SAX2)
Internet Explorer (IE)
displaying XML in web browsers: 7.2.2. Internet Explorer (XML in a Nutshell)
lack of support for XSLT: 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
Internet Inter-ORB Protocol (IIOP): 11.1.1. What Is RMI? (Java & XML)
Internet xml-dev mailing list, developing SAX 1.0: 1.4.1. SAX1 (SAX2)
interoperability: 13.1. Web Services (Java & XML)
between DOM, JDOM: 1.2.4.4. JDOM and DOM interoperability (Java and XSLT)
interpreters: 5.4. Stylesheet Compilation (Java and XSLT)
intersections of character classes: 6.4.3.3. User-defined character classes (XML Schema)
intrinsic events module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
INUSE_ATTRIBUTE_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
invalid XML documents: 3.1. XML Parsers (Perl & XML)
INVALID_ACCESS_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
INVALID_CHARACTER_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
INVALID_MODIFICATION_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
INVALID_STATE_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
invocation in SOAP: 12.1.3. Invocation (Java & XML)
IOException: 5.4.3. DOM Parsers Throwing SAX Exceptions (Java & XML)
IPA (International Phonetic Alphabet) Extensions Unicode block: 26.3.3. IPA Extensions (XML in a Nutshell)
isNamespaceAware( ): 9.2.1.2. Working with the SAXParser class (Java & XML)
ISO/IEC 10646: 8.6.2. Encodings (Java and XSLT)
ISO-8859-12 encoding: 26. Character Sets (XML in a Nutshell)
ISO-8859 character sets: 26.1.2. ISO-8859-1, Latin-1 (XML in a Nutshell)
ISO-3166 (Codes for the Representation of Names of Countries): 5.11.2. Subcodes (XML in a Nutshell)
ISO-639 (Codes for the Representation of Names of Languages): 5.11.1. Language Codes (XML in a Nutshell)
ISO-2022-JP encoding: 5.4. XML-Defined Character Sets (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
ISO 8601 standard and date/time formats: 4.5.1. The Realm of ISO 8601 (XML Schema)
14.2. The W3C XML Schema Annotation Element (XML Schema)
ISO-10646-UCS-4 encoding: 5.4. XML-Defined Character Sets (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
ISO character sets: 5.6. ISO Character Sets (XML in a Nutshell)
ISO DSDL (Document Schema Definition Language): B.2. ISO: DSDL (XML Schema)
ISO (International Standards Organization): 1.4.4. Is SAX2 a "Standard"? (SAX2)
ISO Latin-1 characters: 2.6. Unicode, Character Sets, and Encodings (Perl & XML)
isSameNode( ): 6.4.2. Node Comparisons (Java & XML)
isRequestedSessionIdFromCookie( ): 8.2.2. Session Tracking with Java and XSLT (Java and XSLT)
isSupported( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
isSupported method for Node class (DOM): 7.2.4.2. Methods (Perl & XML)
isValidating( ): 9.2.1.2. Working with the SAXParser class (Java & XML)
item( ) method: 7.3. XML::DOM (Perl & XML)
item class for Node class (DOM): 7.2.6.2. Methods (Perl & XML)
item( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
item method for NodeList class (DOM): 7.2.5.2. Methods (Perl & XML)
items: 14.3.1. Rich Site Summary (Java & XML)
ItemSearcher class: 6.3.2.1. NodeIterator (Java & XML)
itemType attribute: 5.2.1. List Datatypes (XML Schema)
iteration: 1.1. The Design of XSLT (XSLT)
iterators (tree climbers): 8.1. Tree Climbers (Perl & XML)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

Jacl code: 8. Extending XSLT (XSLT)
Jakarta Tomcat (see Tomcat)
James ClarkÕs XP parser, online resources: 11.2.3. Writing the Server (Java & XML)
Japanese characters
national standard character sets for: 26. Character Sets (XML in a Nutshell)
UTF-8/UTF-16 encodings: 5.5.2. UTF-8 (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
jar command, creating WAR files with: 6.2.1. WAR Files (Java and XSLT)
JAR file issues: 9.1.1.1. JAR hell? (Java and XSLT)
Ant, incompatibility with JAXP 1.1: 9.1.2.1. Installing Ant (Java and XSLT)
sealed JAR files: 9.1.1.2. Sealing violations (Java and XSLT)
versioning: 6.3.5. How to Compile, Deploy, and Run (Java and XSLT)
9.1.1.1. JAR hell? (Java and XSLT)
Java
APIs for XML: 1.2.4. Java and XML (Java and XSLT)
Castor: 15.2. Castor (Java & XML)
compatible operating systems: 1.3.1. An Operating System and Java (Java & XML)
datatypes supported in XML-RPC: 11.2.2. Writing the Handler (Java & XML)
DOM and: 5.1.1. Language Bindings (Java & XML)
language bindings: 5.1.1. Language Bindings (Java & XML)
examples/bindings for DOM Core objects: 24.2. Object Reference (XML in a Nutshell)
integrated development environments: 9.1.1.5. Java IDEs (Java and XSLT)
language-specific bindings for: 18.1.1. DOM Notation (XML in a Nutshell)
24. DOM Reference (XML in a Nutshell)
ouput forms: 5.2.3. DOMSerializer (Java & XML)
platforms: 0.3. Software and Versions (Java & XML)
properties files: 7.2.1. Java Properties Files (Java & XML)
XML, conversion to: 7.2.2. Converting to XML (Java & XML)
SerializerTest.java, source files: 5.2.1. Getting a DOM Parser (Java & XML)
servlets: 1.3. Beyond Dynamic Web Pages (Java and XSLT)
4.1.2. Servlets as CGI Replacements (Java and XSLT)
SOAP implementation projects: 12.2. Setting Up (Java & XML)
switch statement, XSLT equivalent to: 3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise> (Java and XSLT)
TrAX and: 23.4. TrAX (XML in a Nutshell)
webpage presentation, and: 1.1.3. The XSLT Solution (Java and XSLT)
XML, and, future developments: 16. Looking Forward (Java & XML)
XSLT, and, program example: 5.1. A Simple Example (Java and XSLT)
XSLT processors, used for: 0. Preface (Java and XSLT)
Java API for XML Parsing (see JAXP)
Java Development Kit character set conversion tool: 5.8. Converting Between Character Sets (XML in a Nutshell)
Java Document Object Model, see JDOM: 0.6. Acknowledgments (Java & XML)
Java2 Enterprise Edition (J2EE): 1.4.5. Sun's Java API for XML Processing (JAXP) (SAX2)
Java in XML messaging: 6.2.2. Roles for Java in XML Messaging (SAX2)
Java parsers: 1.4.1. SAX1 (SAX2)
Java programming language: 1.1. Why Use Perl with XML? (Perl & XML)
5. SAX (Perl & XML)
7.1. DOM and Perl (Perl & XML)
Java programs
converting "Hello World" document into: 2.5.3. The Hello World Java Program (XSLT)
global parameters, setting in: 4.4.3.6. Setting global parameters in a Java program (XSLT)
Java Project X: 1.6.2. Crimson (SAX2)
Java Runtime Environment (JRE): 1.3. Installing Xalan (XSLT)
Javadocs, string-interning: 4.1.2.3. Interning and entities (Java & XML)
java.io.CharArrayReader method for InputSource class: 3.1.2.2. Providing entity text (SAX2)
java.io.InputStream in method for InputStream class: 3.1.2.2. Providing entity text (SAX2)
java.io.IOException class: 3.4. The EntityResolver Interface (SAX2)
java.io.Reader class: 2.2.1. How Do the Parts Fit Together? (SAX2)
3.1. Pull Mode Event Production with XMLReader (SAX2)
InputSource class and: 3.1.2.2. Providing entity text (SAX2)
installing SAX 2.0: 1.7. Installing a SAX2 Parser (SAX2)
java.lang.Character class, using surrogate pairs: 4.1.3. Internationalization Concerns (SAX2)
java.lang.String class, using surrogate pairs: 4.1.3. Internationalization Concerns (SAX2)
java.net.MalformedURLException class: 11.2.4. Writing the Client (Java & XML)
java.util.Map: 5.4.2. A Stylesheet Cache (Java and XSLT)
javax.xml.transform.dom: B. JAXP API Reference (Java and XSLT)
javax.xml.transform.OutputKeys, constants: 5.2.3. The Transformer Class (Java and XSLT)
javax.xml.transform.sax: B. JAXP API Reference (Java and XSLT)
javax.xml.transform.stream: B. JAXP API Reference (Java and XSLT)
javax.xm..transform: B. JAXP API Reference (Java and XSLT)
javaz.xml.parsers: B. JAXP API Reference (Java and XSLT)
JavaScript code: 8. Extending XSLT (XSLT)
caution when generating: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
JavaScript language: 1.4.4. Is SAX2 a "Standard"? (SAX2)
JavaScript programming language: 1.1. Why Use Perl with XML? (Perl & XML)
java.text.SimpleDateFormat class: 11.3.1. A Shared Handler (Java & XML)
java.util.Enumeration class: 11.3.1. A Shared Handler (Java & XML)
JavaXML.explorer-html.xsl: 10.3.3. Browser-Dependent Styling (Java & XML)
JavaXML.fo.xsl: 10.4.1. Creating an XSP Page (Java & XML)
JavaXML.html.xsl: 10.3.3. Browser-Dependent Styling (Java & XML)
JavaXML.wml.xsl: 10.3.4. WAP and XML (Java & XML)
javax.xml.parsers package: A.3.1. Package: javax.xml.parsers (Java & XML)
javax.xml.parsers.SAXParserFactory, using JAXP: 3.2.3. Using JAXP (SAX2)
javax.xml.transform package: A.3.2. Package: javax.xml.transform (Java & XML)
23.4. TrAX (XML in a Nutshell)
javax.xml.transform.dom package: A.3.3. Package: javax.xml.transform.dom (Java & XML)
javax.xml.transform.sax package: A.3.4. Package: javax.xml.transform.sax (Java & XML)
4.5.3. The javax.xml.transform.sax Package (SAX2)
JAXB (Java Architecture for Data Binding): 15.4. JAXB (Java & XML)
JAXP (Java API for XML Parsing): 9. JAXP (Java & XML)
A.3. JAXP 1.1 (Java & XML)
classes: 9.1. API or Abstraction (Java & XML)
compared to other APIs: 9.1. API or Abstraction (Java & XML)
configuration exceptions: 9.1.1.3. Other exceptions and errors (Java and XSLT)
download site: 9.1. API or Abstraction (Java & XML)
error handlers: 5.2.4. JAXP XSLT Design (Java and XSLT)
factory, locating: 5.2.2. XSLT Plugability Layer (Java and XSLT)
input and output: 5.3. Input and Output (Java and XSLT)
JDOM output, feeding into: 5.3.6. Feeding JDOM Output into JAXP (Java and XSLT)
online resources: 0.2. Software and Versions (Java and XSLT)
parsing capability, lack of: 9.1. API or Abstraction (Java & XML)
TrAX API: 9.3.2. The TrAX API (Java & XML)
version 1.1: 9.3. JAXP 1.1 (Java & XML)
changes from v. 1.0: 9.3. JAXP 1.1 (Java & XML)
DefaulstHandler class: 9.3.1.1. The road to SAX 2.0 (Java & XML)
download site: 9.3. JAXP 1.1 (Java & XML)
Version 1.1: 5.2. Introduction to JAXP 1.1 (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
online resources: B. JAXP API Reference (Java and XSLT)
versions and supported parsers: 9.1.2. The Old and the New (Java & XML)
jaxp.jar file: 1.6.2. Crimson (SAX2)
JBuilder: 9.1.1.5. Java IDEs (Java and XSLT)
JdbcDataAdapter class: 7.3.2. Data Adapter Layer (Java and XSLT)
JCP (Java Community Process): 7.4.1. JDOM as a JSR (Java & XML)
JDK (Java Development Kit), versions: 1.3.1. An Operating System and Java (Java & XML)
JDK 1.2, online resources: 0.2. Software and Versions
JDOM (Java Document Object Model): 8. Advanced JDOM (Java & XML)
4.4.1. SAX-to-DOM Consumers (SAX2)
architecture: 8.1. Helpful JDOM Internals (Java & XML)
Attribute class: 7.2.3. Creating XML with JDOM (Java & XML)
beta 6, online resources: 0.2. Software and Versions (Java and XSLT)
builder classes: 8.2.2. Building with Custom Classes (Java & XML)
classes: 7.1. The Basics (Java & XML)
document viewer implementation: 8.3.4. Endgame (Java & XML)
DOM, contrasted with: 7. JDOM (Java & XML)
7.5.1. JDOM isn't DOM (Java & XML)
DOM, interoperability with: 1.2.4.4. JDOM and DOM interoperability (Java and XSLT)
Element class, subclassing: 8.2. JDOM and Factories (Java & XML)
element methods: 8.2.1. Creating a Factory (Java & XML)
EntityRef class: 8.1.2. The EntityRef Class (Java & XML)
factories: 8.2. JDOM and Factories (Java & XML)
creating: 8.2.1. Creating a Factory (Java & XML)
input/output model: 7.1.3. Input and Output (Java & XML)
interfaces, absence of: 8.3. Wrappers and Decorators (Java & XML)
iteration using Text class: 8.1.1. The Text Class (Java & XML)
Java Collections support: 7.1.1. Java Collections Support (Java & XML)
JDOM Beta 7: 8.1. Helpful JDOM Internals (Java & XML)
JDOMNode interface: 8.3.1. JDOMNode (Java & XML)
Namespace class: 8.1.3. The Namespace Class (Java & XML)
null values: 7.5.2. Null Return Values (Java & XML)
online resources: 1.2.4.3. JDOM (Java and XSLT)
output, feeding into JAXP: 5.3.6. Feeding JDOM Output into JAXP (Java and XSLT)
JDOM to text approach: 5.3.6.3. JDOM to text approach (Java and XSLT)
producer classes: 7.3.3. JDOM XML Production (Java and XSLT)
PropsToXML class: 7.2. PropsToXML (Java & XML)
SimpleXPathViewer class: 8.3.4. Endgame (Java & XML)
standardization: 7.4. Is JDOM a Standard? (Java & XML)
subclassing: 8.4.1. More on Subclassing (Java & XML)
Sun JSR, and: 1.2.4.3. JDOM (Java and XSLT)
Text class: 8.1.1. The Text Class (Java & XML)
trees, turning into SAX events: 3.5.1.3. Turning JDOM trees into SAX events (SAX2)
Version 1.0: A.4. JDOM 1.0 (Beta 7) (Java & XML)
XML generation with: 1.2.4.3. JDOM (Java and XSLT)
XML output, formatting methods: 7.2.4. Outputting XML with JDOM (Java & XML)
XML, outputting: 7.2.4. Outputting XML with JDOM (Java & XML)
XML production using: 7.3.3. JDOM XML Production (Java and XSLT)
XML, storing: 7.3.1. Storing XML (Java & XML)
XML to RSS, for conversion of: 14.3.2. Creating an XML RSS Document (Java & XML)
XML trees, and: 7.1. The Basics (Java & XML)
XMLProperties class: 7.3. XMLProperties (Java & XML)
XPathDisplayNode class: 8.3.3. Providing Support for XPath (Java & XML)
XSLT processors, support by: 1.2.4.4. JDOM and DOM interoperability (Java and XSLT)
JDOMException class: A.4.1.8. JDOMException (Java & XML)
JDOMFactory interface: 8.2.1. Creating a Factory (Java & XML)
A.4.3.3. JDOMFactory (Java & XML)
JDOMNode interface: 8.3.1. JDOMNode (Java & XML)
J2EE (Java2 Enterprise Edition): 1.4.5. Sun's Java API for XML Processing (JAXP) (SAX2)
Jelliffe, Rick: A.3.4. Schematron (XML Schema)
14.1. What's at the End of a Namespace URL? (XML in a Nutshell)
JIndent: 8.5. XSLT as a Code Generator (Java and XSLT)
.jpeg files, declaring with NDATA annotations: 4.3.2. The DTDHandler Interface (SAX2)
JPEG files, generating: 8.3.1. Generating JPEG Files from XML Content (XSLT)
9.5.7. Generating the JPEG Files (XSLT)
JRE (Java Runtine Environment): 1.3. Installing Xalan (XSLT)
JRMP: 11.1.1. What Is RMI? (Java & XML)
jsessionid: 8.2.1. Servlet Session-Tracking API (Java and XSLT)
8.2.1. Servlet Session-Tracking API (Java and XSLT)
JSP (Java Server Pages): 10.4. XSP (Java & XML)
embedded HTML, and: 4.1.3. JSP (Java and XSLT)
error handling: 4.1.3. JSP (Java and XSLT)
limitations: 10.4. XSP (Java & XML)
namespace prefixes: 4.1.3. JSP (Java and XSLT)
RequestDispatcher class: 4.1.5. The Hybrid Approach (Java and XSLT)
tag handlers: 4.1.3. JSP (Java and XSLT)
validation: 4.1.3. JSP (Java and XSLT)
JSR (Java Specification Request): 7.4.1. JDOM as a JSR (Java & XML)
JTreeContentHandler class: 3.3. Content Handlers (Java & XML)
jUDDI, online resources: 13.2. UDDI (Java & XML)
Jumbo XML browser: 1.4.1. SAX1 (SAX2)
JUnit: 9.1. Development Environment (Java and XSLT)
v. 3.7., online resources: 0.2. Software and Versions (Java and XSLT)
JVM (Java Virtual Machine)
CGI programming, unsuitability for: 4.1.1. CGI (Java and XSLT)
classes, loading of: 9.1.1.4. Java optional packages (Java and XSLT)
XSLT processor, as: 1.4.1. Java XSLT Processor Choices (Java and XSLT)
JVMPI (Java Virtual Machine Profiling Interface): 9.3.2.2. JVMPI (Java and XSLT)
Jython code: 8. Extending XSLT (XSLT)
8.1.2.2. Example: Writing extensions in other languages (XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: K

Kawaguchi, Kohsuke: 7.8. Derivation or Groups (XML Schema)
Kay, Michael: 8.3. Stylesheet Processors (XML in a Nutshell)
0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
8.2. Extending the Saxon Processor (XSLT)
Kernighan, Brian: 2.2. Transforming Hello World (XSLT)
<key> element: 8.2. XPath (Perl & XML)
key( ) function: C. XSLT and XPath Function Reference (XSLT)
advantages of: 5.2.4. Advantages of the key( ) Function (XSLT)
creating links using: 5.2. Generating Links with the key( ) Function (XSLT)
grouping elements/nodes and: 6.2. Grouping Nodes (XSLT)
IDREFS datatype and: 5.2.3.1. The key( ) function and the IDREFS datatype (XSLT)
limitations of IDs and: 5.1.4. Limitations of IDs (XSLT)
key( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
keys and references in schemas: 16.9.4.2. Keys and references (XML in a Nutshell)
keys/key references, defining: 9.2.3. Keys (XML Schema)
Korean characters
national standard character set for: 5.6. ISO Character Sets (XML in a Nutshell)
UTF-8/UTF-16 encodings: 5.5.2. UTF-8 (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

label attribute (Xlink): 10.4.1. Locators (XML in a Nutshell)
local resources and: 10.4.3. Local Resources (XML in a Nutshell)
lang attribute, <xsl:sort> element and: 6.1.2.2. Attributes (XSLT)
lang( ) function: C. XSLT and XPath Function Reference (XSLT)
lang pseudoclass: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
lang( ) (XPath): 22.5. XPath Functions (XML in a Nutshell)
language bindings: 5.1.1. Language Bindings (Java & XML)
language codes: 5.11.1. Language Codes (XML in a Nutshell)
languages
limiting number of: 5.5. Back to Our Library (XML Schema)
programming, functional: 1.1. The Design of XSLT (XSLT)
scripting, writing extensions and: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
Spanish, sorting and: 8.2. Extending the Saxon Processor (XSLT)
LanguageDemo.java: 8.6.3.3. Web page and servlet (Java and XSLT)
LANs (local area networks): 6.2.1. XML/Internet Versus Older Technologies (SAX2)
Lark and Larval: 1.3.2. A Parser (Java & XML)
Lark Java parser: 1.4.1. SAX1 (SAX2)
last( ) function: C. XSLT and XPath Function Reference (XSLT)
last-in, first-out (LIFO): 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
last( ) (XPath): 9.7.1. Node-Set Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
lastChild attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
lastChild property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
late binding of handlers: 3.1.1. The XMLReader Interface (SAX2)
Latin-1 characters entity set: 5.10. Character References (XML in a Nutshell)
26.2. HTML4 Entity Sets (XML in a Nutshell)
Latin-4 encoding: 5.6. ISO Character Sets (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
Latin Extended-A Unicode block: 26.3.1. Latin Extended-A (XML in a Nutshell)
Latin Extended Additional Unicode block: 26.3.15. Latin Extended Additional (XML in a Nutshell)
Latin Extended-B Unicode block: 26.3.2. Latin Extended-B (XML in a Nutshell)
Latin-1Supplement Unicode block: 6.5.1.1. Unicode blocks (XML Schema)
layout-master-set element (XSL-FO): 13.2. The Structure of an XSL-FO Document (XML in a Nutshell)
13.3. Laying Out the Master Pages (XML in a Nutshell)
LDAP (Lightweight Directory Access Protocol): 11.1.1. What Is RMI? (Java & XML)
11.1.1. What Is RMI? (Java & XML)
leading zeros, removing with regular expressions: 6.5.2.1. Leading zeros (XML Schema)
left property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
legacy encodings: 5.4. XML-Defined Character Sets (XML in a Nutshell)
legacy module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
legends
for documents, sample of: 3.5.2. The Stylesheet (XSLT)
for pie charts, drawing: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
length attribute
CharacterData interface: 24.2. Object Reference (XML in a Nutshell)
NamedNodeMap interface: 24.2. Object Reference (XML in a Nutshell)
NodeList interface: 24.2. Object Reference (XML in a Nutshell)
length-constraining facets: 5.1.1.1.2. (XML Schema)
using lists to change behavior of: 5.4.2. Using or Abusing Lists to Change the Behavior of Length Constraining Facets (XML Schema)
length facet element: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
length property
CharacterData class (DOM): 7.2.7.1. Properties (Perl & XML)
NameNodeMap class (DOM): 7.2.6.1. Properties (Perl & XML)
NodeList class (DOM): 7.2.5.1. Properties (Perl & XML)
length, units of: 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
less than operator (<): 4.2.1. The <xsl:if> Element (XSLT)
4.2.1. The <xsl:if> Element (XSLT)
less-than-or-equal operator (<=): 4.2.1. The <xsl:if> Element (XSLT)
4.2.1. The <xsl:if> Element (XSLT)
< (less-than sign), not allowed in attribute values): 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
Letter-like Symbols Unicode block: 26.3.19. Letter-Like Symbols (XML in a Nutshell)
letter-sized pages, PDF files for: 9.1. About the Toot-O-Matic (XSLT)
9.3.7. PDF Files (XSLT)
generating: 9.5.6. Generating the PDF Files (XSLT)
icons for: 9.3.1. Menu Panel (XSLT)
lexical handlers: 5.7.2. SAX2 Handler Interface (Perl & XML)
5.7.2.3. Lexical event handlers (Perl & XML)
lexical spaces: 4.1. Lexical and Value Spaces (XML Schema)
fixing format of: 6.5.2.2. Fixed format (XML Schema)
multiple restrictions and: 5.1.2. Multiple Restrictions and Fixed Attribute (XML Schema)
restricting, using regular expressions: 6. Using Regular Expressions to Specify Simple Datatypes (XML Schema)
of xs:ID and xs:IDREF types: 9.1. and (XML Schema)
LexicalHandler class: 4.4.1. LexicalHandler (Java & XML)
3.3.2. XMLReader Feature Flags (SAX2)
4.2. The LexicalHandler Interface (SAX2)
A.3.2. The LexicalHandler Interface (SAX2)
DOM documents, building: 4.4.1. SAX-to-DOM Consumers (SAX2)
DTD data and: 4.3. Exposing DTD Information (SAX2)
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
LexicalHandler interface: A.1.2.2. LexicalHandler (Java & XML)
LexicalHandler interface (SAX): 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
LexicalHandler.comment( ) function: B.9. Comment Information Items (SAX2)
LexicalHandler.endDTD( ) function: B.3. Document Information Item (SAX2)
LexicalHandler.startDTD( ) function: B.10. Document Type Declaration Information Item (SAX2)
LexicalHandler.startEntity( ) function: 4.3.1. The DeclHandler Interface (SAX2)
libxml2: 3.5. XML::LibXML (Perl & XML)
5.7.1. XML::SAX::ParserFactory (Perl & XML)
LibXML library: 7.4. XML::LibXML (Perl & XML)
LIFO (last-in, first-out): 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
Lightweight Directory Access Protocol (LDAP): 11.1.1. What Is RMI? (Java & XML)
line areas (XSL-FO): 13.1. XSL Formatting Objects (XML in a Nutshell)
line-height property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
link module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
link pseudoclass: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
linkbases: 10.5. Linkbases (XML in a Nutshell)
links: 17.2.6. Links and References (XML in a Nutshell)
(see also XLinks)
behavior of: 10.2. Link Behavior (XML in a Nutshell)
creating: 5. Creating Links and Cross-References (XSLT)
in unstructured documents: 5.3. Generating Links in Unstructured Documents (XSLT)
using id( ) function: 5.1. Generating Links with the id( ) Function (XSLT)
using key( ) function: 5.2. Generating Links with the key( ) Function (XSLT)
extended (see extended links)
following: 10.2.2. xlink:actuate (XML in a Nutshell)
inbound/outbound: 10.5. Linkbases (XML in a Nutshell)
semantics of: 10.3. Link Semantics (XML in a Nutshell)
simple: 10.1. Simple Links (XML in a Nutshell)
third-party: 10.5. Linkbases (XML in a Nutshell)
XML processing issues: 17.2.6. Links and References (XML in a Nutshell)
XPointers in: 11.2. XPointers in Links (XML in a Nutshell)
Linux/Unix, Cocoon binaries: 10.2.1. Source Code or Binaries (Java & XML)
list( ): 12.3.2.1. Code artifacts (Java & XML)
error handling: 12.4.2. Better Error Handling (Java & XML)
list-block element (XSL-FO): 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
list datatypes: 4.6. List Types (XML Schema)
5.2.1. List Datatypes (XML Schema)
defining by embedding xs:simpleType (global definition): 5.2.1. List Datatypes (XML Schema)
deriving from atomic datatypes: 5.2. Derivation By List (XML Schema)
list elements (CSS display property): 12.5.3. List Elements (XML in a Nutshell)
list-item value (display property): 12.5.3. List Elements (XML in a Nutshell)
list mode: 3.2.5. Template Modes (Java and XSLT)
list module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
list-style-image value (display property): 12.5.3. List Elements (XML in a Nutshell)
list-style-position value (display property): 12.5.3. List Elements (XML in a Nutshell)
list-style-type value (display property): 12.5.3. List Elements (XML in a Nutshell)
literal data characters: 8.4. Templates and Template Rules (XML in a Nutshell)
literal default declaration: 3.3.2. Attribute Defaults (XML in a Nutshell)
Literal Result Element as Stylesheet (XSLT 1.0 specification): 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
literal result elements: 8.4. Templates and Template Rules (XML in a Nutshell)
attribute value templates and: 8.9. Attribute Value Templates (XML in a Nutshell)
namespace syntax and: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
xsl:apply-templates and: 8.6. Applying Templates with xsl:apply-templates (XML in a Nutshell)
literal result elements (LREs): 3.3. Attribute Value Templates (XSLT)
literal string property: 4.1.2.4. DOM nodes and literal strings (Java & XML)
literal values, single quotes and: 4.5. Variables (XSLT)
little-endian UCS-2: 5.5.1. UCS-2 and UTF-16 (XML in a Nutshell)
load( ): 7.3. XMLProperties (Java & XML)
7.3.2. Loading XML (Java & XML)
loadFromElements( ): 7.3.2. Loading XML (Java & XML)
7.3.2. Loading XML (Java & XML)
local area networks (LANs): 6.2.1. XML/Internet Versus Older Technologies (SAX2)
local complex types: 7.2.2. Named Versus Anonymous Types (XML Schema)
local-name( ) function: C. XSLT and XPath Function Reference (XSLT)
local-name( ) (XPath): 9.7.1. Node-Set Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
local parts: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
local resources (extended links): 10.4. Extended Links (XML in a Nutshell)
10.4.3. Local Resources (XML in a Nutshell)
##local wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
locale-specific content: 8.6.1. XSLT Stylesheet Design (Java and XSLT)
locally defining components: 3.1. Working From the Structure of the Instance Document (XML Schema)
Russian doll design: 3.2.2. Russian Doll and Object-Oriented Design (XML Schema)
when to use: 3.2.1. Depth Versus Modularity? (XML Schema)
localName attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
localName parameter: B.4. Element Information Items (SAX2)
location paths: 9.2. Location Paths (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
3.2. Location Paths (XSLT)
axes: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
building compound: 9.3.1. Building Compound Location Paths from Location Steps with / (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
node tests: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
unabbreviated: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
using predicates in: 9.4. Predicates (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
22.4. Predicates (XML in a Nutshell)
location paths in XPath expressions: 8.2. XPath (Perl & XML)
location steps
attribute: 9.2.3. Attribute Location Steps (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
child element: 9.2.2. Child Element Location Steps (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
combining: 9.3. Compound Location Paths (XML in a Nutshell)
comment( ): 9.2.4. The comment( ), text( ), and processing-instruction( ) Location Steps (XML in a Nutshell)
components of: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
processing-instruction( ): 9.2.4. The comment( ), text( ), and processing-instruction( ) Location Steps (XML in a Nutshell)
text( ): 9.2.4. The comment( ), text( ), and processing-instruction( ) Location Steps (XML in a Nutshell)
locations and XPath nodes: 11.6. Points (XML in a Nutshell)
Locator class: 3.3.1. The Document Locator (Java & XML)
A.1.1.10. Locator (Java & XML)
4.1.1. Other ContentHandler Methods (SAX2)
4.1.2. The Locator Interface (SAX2)
A.1.10. The Locator Interface (SAX2)
locator data: B.2.2. Base URIs, xml:base, and Locator Data (SAX2)
locator elements: 10.4.1. Locators (XML in a Nutshell)
Locator instance: 3.5.3. The Misplaced Locator (Java & XML)
Locator interface (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
Locator.getSystemId( ) function: B.3. Document Information Item (SAX2)
B.7. Unexpanded Entity Reference Information Items (SAX2)
B.12. Notation Information Items (SAX2)
LocatorImpl class: A.1.3.4. LocatorImpl (Java & XML)
5.1.2. The LocatorImpl Class (SAX2)
A.2.4. The LocatorImpl Class (SAX2)
LocatorImpl class (SAX): 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
locators in XPath: 8.2. XPath (Perl & XML)
log( ): 6.3.3. Source Code (Java and XSLT)
LogDriver directory, installing parsers: 5.7.5. Installing Your Own Parser (Perl & XML)
logic: 4. Branching and Control Elements (XSLT)
for controlling flow of stylesheets: 4.5. Variables (XSLT)
in sample stylesheet: 4.7.3. The Complete Example (XSLT)
logging file: 3.5.1. An Example XML File (Java and XSLT)
lookup tables: 7.4.2. Implementing Lookup Tables (XSLT)
looping: 2.3.3.2. Looping (Java & XML)
loops: 4.7. A Stylesheet That Emulates a for Loop (XSLT)
<xsl:for-each> element vs.: 4.2.3. The <xsl:for-each> Element (XSLT)
loops, checking XML markup objects: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
loose coupling: 6.2.1. XML/Internet Versus Older Technologies (SAX2)
Lorie, Ray: 1.4. The Evolution of XML (XML in a Nutshell)
6.1. SGML's Legacy (XML in a Nutshell)
Lorie, Raymond: 2.1. A Brief History of XML (Perl & XML)
LotusXSL: 1.4.1.2. LotusXSL (Java and XSLT)
low-level APIs: 1.3.3. APIs (Java & XML)
low surrogate (Java char values): 4.1.3. Internationalization Concerns (SAX2)
LREs (literal result elements): 3.3. Attribute Value Templates (XSLT)
Lynx User-Agent: 8.3. Identifying the Browser (Java and XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

Mac-specific character sets: 5.7.2. MacRoman (XML in a Nutshell)
Macherius, Ingo: A.3.2. The W3C XML Schema Family (XML Schema)
Macintosh
filenames, turning into URIs: 3.1.3. Filenames Versus URIs (SAX2)
XML output, writing: 2.2.3.2. Concerns when writing XML text (SAX2)
MacLeod, Ken: 6.6. XML::Grove (Perl & XML)
MacRoman character set: 5.7.2. MacRoman (XML in a Nutshell)
mailing lists
XML-DEV: 1.2.3. DOM and SAX (XSLT)
XSL-LIST: 2.6. Summary (XSLT)
mailing lists, Xerces parser: 3.1.1. Obtaining a Parser (Java & XML)
main menu panel, generating: 9.5.3. Generating the Main Menu Panel (XSLT)
main(): 1.2.4.2. DOM (Java and XSLT)
mode attribute and: 9.5.1. Stylesheets and Modes (XSLT)
Makefile.PL, installing parsers: 5.7.5. Installing Your Own Parser (Perl & XML)
MapEntry class: 5.4.2. A Stylesheet Cache (Java and XSLT)
mappings element: 12.4.1. Custom Parameter Types (Java & XML)
mapTypes( ): 12.4.1. Custom Parameter Types (Java & XML)
margin-bottom property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
margin-left property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
margin-right property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
margin-top property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
markup
describing a document's structure: 6.5. Document Permanence (XML in a Nutshell)
input vs. output languages: 6.6. Transformation and Presentation (XML in a Nutshell)
tags and: 2.2. Elements, Tags, and Character Data (XML in a Nutshell)
markup languages: 2. An XML Recap (Perl & XML)
1.2. XML Review (Java and XSLT)
1.2.1. SGML, XML, and Markup Languages (Java and XSLT)
elements and structure for: 2.2. Markup, Elements, and Structure (Perl & XML)
marshal( ): 13.4.1. A Messaging Service (Java & XML)
15.4.3. Marshalling and Unmarshalling (Java & XML)
marshalling: 15.1.3. Marshalling (Java & XML)
3.5.2.2. Turning objects into SAX events (SAX2)
master document: 7.2. The document( ) Function (XSLT)
master-name attribute (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
master pages for XSL-FO, laying out: 13.3. Laying Out the Master Pages (XML in a Nutshell)
Mathematical Operators Unicode block: 26.3.21. Mathematical Operators (XML in a Nutshell)
mathematics
performed with recursion: 7.2.1. An Aside: Doing Math with Recursion (XSLT)
trigonometric functions for: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
XPath operators for: B.4.1. Mathematical Operators (XSLT)
MathML: 4.1.3. Internationalization Concerns (SAX2)
MathML elements: 10.1. Perl and XML Namespaces (Perl & XML)
MathML (Mathematical Markup Language): 0.. What This Book Covers (XML in a Nutshell)
entity sets and: 5.10. Character References (XML in a Nutshell)
namespaces
need for: 4.1. The Need for Namespaces (XML in a Nutshell)
syntax for: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
maxExclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
maxInclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
maxLength facet element: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
maxOccurs attribute: 2.2. Our First Schema (XML Schema)
compositors and particles: 7.4.1.1. Compositors and particles (XML Schema)
extending attribute/element groups, issues with: 8.2.2.1. Extension (XML Schema)
maxOccurs attribute (xs:element element): 16.4.1. Occurrence Constraints (XML in a Nutshell)
McLean, Grant: 1.2. XML Is Simple with XML::Simple (Perl & XML)
6.2. XML::Simple (Perl & XML)
media pseudoattribute: 7.2.1.2. The media pseudoattribute (XML in a Nutshell)
12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
media types, MIME: 5.1. Character-Set Metadata (XML in a Nutshell)
Megginson, David: 5. SAX (Perl & XML)
1.4.1. SAX1 (SAX2)
1.4. The Evolution of XML (XML in a Nutshell)
25. SAX Reference (XML in a Nutshell)
memory
consuming, with SAX and DOM: 1.2.3. Memory Consumption with SAX and DOM (SAX2)
SAX parsers, using: 1.2.1. Stream-Based Processing (SAX2)
memory and DOM: 5.1.3.3. Why use SAX at all? (Java & XML)
5.4.1. Memory, Performance, and Deferred DOMs (Java & XML)
menu panel (tutorials): 9.3.1. Menu Panel (XSLT)
message element: 13.3. WSDL (Java & XML)
Message interface: 7.3.1. Domain Classes (Java and XSLT)
A. Discussion Forum Code (Java and XSLT)
MessageSummary interface: 7.3.1. Domain Classes (Java and XSLT)
A. Discussion Forum Code (Java and XSLT)
messaging: 6.2. XML and Messaging (SAX2)
HTTP with SAX 2.0 and: 6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
Java, roles for: 6.2.2. Roles for Java in XML Messaging (SAX2)
messaging services: 13.4.1. A Messaging Service (Java & XML)
method names, and: 13.4.1. A Messaging Service (Java & XML)
XML, server response to client, coding: 13.4.1. A Messaging Service (Java & XML)
META-INF/MANIFEST.MF file: 9.1.1.1. JAR hell? (Java and XSLT)
metadata, character-set: 5.1. Character-Set Metadata (XML in a Nutshell)
metainformation module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
metalanguages: 1.2.1. SGML, XML, and Markup Languages (Java and XSLT)
methods
access synchronization, drawbacks to: 6.5.1. Servlet Threading Model (Java and XSLT)
addContent( ): 7.2.3. Creating XML with JDOM (Java & XML)
7.3.1. Storing XML (Java & XML)
addElem( ): 6.3.3. Source Code (Java and XSLT)
addEvent( ): 11.3.1. A Shared Handler (Java & XML)
11.3.1. A Shared Handler (Java & XML)
addHandler( ): 11.2.3. Writing the Server (Java & XML)
attribute names, accessing: 2.6.2.2. Attribute naming (SAX2)
callback methods: 3.2.2. Parsing the Document (Java & XML)
characters( ): 3.3.6. Element Data (Java & XML)
cleanup( ): 6.3.3. Source Code (Java and XSLT)
count( ): 3.1.1. <xsl:if> (Java and XSLT)
createDocument( ): 6.1.1. Creating a New DOM Tree (Java & XML)
createXMLRepresentation( ): 7.2.3. Creating XML with JDOM (Java & XML)
7.2.3. Creating XML with JDOM (Java & XML)
destroy( ): 5.4.2. A Stylesheet Cache (Java and XSLT)
doFilter( ): 8.4.1. Filter Overview (Java and XSLT)
doGet( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
6.1.1. Splash Screen Servlet Example (Java and XSLT)
6.3.1. Design (Java and XSLT)
doPost( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
6.3.1. Design (Java and XSLT)
element methods: 8.2.1. Creating a Factory (Java & XML)
encodeURL( ): 8.2.1. Servlet Session-Tracking API (Java and XSLT)
endDocument( ): 1.2.4.1. SAX (Java and XSLT)
endElement( ): 1.2.4.1. SAX (Java and XSLT)
endDocument( ): 3.3.2. The Beginning and the End of a Document (Java & XML)
enumerateItem( ): 15.2.2. Source Generation (Java & XML)
equals( ): 8.1.3. The Namespace Class (Java & XML)
fault.getString( ): 12.4.2. Better Error Handling (Java & XML)
flushAll( ): 5.4.2. A Stylesheet Cache (Java and XSLT)
format-number( ): 3.4.1. Number Formatting (Java and XSLT)
generateRSSContentMethod( ): 14.3.2. Creating an XML RSS Document (Java & XML)
getAttribute( ): 9.3.2.1. Working with the factory (Java & XML)
getAttributeValue( ): 7.3.4. Backtracking (Java & XML)
getBody( ): 13.4.1. A Messaging Service (Java & XML)
getChild( ): 7.2.3. Creating XML with JDOM (Java & XML)
getChildNodes( ): 5.2.3.3. Working with nodes (Java & XML)
getChildren( ): 7.3.2. Loading XML (Java & XML)
getColumnNumber( ): 3.3.1. The Document Locator (Java & XML)
getContent( ): 7.3.4. Backtracking (Java & XML)
7.3.4. Backtracking (Java & XML)
getDetailEntries( ): 12.4.2. Better Error Handling (Java & XML)
getDocumentElement( ): 5.2.3.3. Working with nodes (Java & XML)
getException( ): 3.3.2. The Beginning and the End of a Document (Java & XML)
getFeature( ): 9.3.1.2. Changes in SAX classes (Java & XML)
getInstance( ): 11.4.2. Shared Instances (Java & XML)
getItem( ): 15.2.2. Source Generation (Java & XML)
getLineNumber( ): 3.3.1. The Document Locator (Java & XML)
getListOfEvents( ): 11.3.1. A Shared Handler (Java & XML)
getLocalName(int index): 3.3.5. Element Callbacks (Java & XML)
getNodeName( ): 5.2.3.3. Working with nodes (Java & XML)
getOutputProperties( ): 5.4.1. Templates API (Java and XSLT)
getOutputStream( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
8.4.2. XSLT Transformation Filter (Java and XSLT)
getParameter( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
getPersonalData( ): 6.3.3. Source Code (Java and XSLT)
getResource( ): 6.2.4. Servlet API Highlights (Java and XSLT)
getResourceAsStream( ): 6.2.4. Servlet API Highlights (Java and XSLT)
getProperty( ): 7.2.3. Creating XML with JDOM (Java & XML)
9.3.1.2. Changes in SAX classes (Java & XML)
getRootElement( ): 7.3.2. Loading XML (Java & XML)
getServletInfo( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
getText( ): 7.3.2. Loading XML (Java & XML)
getTextTrim( ): 7.3.2. Loading XML (Java & XML)
getURI(int index): 3.3.5. Element Callbacks (Java & XML)
getWriter( ): 6.1.1. Splash Screen Servlet Example (Java and XSLT)
8.4.2. XSLT Transformation Filter (Java and XSLT)
getXMLReader( ): 9.3.1.1. The road to SAX 2.0 (Java & XML)
getXPath( ): 8.3.3. Providing Support for XPath (Java & XML)
init( ): 6.3.3. Source Code (Java and XSLT)
stylesheets, location of: 6.3.6. Locating Stylesheets with Initialization Parameters (Java and XSLT)
InputSource class: 3.1.2.1. Always provide absolute URIs (SAX2)
isNamespaceAware( ): 9.2.1.2. Working with the SAXParser class (Java & XML)
isValidating( ): 9.2.1.2. Working with the SAXParser class (Java & XML)
list( ): 12.3.2.1. Code artifacts (Java & XML)
12.4.2. Better Error Handling (Java & XML)
load( ): 7.3. XMLProperties (Java & XML)
7.3.2. Loading XML (Java & XML)
loadFromElements( ): 7.3.2. Loading XML (Java & XML)
7.3.2. Loading XML (Java & XML)
log( ): 6.3.3. Source Code (Java and XSLT)
main(): 1.2.4.2. DOM (Java and XSLT)
mapTypes( ): 12.4.1. Custom Parameter Types (Java & XML)
marshal( ): 13.4.1. A Messaging Service (Java & XML)
15.4.3. Marshalling and Unmarshalling (Java & XML)
naming and messaging services: 13.4.1. A Messaging Service (Java & XML)
newSAXParser( ): 9.2.1.1. A look at the SAXParserFactory class (Java & XML)
newTransformer( ): 9.3.2.1. Working with the factory (Java & XML)
parse( ): 3.2.2. Parsing the Document (Java & XML)
propertyNames( ): 7.2.3. Creating XML with JDOM (Java & XML)
remote calls: 11.2.3. Writing the Server (Java & XML)
removeEvent( ): 11.3.1. A Shared Handler (Java & XML)
11.3.1. A Shared Handler (Java & XML)
RMI, invocation by: 11.1.1. What Is RMI? (Java & XML)
save( ): 7.3. XMLProperties (Java & XML)
service( ): 6.5.2. Thread Safety Tips (Java and XSLT)
setAttribute( ): 7.2.3. Creating XML with JDOM (Java & XML)
9.3.2.1. Working with the factory (Java & XML)
setDriver( ): 11.2.3. Writing the Server (Java & XML)
11.2.4. Writing the Client (Java & XML)
setErrorListener( ): 9.3.2.1. Working with the factory (Java & XML)
setExpandEntities( ): 8.1.2. The EntityRef Class (Java & XML)
setFeature( ): 9.3.1.2. Changes in SAX classes (Java & XML)
setProperty( ): 9.3.1.2. Changes in SAX classes (Java & XML)
setSystemId( ): 6.3.3. Source Code (Java and XSLT)
setText( ): 7.2.3. Creating XML with JDOM (Java & XML)
showErrorPage( ): 6.3.3. Source Code (Java and XSLT)
showPage( ): 6.3.3. Source Code (Java and XSLT)
startDocument( ): 1.2.4.1. SAX (Java and XSLT)
startElement( ): 1.2.4.1. SAX (Java and XSLT)
startDocument( ): 3.3.2. The Beginning and the End of a Document (Java & XML)
store( ): 7.3. XMLProperties (Java & XML)
7.3.1. Storing XML (Java & XML)
7.3.4. Backtracking (Java & XML)
synchronization: 5.4.2. A Stylesheet Cache (Java and XSLT)
transform( ): 9.3.2.2. Transforming XML (Java & XML)
toURL( ): 5.1.2. Xalan 1 Implementation (Java and XSLT)
Transformer( ): 5.4.2. A Stylesheet Cache (Java and XSLT)
unmarshall( ): 15.4.3. Marshalling and Unmarshalling (Java & XML)
XML output, for formatting: 7.2.4. Outputting XML with JDOM (Java & XML)
XML-RPC, invocation in: 11.2.2. Writing the Handler (Java & XML)
XMLReader interface, for setting: 4.1.1. Setting Properties and Features (Java & XML)
methods (DOM): 7.2.6.2. Methods (Perl & XML)
Document class: 7.2.1.2. Methods (Perl & XML)
Elements node: 7.2.8.2. Methods (Perl & XML)
Node class: 7.2.4.2. Methods (Perl & XML)
NodeList class: 7.2.5.2. Methods (Perl & XML)
Microsoft
and character sets: 5.7. Platform-Dependent Character Sets (XML in a Nutshell)
font configuration, determining: 8.6.3. Putting It All Together (Java and XSLT)
Internet Explorer, XSL stylesheet: 10.3.3. Browser-Dependent Styling (Java & XML)
Microsoft DTD repository (biztalk.org): 3.10. Locating Standard DTDs (XML in a Nutshell)
MXSML Parser: 1.3.2. A Parser (Java & XML)
shipping CDFs: 6.1. Rich Site Summary: RSS (SAX2)
SOAP implementation: 12.2.1.2. Isn't Microsoft a player? (Java & XML)
User-Agent: 8.3. Identifying the Browser (Java and XSLT)
Windows, Cocoon binaries: 10.2.1. Source Code or Binaries (Java & XML)
XSLT support: 1.5. Web Browser Support for XSLT (Java and XSLT)
7.2.3. XSLT Stylesheets (Java and XSLT)
Windows, Unicode support: 8.6.2.1. Creating the XML and XSLT (Java and XSLT)
MIME media types: 5.1. Character-Set Metadata (XML in a Nutshell)
rddl:resource elements and: 14.2. RDDL Syntax (XML in a Nutshell)
for stylesheets: 12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
minExclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
minInclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
minLength facet element: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
minOccurs attribute: 2.2. Our First Schema (XML Schema)
compositors and particles: 7.4.1.1. Compositors and particles (XML Schema)
extending attribute/element groups, issues with: 8.2.2.1. Extension (XML Schema)
minOccurs attribute (xs:element element): 16.4.1. Occurrence Constraints (XML in a Nutshell)
Miscellaneous Symbols Unicode block: 26.3.25. Miscellaneous Symbols (XML in a Nutshell)
Miscellaneous Technical Unicode block: 26.3.22. Miscellaneous Technical (XML in a Nutshell)
%Misc.extra entity reference: 7.3.2. Mixing Your Applications into XHTML (XML in a Nutshell)
mixed attribute: 7.2.1. Content Models (XML Schema)
mixed content
element type: 3.2.7. Mixed Content (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
in elements: 2.2.3. Mixed Content (XML in a Nutshell)
schemas and: 16.7. Mixed Content (XML in a Nutshell)
mixed content models: 2.2. Our First Schema (XML Schema)
creating: 7.5.1. Creating Mixed Content Models (XML Schema)
derivation by extension: 7.5.2.1. Derivation by extension (XML Schema)
derivation by restriction: 7.5.2.2. Derivation by restriction (XML Schema)
deriving from complex contents: 7.5.2.3. Derivation between complex and mixed content models (XML Schema)
xs:annotation element and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
mixed mode: 2.6.3. Namespace Feature Flags (SAX2)
<mml:math> element: 10.1. Perl and XML Namespaces (Perl & XML)
mod (XPath arithmetic operator): 9.6.1. Numbers (XML in a Nutshell)
mode attribute: 4.3.2. Templates à la Mode (XSLT)
9.5.1. Stylesheets and Modes (XSLT)
mode attributes for XSLT elements: 8.8. Modes (XML in a Nutshell)
mode=generate-pdf attribute: 9.5.6. Generating the PDF Files (XSLT)
modeling tools for schema languages: A.1.3. Intrusive Modeling Tools (XML Schema)
modes: 3.2.5. Template Modes (Java and XSLT)
Modular XHTML (see XHTML 1.1)
modularity of W3C XML Schema: 2.3.1. W3C XML Schema Is Modular (XML Schema)
vs. depth of schemas: 3.2.1. Depth Versus Modularity? (XML Schema)
Modularization of XHTML (W3C specification): 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
modules: 1.4. A Myriad of Modules (Perl & XML)
CPAN and: 1.1. Why Use Perl with XML? (Perl & XML)
XML: 9.1. XML Modules (Perl & XML)
modules, XHTML: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
monetary symbols, Unicode block for: 26.3.18. Currency Symbols (XML in a Nutshell)
Mosher, Ed: 1.4. The Evolution of XML (XML in a Nutshell)
6.1. SGML's Legacy (XML in a Nutshell)
Mosher, Edward: 2.1. A Brief History of XML (Perl & XML)
Motorola Mobile Application Development Kit: 10.1. Wireless Technologies (Java and XSLT)
$mouse-effects variable: 9.5.2. Initializing Global Variables (XSLT)
mouse_over( ) function: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
Mozilla, displaying XML in: 7.2.3. Netscape and Mozilla (XML in a Nutshell)
MSXML and stylesheet processors: 8.3. Stylesheet Processors (XML in a Nutshell)
MSXML Java parser: 1.4.1. SAX1 (SAX2)
MSXML parser: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
Muellner, Leonard: 6.4. DocBook (XML in a Nutshell)
Muench method: 6.2.4. The <xsl:key> Approach (XSLT)
Muench, Steve: 6.2.4. The <xsl:key> Approach (XSLT)
multilingual documents: 5.11. xml:lang (XML in a Nutshell)
multithreaded applications, using SAX with: 1.2.4. Other Reasons to Prefer SAX (SAX2)
Murata, Makoto: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
A.3.3. The RELAX NG Family (XML Schema)
A.7. RELAX NG (XML Schema)
Murray-Rust, Peter: 1.4. The Evolution of XML (XML in a Nutshell)
MVC (model-view-controller): 4.2. The Universal Design (Java and XSLT)
advantages: 4.2.2. Model-View-Controller (Java and XSLT)
classes: 4.2.2. Model-View-Controller (Java and XSLT)
controller, purpose: 4.2.2. Model-View-Controller (Java and XSLT)
dependencies: 4.2.2. Model-View-Controller (Java and XSLT)
scalability: 4.2.2. Model-View-Controller (Java and XSLT)
My Netscape, using RSS: 6.1. Rich Site Summary: RSS (SAX2)
MySQL dump: 7.3.2. Data Adapter Layer (Java and XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

\n special character (new line): 6.4.1. Special Characters (XML Schema)
name attribute
Attr interface: 24.2. Object Reference (XML in a Nutshell)
DocumentType interface: 24.2. Object Reference (XML in a Nutshell)
name characters (Unicode): 26.1. Character Tables (XML in a Nutshell)
name( ) function: 3.1.6. Processing Instruction Nodes (XSLT)
C. XSLT and XPath Function Reference (XSLT)
name identification module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
name property
Attr class (DOM): 7.2.9.1. Properties (Perl & XML)
DocumentType class (DOM): 7.2.3.1. Properties (Perl & XML)
name start characters (Unicode): 26.1. Character Tables (XML in a Nutshell)
name tokens, datatypes for: 4.3.3.1. Tokenss (XML Schema)
name tokens, XML: 3.3.1.2. NMTOKEN (XML in a Nutshell)
name-value pairs: 2.3. Attributes (XML in a Nutshell)
name( ) (XPath): 9.7.1. Node-Set Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
named templates, invoking: 4.3. Invoking Templates by Name (XSLT)
named vs. anonymous datatypes: 7.2.2. Named Versus Anonymous Types (XML Schema)
NamedNodeMap: 5.2.3.3. Working with nodes (Java & XML)
interface: A.2.1.13. NamedNodeMap (Java & XML)
NamedNodeMap class (DOM): 7.2.6. NamedNodeMap (Perl & XML)
NamedNodeMap interface (DOM): 18.3.2. The NamedNodeMap Interface (XML in a Nutshell)
attribute for: 24.2. Object Reference (XML in a Nutshell)
methods for: 24.2. Object Reference (XML in a Nutshell)
names
element (see element names)
qualified: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
defining: 4.4.1. Parameter Entity References for Namespace Prefixes (XML in a Nutshell)
XML specification for: 20.3.4. Namespaces (XML in a Nutshell)
unqualified: 20.3.4. Namespaces (XML in a Nutshell)
XML: 2.4. XML Names (XML in a Nutshell)
specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
vs.name tokens: 3.3.1.2. NMTOKEN (XML in a Nutshell)
names of elements: 3.3.5. Element Callbacks (Java & XML)
namespaces: 2.1.2. Namespaces (Java & XML)
4.1.2.2. Namespaces (Java & XML)
6.2. Namespaces (Java & XML)
1.6. XML Gotchas (Perl & XML)
2.3. Namespaces (Perl & XML)
10.1. Perl and XML Namespaces (Perl & XML)
1.8. What XML Are We Talking About? (SAX2)
2.4.1. SAX2 Feature Flags (SAX2)
2.6. Namespaces and SAX2 (SAX2)
10. Controlling Namespaces (XML Schema)
0.. What This Book Covers (XML in a Nutshell)
4. Namespaces (XML in a Nutshell)
1.2.2.11. Namespaces (XSLT)
accommodating attributes/elements from other: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
behavior of imported schemas: 10.8. Namespace Behavior of Imported Components (XML Schema)
challenges presented to schema languages: 10.1. Namespaces Present Two Challenges to Schema Languages (XML Schema)
constraints for: 20.4.3. Namespace Constraints (XML in a Nutshell)
controlling qualification: 16.3.2. Controlling Qualification (XML in a Nutshell)
declarations: 6.2. Namespaces (Java & XML)
10.2. Namespace Declarations (XML Schema)
WML: 10.3.4. WAP and XML (Java & XML)
default (see default namespaces)
defining alias for: 4.8.3. Complications (XSLT)
DTDs and: 4.4. Namespaces and DTDs (XML in a Nutshell)
element/attribute declarations: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
feature flags: 2.6.3. Namespace Feature Flags (SAX2)
how parsers handle: 4.3. How Parsers Handle Namespaces (XML in a Nutshell)
identifying XML with: 2.1.2. Namespaces (Java & XML)
importing schemas with no namespaces: 10.9. Importing Schemas with No Namespaces (XML Schema)
including schemas using xs:include and xs:redefine: 10.10. Chameleon Design (XML Schema)
Information Items: B.13. Namespace Information Items (SAX2)
NamespaceSupport class and: 5.1.3. The NamespaceSupport Class (SAX2)
naming attributes and elements with: 2.6.2. Element and Attribute Naming with Namespaces (SAX2)
nodes
default template for: 8.7.4. The Default Template Rule for Namespace Nodes (XML in a Nutshell)
location paths and: 9.2.4. The comment( ), text( ), and processing-instruction( ) Location Steps (XML in a Nutshell)
node set functions for: 9.7.1. Node-Set Functions (XML in a Nutshell)
XPath data model and: 22.1. The XPath Data Model (XML in a Nutshell)
prefixes
4.1.3. JSP (Java and XSLT)
controversy with: 10.2. Namespace Declarations (XML Schema)
declaring, in XPath expressions: 10.5. Namespaces and XPath Expressions (XML Schema)
defining in schemas: 10.3. To Qualify Or Not to Qualify? (XML Schema)
issues with: 10.1. Namespaces Present Two Challenges to Schema Languages (XML Schema)
namespace declarations and: 10.2. Namespace Declarations (XML Schema)
qualified/unqualified attributes: 10.3. To Qualify Or Not to Qualify? (XML Schema)
XML 1.0 namespace: 10.7.1. XML Attributes (XML Schema)
RDDL and namespace URLs: 14. Resource Directory Description Language (RDDL) (XML in a Nutshell)
reasons for using: 4.1. The Need for Namespaces (XML in a Nutshell)
referencing other namespaces: 10.6. Referencing Other Namespaces (XML Schema)
SAX callbacks: 3.3.4. Namespace Callbacks (Java & XML)
schema specification, similarity to: 2.1.2. Namespaces (Java & XML)
schemas and: 16.1.2. Namespace Issues (XML in a Nutshell)
16.3. Working with Namespaces (XML in a Nutshell)
21.1. The Schema Namespaces (XML in a Nutshell)
setting default: 4.2.4. Setting a Default Namespace with the xmlns Attribute (XML in a Nutshell)
20.3.4. Namespaces (XML in a Nutshell)
specification for: 1.2.4.7. Namespaces in XML (XSLT)
specification of: 1.4.2. SAX2 (SAX2)
syntax of: 4.2. Namespace Syntax (XML in a Nutshell)
target (see target namespaces)
target namespaces: 16.3.1. Target Namespaces (XML in a Nutshell)
URIs: 4.2.3. Namespace URIs (XML in a Nutshell)
returned by SAX parsers: 25.3. SAX Features and Properties (XML in a Nutshell)
XSLT processors and: 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
XML 1.0 namespace: 10.7.1. XML Attributes (XML Schema)
XML specification for: 20.3.4. Namespaces (XML in a Nutshell)
XPath expressions and: 10.5. Namespaces and XPath Expressions (XML Schema)
XPointers and: 11.5. Namespaces (XML in a Nutshell)
XSLT and: 8.10. XSLT and Namespaces (XML in a Nutshell)
23.1. The XSLT Namespace (XML in a Nutshell)
namespace attribute: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
namespace-aware methods: 6.2. Namespaces (Java & XML)
namespace axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
3.2.6.2. Axis roll call (XSLT)
Namespace class: A.4.1.9. Namespace (Java & XML)
JDOM: 8.1.3. The Namespace Class (Java & XML)
namespace declarations, set of (context): 3.2.1. The Context (XSLT)
B.6. The XPath Context (XSLT)
namespace flag: 2.6.3. Namespace Feature Flags (SAX2)
Namespace node type: 6.1. XML Trees (Perl & XML)
namespace nodes (XPath): 3.1.7. Namespace Nodes (XSLT)
B.1.7. Namespace Nodes (XSLT)
selecting: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
namespace prefixes: 1.2.2.11. Namespaces (XSLT)
namespace nodes and: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
namespace-prefixes feature: 3.3.2. XMLReader Feature Flags (SAX2)
namespace URI: 3.3.5. Element Callbacks (Java & XML)
8.1.1.1. Example: Generating multiple output files (XSLT)
namespace-uri( ) function: C. XSLT and XPath Function Reference (XSLT)
namespace-uri( ) (XPath): 9.7.1. Node-Set Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
NAMESPACE_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
namespaceMappings object: 3.3.5. Element Callbacks (Java & XML)
Namespaces in XML
qualified/unqualified attributes: 10.4. Disruptive Attributes (XML Schema)
Namespaces in XML recommendation: 20.2. Annotated Sample Documents (XML in a Nutshell)
: (colon) character is reserved: 20.3.1. Global Syntax Structures (XML in a Nutshell)
impacting element/attribute names: 20.3.4. Namespaces (XML in a Nutshell)
namespace constraints: 20.4.3. Namespace Constraints (XML in a Nutshell)
schemas and: 16.1.2. Namespace Issues (XML in a Nutshell)
NamespaceSupport class: A.1.3.5. NamespaceSupport (Java & XML)
3.5.2.2. Turning objects into SAX events (SAX2)
5.1.3. The NamespaceSupport Class (SAX2)
A.2.5. The NamespaceSupport Class (SAX2)
NamespaceSupport class (SAX): 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
namespaceURI attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
namespaceURI parameter: B.4. Element Information Items (SAX2)
namespaceURI property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
naming
attributes: 2.3.3.1. Attribute lookup by name (SAX2)
2.6.2.2. Attribute naming (SAX2)
elements: 2.6.1. What Namespaces Do to XML (SAX2)
with namespaces: 2.6.2. Element and Attribute Naming with Namespaces (SAX2)
naming conventions, elements: 2.1.1.2. Elements (Java & XML)
naming conventions, importance of: 9.5.2. Initializing Global Variables (XSLT)
narrative-oriented DTDs: 3.9.2. Narrative-Oriented DTDs (XML in a Nutshell)
narrative-oriented XML documents
DocBook: 6.4. DocBook (XML in a Nutshell)
structure of: 6.2. Narrative Document Structures (XML in a Nutshell)
TEI (Text Encoding Initiative): 6.3. TEI (XML in a Nutshell)
national standard character sets: 5.6. ISO Character Sets (XML in a Nutshell)
native2ascii conversion tool (JDK): 5.8. Converting Between Character Sets (XML in a Nutshell)
natures of related resources: 14.3. Natures (XML in a Nutshell)
NDATA attribute: 4.3.2. The DTDHandler Interface (SAX2)
NDATA declaration: 3.6.1. Notations (XML in a Nutshell)
negating lists in user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
nesting of tags: 2.1.1.2. Elements (Java & XML)
Netscape: 9.2.1. Introduction to RSS (Perl & XML)
displaying XML in: 7.2.3. Netscape and Mozilla (XML in a Nutshell)
font configuration, determining: 8.6.3. Putting It All Together (Java and XSLT)
HTTP request: 8.3. Identifying the Browser (Java and XSLT)
Netscape Navigator User-Agent: 8.3. Identifying the Browser (Java and XSLT)
shipping RSS: 6.1. Rich Site Summary: RSS (SAX2)
XSLT support: 1.5. Web Browser Support for XSLT
new( ) XML::SAX::ParserFactory class method: 5.7.1. XML::SAX::ParserFactory (Perl & XML)
newSAXParser( ): 9.2.1.1. A look at the SAXParserFactory class (Java & XML)
newTransform( ): 23.4. TrAX (XML in a Nutshell)
newTransformer( ): 5.4.1. Templates API (Java and XSLT)
5.4.2. A Stylesheet Cache (Java and XSLT)
9.3.2.1. Working with the factory (Java & XML)
Next link: 8.1.1.1. Example: Generating multiple output files (XSLT)
nextSibling attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
nextSibling property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
nil values, defining: 11.3. Defining Nil (Null) Values (XML Schema)
nillable attribute: 11.3. Defining Nil (Null) Values (XML Schema)
NJStar Chinese word processor: 8.6.2.1. Creating the XML and XSLT (Java and XSLT)
NMTOKEN attribute: 4.3.1. The DeclHandler Interface (SAX2)
NMTOKEN attribute type: 3.3.1.2. NMTOKEN (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
values must be legal name tokens: 20.4.2. Validity Constraints (XML in a Nutshell)
NMTOKENS attribute: 4.3.1. The DeclHandler Interface (SAX2)
NMTOKENS attribute type: 3.3.1.3. NMTOKENS (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
values must be legal name tokens: 20.4.2. Validity Constraints (XML in a Nutshell)
NO_DATA_ALLOWED_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
node( ): 3.5.2. The Identity Transformation (Java and XSLT)
node test: 22.3.3. Node Tests (XML in a Nutshell)
XPath wildcard: 9.2.5. Wildcards (XML in a Nutshell)
node( ) method in XPath: 8.2. XPath (Perl & XML)
Node class (DOM): 7.1. DOM and Perl (Perl & XML)
7.2.4. Node (Perl & XML)
7.3. XML::DOM (Perl & XML)
7.4. XML::LibXML (Perl & XML)
node comparisons, DOM Level 3: 6.4.2. Node Comparisons (Java & XML)
Node interface: 5.2.3.3. Working with nodes (Java & XML)
A.2.1.14. Node (Java & XML)
Node interface (DOM): 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24. DOM Reference (XML in a Nutshell)
attributes for: 24.2. Object Reference (XML in a Nutshell)
methods for: 24.2. Object Reference (XML in a Nutshell)
node( ) node test: 3.2.5. Using Wildcards (XSLT)
B.2. XPath Node Tests (XSLT)
node-set datatype: 3.4. XPath Datatypes (XSLT)
converting to boolean values: 4.2.1.1. Converting to boolean values (XSLT)
node-set functions (XPath): 9.7.1. Node-Set Functions (XML in a Nutshell)
node sets in XPath: 22.2. Data Types (XML in a Nutshell)
node tests for XPath: 3.2.4.2. Selecting the text of an element (XSLT)
B.2. XPath Node Tests (XSLT)
node tests in location paths: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
22.3.3. Node Tests (XML in a Nutshell)
NodeFilter interface: 6.3.2.1. NodeIterator (Java & XML)
NodeIterator interface: 6.3.2.1. NodeIterator (Java & XML)
online resources: 6.3.2.2. TreeWalker (Java & XML)
NodeList class (DOM): 7.2.5. NodeList (Perl & XML)
NodeList interface: A.2.1.15. NodeList (Java & XML)
NodeList interface (DOM): 18.3.1. The NodeList Interface (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
attribute for: 24.2. Object Reference (XML in a Nutshell)
method for: 24.2. Object Reference (XML in a Nutshell)
nodeName attribute (Node interface): 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeName property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
nodes: 3.1. The XPath Data Model (XSLT)
grouping: 6.2. Grouping Nodes (XSLT)
illustrated in sample stylesheet: 3.5. The XPath View of an XML Document (XSLT)
types and (XPath): 3.1.1. The Root Node (XSLT)
B.1. XPath Node Types (XSLT)
XML trees: 6.1. XML Trees (Perl & XML)
XPath: 8.2. XPath (Perl & XML)
nodes, determining type of: 5.2.3.3. Working with nodes (Java & XML)
nodes in XML documents
DOM and: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
XPath and: 9.1. The Tree Structure of an XML Document (XML in a Nutshell)
XSLT and: 8.7. The Built-in Template Rules (XML in a Nutshell)
nodeType attribute (Node interface): 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeType property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
nodeValue attribute (Node interface): 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
nodeValue property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
noise data, pruning from DOM trees: 4.4.2. Pruning Noise Data from a DOM Tree (SAX2)
Nokia WAP Toolkit: 10.1. Wireless Technologies (Java and XSLT)
NO_MODIFICATION_ALLOWED_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
non-fatal errors: 3.4.2. Nonfatal Errors (Java & XML)
noncolonized names, datatypes for: 4.3.3.1. Tokenss (XML Schema)
nondeterministic content models: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
none value (display property): 12.5.4. Hidden Elements (XML in a Nutshell)
nonvalidating parsers: 2.4.1. SAX2 Feature Flags (SAX2)
normalize( ) (DOM)
Element interface: 24.2. Object Reference (XML in a Nutshell)
Node interface: 24.2. Object Reference (XML in a Nutshell)
normalize method for Element node: 7.2.8.2. Methods (Perl & XML)
normalize-space( ) function: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
C. XSLT and XPath Function Reference (XSLT)
normalize-space( ) (XPath): 9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
normalized strings: 4.2. Whitespace Processing (XML Schema)
normalizeSpace( ) function: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
not( ) function: C. XSLT and XPath Function Reference (XSLT)
not( ) (XPath): 9.7.3. Boolean Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
NOTATION attribute: 4.3.2. The DTDHandler Interface (SAX2)
NOTATION attribute type: 3.3.1.10. NOTATION (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
not available for empty elements: 20.4.2. Validity Constraints (XML in a Nutshell)
only one per element type: 20.4.2. Validity Constraints (XML in a Nutshell)
notation attributes: 20.4.2. Validity Constraints (XML in a Nutshell)
Notation class (DOM): 7.2.16. Notation (Perl & XML)
NOTATION declaration: 3.6. External Unparsed Entities and Notations (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
names must be unique: 20.4.2. Validity Constraints (XML in a Nutshell)
unparsed entities and: 20.4.2. Validity Constraints (XML in a Nutshell)
notation for DocumentType class (DOM): 7.2.3.1. Properties (Perl & XML)
Notation Information Items: B.12. Notation Information Items (SAX2)
Notation interface: A.2.1.16. Notation (Java & XML)
Notation interface (DOM): 18.4.1.3. Notation (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy for: 24.1. Object Hierarchy (XML in a Nutshell)
notation syntax of XML: 17.2.4. Notations (XML in a Nutshell)
notationDecl( ) callback function: 4.3.2. The DTDHandler Interface (SAX2)
notation_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers (Perl & XML)
notationName attribute (Entity interface): 24.2. Object Reference (XML in a Nutshell)
notationName property for Entity node: 7.2.15.1. Properties (Perl & XML)
NOTATION_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
notations: 4.3.3.4. Notations (XML Schema)
notations attribute (DocumentType interface): 24.2. Object Reference (XML in a Nutshell)
NOT_FOUND_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
NOT_SUPPORTED_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
NSFilter class: 6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
NTT DoCoMo: 10.1. Wireless Technologies (Java and XSLT)
null, setting handlers: 3.1.1. The XMLReader Interface (SAX2)
null values, defining: 11.3. Defining Nil (Null) Values (XML Schema)
NullPointerException class: 3.1.1. The XMLReader Interface (SAX2)
number datatype: 3.4. XPath Datatypes (XSLT)
converting to boolean values: 4.2.1.1. Converting to boolean values (XSLT)
number formatting: 3.4.1. Number Formatting (Java and XSLT)
number( ) function: C. XSLT and XPath Function Reference (XSLT)
number( ) (XPath): 9.7.4. Number Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
numbers, in predicates: 3.2.7.1. Numbers in predicates (XSLT)
numbers in XPath: 9.6.1. Numbers (XML in a Nutshell)
22.2. Data Types (XML in a Nutshell)
numbers_chinese.xslt: 8.6.3.2. XSLT stylesheets (Java and XSLT)
numbers_english.xslt: 8.6.3.2. XSLT stylesheets (Java and XSLT)
numbers_spanish.xslt: 8.6.3.2. XSLT stylesheets (Java and XSLT)
numeric character entities: 2.5. Entities (Perl & XML)
numeric datatypes: 4.4. Numeric Datatypes (XML Schema)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
regular expressions and: 6.5.2. Numeric and Float Types (XML Schema)
numeric functions (XPath): 9.7.4. Number Functions (XML in a Nutshell)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

OASIS DTD repository (xml.org): 3.10. Locating Standard DTDs (XML in a Nutshell)
OASIS group: 1.4.1. SAX1 (SAX2)
OASIS-Open: 3.7.2. Schemas (Perl & XML)
OASIS SGML/Open Catalog (SOCAT): 3.4. The EntityResolver Interface (SAX2)
OASIS Technical Committee: A.3.3. The RELAX NG Family (XML Schema)
object hierarchy (DOM): 24.1. Object Hierarchy (XML in a Nutshell)
object module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
object-oriented design and Russian doll design: 3.2.2. Russian Doll and Object-Oriented Design (XML Schema)
object-oriented XML schema languages: A.2.3. Object-Oriented XML Schema Languages (XML Schema)
object parsing style: 3.2.2. Parsing Styles (Perl & XML)
object reference (DOM Level 2 Core): 24.2. Object Reference (XML in a Nutshell)
object serialization and XML: 15.1.3. Object Serialization (XML in a Nutshell)
object tree parsers API: 1.1. Types of XML APIs (SAX2)
object values: 3.3.2. XMLReader Feature Flags (SAX2)
objects, turning into SAX events: 3.5.2.2. Turning objects into SAX events (SAX2)
OCR-A characters, Unicode block for: 26.3.23. Optical Character Recognition (XML in a Nutshell)
online resources: 0.3. Software and Versions (Java & XML)
0.5. Comments and Questions (Java & XML)
Ant: 3.6. Ant Documentation Stylesheet (Java and XSLT)
Castor: 15.2. Castor (Java & XML)
Cocoon source and binaries: 10.2.1. Source Code or Binaries (Java & XML)
CVS: 10.2.1. Source Code or Binaries (Java & XML)
DOM Level 2 modules: 6.3.6. Odds and Ends (Java & XML)
DOM Level 2 parsers: 6.3. DOM Level 2 Modules (Java & XML)
DOM specifications, Levels 1 and 2: 5.1. The Document Object Model (Java & XML)
DTD constraint models: 2.2.2.2. Multiple namespaces (Java & XML)
installation, SOAP servlet engines: 12.2.2.3. The router servlet and admin client (Java & XML)
JAXP: 9.1. API or Abstraction (Java & XML)
9.3. JAXP 1.1 (Java & XML)
Apache Xerces implementation: 9.2.1. Starting with SAX (Java & XML)
JAXP version 1.1: B. JAXP API Reference (Java and XSLT)
JDOM: 1.2.4.3. JDOM (Java and XSLT)
JSRs (Java Specification Requests): 7.4.1. JDOM as a JSR (Java & XML)
jUDDI: 13.2. UDDI (Java & XML)
LotusXSL: 1.4.1.2. LotusXSL (Java and XSLT)
Microsoft SOAP toolkit: 12.2.1.2. Isn't Microsoft a player? (Java & XML)
NodeIterator interface: 6.3.2.2. TreeWalker (Java & XML)
NJStar Chinese word processor: 8.6.2.1. Creating the XML and XSLT (Java and XSLT)
parsers: 1.3.2. A Parser (Java & XML)
phone simulators: 10.1. Wireless Technologies (Java and XSLT)
Range module: 6.3.3. Range (Java & XML)
RSS: 14.3.2. Creating an XML RSS Document (Java & XML)
SAX (Simple API for XML): 1.2.4.1. SAX (Java and XSLT)
SAXON: 1.4.1.3. SAXON (Java and XSLT)
SOAP: 12.1. Starting Out (Java & XML)
installation errors: 12.2.2.3. The router servlet and admin client (Java & XML)
Tomcat: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
Turbine: 8.3. Identifying the Browser (Java and XSLT)
Treewalker interface: 6.3.2.2. TreeWalker (Java & XML)
Unicode: 8.6.2. Encodings (Java and XSLT)
UP.SDK: 10.3.4. WAP and XML (Java & XML)
WAP: 10.3.4. WAP and XML (Java & XML)
specifications: 10.3.4. WAP and XML (Java & XML)
web publishing frameworks: 10.1. Selecting a Framework (Java & XML)
web services: 13.4.6. Where Do I Go From Here? (Java & XML)
WML: 10.3.4. WAP and XML (Java & XML)
WSDL: 13.3. WSDL (Java & XML)
13.4.4. WSDL (Java & XML)
Xalan: 2.1.1. An XSLT Example (Java and XSLT)
XML: 2.1. The Basics (Java & XML)
related technologies: 2.4. And More... (Java & XML)
XML Schema: 1.2.3. Validation (Java and XSLT)
XML Spy: 2.1.2. Trying It Out (Java and XSLT)
XPointer: 16.2. XPointer (Java & XML)
XSL: 10.3.2. Viewing PDFs from XML (Java & XML)
online validators: 3.1.4. Validating a Document (XML in a Nutshell)
Open Directory Project: 6.1. Rich Site Summary: RSS (SAX2)
open schemas, need for: 13.2. The Need for Open Schemas (XML Schema)
Opera User-Agent: 8.3. Identifying the Browser (Java and XSLT)
opening tags: 2.1.1.2. Elements (Java & XML)
Openwave Software Development Kit: 10.1. Wireless Technologies (Java and XSLT)
operating systems (OS), Java compatibility: 1.3.1. An Operating System and Java (Java & XML)
operators (XPath): B.4. XPath Operators (XSLT)
Optical Character Recognition Unicode block: 26.3.23. Optical Character Recognition (XML in a Nutshell)
optionally validating parsers: 2.4.1. SAX2 Feature Flags (SAX2)
or character (|): 6.4.4. Oring and Grouping (XML Schema)
Oracle XML Parser: 1.3.2. A Parser (Java & XML)
order attribute, <xsl:sort> element and: 6.1.2.2. Attributes (XSLT)
OrderProcessor class: 13.4.5. Writing a Client (Java & XML)
org.apache.crimson.tree.Xml-DocumentBuilder class: 4.4.1. SAX-to-DOM Consumers (SAX2)
org.apache.soap.Body: 13.4.1. A Messaging Service (Java & XML)
org.apache.soap.Envelope: 13.4.1. A Messaging Service (Java & XML)
org.dom4j.io.SAXContentHandler class: 4.4.1. SAX-to-DOM Consumers (SAX2)
org.dom4j.io.XMLWriter: 2.2.3. XMLWriter: an Event Consumer (SAX2)
org.jdom package: A.4.1. Package: org.jdom (Java & XML)
org.jdom.adapters package: A.4.2. Package: org.jdom.adapters (Java & XML)
org.jdom.input package: A.4.3. Package: org.jdom.input (Java & XML)
org.jdom.input.SAXHandler: 4.4.1. SAX-to-DOM Consumers (SAX2)
org.jdom.output package: A.4.4. Package: org.jdom.output (Java & XML)
org.w3c.dom package: A.2.1. Package: org.w3c.dom (Java & XML)
org.w3c.dom.Document objects: 5.2.2. DOM Parser Output (Java & XML)
org.w3c.dom.Node inferface: 5.4.2. Polymorphism and the Node Interface (Java & XML)
org.xml.sax: A.1.1. Package: org.xml.sax (Java & XML)
org.xml.sax package: 4.1.2.2. Namespaces (Java & XML)
1.4.1. SAX1 (SAX2)
1.5. Packages in the SAX2 API (SAX2)
A.1. The org.xml.sax Package (SAX2)
19. Simple API for XML (SAX) (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
ContentHandler interface and: 19.1. The ContentHandler Interface (XML in a Nutshell)
org.xml.sax.AttributeList class: 5.2. SAX1 Support (SAX2)
org.xml.sax.Attributes instance: 3.3.5. Element Callbacks (Java & XML)
org.xml.sax.ContentHandler: 2.2.2. What Are the SAX2 Event Handlers? (SAX2)
org.xml.sax.DocumentHandler class: 5.2. SAX1 Support (SAX2)
org.xml.sax.driver class: 3.2.1. The XMLReaderFactory Class (SAX2)
org.xml.sax.DTDHandler: 2.2.2. What Are the SAX2 Event Handlers? (SAX2)
org.xml.sax.ErrorHandler: 2.2.2. What Are the SAX2 Event Handlers? (SAX2)
org.xml.sax.ext package: 1.5. Packages in the SAX2 API (SAX2)
A.3. The org.xml.sax.ext Package (SAX2)
25.4. The org.xml.sax.ext Package (XML in a Nutshell)
org.xml.sax.ext.DeclHandler: 2.2.2. What Are the SAX2 Event Handlers? (SAX2)
org.xml.sax.ext.LexicalHandler: 2.2.2. What Are the SAX2 Event Handlers? (SAX2)
org.xml.sax.HandlerBase class: 9.2.1. Starting with SAX (Java & XML)
5.2. SAX1 Support (SAX2)
org.xml.sax.helpers package: A.1.3. Package: org.xml.sax.helpers (Java & XML)
1.5. Packages in the SAX2 API (SAX2)
A.2. The org.xml.sax.helpers Package (SAX2)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
org.xml.sax.helpers.AttributeListImpl class: 5.2. SAX1 Support (SAX2)
org.xml.sax.helpers.ParserAdapter class: 3.5.1. My Parser Doesn't Support SAX 2.0 (Java & XML)
5.2. SAX1 Support (SAX2)
org.xml.sax.helpers.ParserFactory class: 5.2. SAX1 Support (SAX2)
org.xml.sax.helpers.XMLFilterImpl class: 19.3. Filters (XML in a Nutshell)
org.xml.sax.helpers.XMLReaderAdapter class: 5.2. SAX1 Support (SAX2)
org.xml.sax.Parser class: 3.1. Pull Mode Event Production with XMLReader (SAX2)
5.2. SAX1 Support (SAX2)
org.xml.sax.Parser interface: 3.2. SAX Readers (Java & XML)
org.xml.sax.SAXException class: 2.5.1. SAX2 Exception Classes (SAX2)
3.4. The EntityResolver Interface (SAX2)
org.xml.sax.SAXNotRecognizedException class: 2.5.1. SAX2 Exception Classes (SAX2)
org.xml.sax.SAXNotSupportedException class: 2.5.1. SAX2 Exception Classes (SAX2)
org.xml.sax.SAXParseException class: 2.5.1. SAX2 Exception Classes (SAX2)
org.xml.sax.XMLFilter interface: 19.3. Filters (XML in a Nutshell)
org.xml.sax.XMLReader class: 3.1. Pull Mode Event Production with XMLReader (SAX2)
org.xml.sax.XMLReader interface
parse( ): 3.2.2. Parsing the Document (Java & XML)
origin( ): 11.7.7. origin( ) (XML in a Nutshell)
OS (operating systems), Java compatibility: 1.3.1. An Operating System and Java (Java & XML)
##other wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
outbound links: 10.5. Linkbases (XML in a Nutshell)
output
generating/controlling: 7.2.1.2. Generating output to initialize a variable (XSLT)
multiple files for, generating: 8.1.1.1. Example: Generating multiple output files (XSLT)
number formatting: 3.4.1. Number Formatting (Java and XSLT)
text formatting: 3.4.2. Text Formatting (Java and XSLT)
output encoding: B.3. Document Information Item (SAX2)
output escaping: 4.6.1. Implementing a String Replace Function (XSLT)
output formats: 6.6. Transformation and Presentation (XML in a Nutshell)
output streams
BufferedServletOutputStream class: 8.4.2. XSLT Transformation Filter (Java and XSLT)
ByteArrayOutputStream class: 8.4.2. XSLT Transformation Filter (Java and XSLT)
generating text to: D.12. How Do I Generate Some Text? (XSLT)
ServletOutputStream class: 8.4.1. Filter Overview (Java and XSLT)
output, when using XML::RSS module: 9.2.5. Off-the-Cuff Output (Perl & XML)
output, when writing XML text: 2.2.3.2. Concerns when writing XML text (SAX2)
OutputKeys class: A.3.2.2. OutputKeys (Java & XML)
ownerDocument attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
ownerDocument property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
ownerElement attribute (Attr interface): 24.2. Object Reference (XML in a Nutshell)
ownerElement property of Attr class (DOM): 7.2.9.1. Properties (Perl & XML)
OutputStreamWriter class: 8.6.2.1. Creating the XML and XSLT (Java and XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

-package flag: 15.2.2. Source Generation (Java & XML)
packages
Castor: 15.2. Castor (Java & XML)
com.sun.xml.parser: 9.1.1. Sun's JAXP and Parser (Java & XML)
for data binding: 15. Data Binding (Java & XML)
helma.xmlrpc: 11.2.1. XML-RPC Libraries (Java & XML)
javax.xml.parsers: 9.1. API or Abstraction (Java & XML)
A.3.1. Package: javax.xml.parsers (Java & XML)
javax.xml.transform: 9.3.2. The TrAX API (Java & XML)
A.3.2. Package: javax.xml.transform (Java & XML)
javax.xml.transform.dom: A.3.3. Package: javax.xml.transform.dom (Java & XML)
javax.xml.transform.sax: A.3.4. Package: javax.xml.transform.sax (Java & XML)
javax.xml.transform.stream: A.3.5. Package: javax.xml.transform.stream (Java & XML)
org.apache.xalan.xslt: 14.2.2. XSLT from a Servlet (Java & XML)
org.enhydra.zeus.binder: 15.3.2. Class Generation (Java & XML)
org.enhydra.zeus.result: 15.3.3. Marshalling and Unmarshalling (Java & XML)
org.enhydra.zeus.source: 15.3.3. Marshalling and Unmarshalling (Java & XML)
org.jdom: A.4.1. Package: org.jdom (Java & XML)
org.jdom.adapters: A.4.2. Package: org.jdom.adapters (Java & XML)
org.jdom.input: 7.1.3. Input and Output (Java & XML)
A.4.3. Package: org.jdom.input (Java & XML)
org.jdom.output: 7.1.3. Input and Output (Java & XML)
A.4.4. Package: org.jdom.output (Java & XML)
org.w3c.dom: 5.1.1. Language Bindings (Java & XML)
A.2.1. Package: org.w3c.dom (Java & XML)
org.w3c.dom.css: 6.3.4.3. Style (Java & XML)
org.w3c.dom.events: 6.3.4.1. Events (Java & XML)
org.w3c.dom.html: 6.3.5. HTML (Java & XML)
org.w3c.dom.ranges: 6.3.3. Range (Java & XML)
org.w3c.dom.stylesheets: 6.3.4.3. Style (Java & XML)
org.w3c.dom.traversal: 6.3.2. Traversal (Java & XML)
org.w3c.dom.views: 6.3.4.2. Views (Java & XML)
org.xml.sax: 3.1.2. Getting the SAX Classes and Interfaces (Java & XML)
4.1.2.2. Namespaces (Java & XML)
A.1.1. Package: org.xml.sax (Java & XML)
org.xml.sax.ext: 4.4. Even More Handlers (Java & XML)
A.1.2. Package: org.xml.sax.ext (Java & XML)
org.xml.sax.helpers: A.1.3. Package: org.xml.sax.helpers (Java & XML)
SAX: A.1. SAX 2.0 (Java & XML)
page format (see A4-sized pages)
9.3.7. PDF Files (XSLT)
page-sequence element (XSL-FO): 13.2. The Structure of an XSL-FO Document (XML in a Nutshell)
flowing content into pages: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
panels (tutorial pages): 9.1. About the Toot-O-Matic (XSLT)
generating: 9.5.5. Generating the Individual Panels (XSLT)
Paoli, Jean: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
param module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
parameter entities, XML specification for: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
parameter entity declarations: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
declaring: 20.4.2. Validity Constraints (XML in a Nutshell)
parameter entity references: 3.7. Parameter Entities (XML in a Nutshell)
3.7. Parameter Entities (XML in a Nutshell)
changing prefixes using: 4.4.1. Parameter Entity References for Namespace Prefixes (XML in a Nutshell)
connecting XHTML modules: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
defining, with document model modules: 7.3.3. Mixing Your Own XHTML (XML in a Nutshell)
external DTD subsets and: 3.7.3. External DTD Subsets (XML in a Nutshell)
in DTD only: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
including declarations in: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
internal subsets and: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
location in DTD is important: 20.3.1. Global Syntax Structures (XML in a Nutshell)
mixing applications into XHTML: 7.3.2. Mixing Your Applications into XHTML (XML in a Nutshell)
mixing XHTML into applications: 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
proper nesting with
conditional sections: 20.4.2. Validity Constraints (XML in a Nutshell)
markup declarations: 20.4.2. Validity Constraints (XML in a Nutshell)
parenthesized groups: 20.4.2. Validity Constraints (XML in a Nutshell)
redefining: 3.7.2. Redefining Parameter Entities (XML in a Nutshell)
syntax of: 3.7.1. Parameter Entity Syntax (XML in a Nutshell)
XLink elements and: 10.6. DTDs for XLinks (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
parameters
global: 4.4.3. Global Parameters (XSLT)
passing to template: 4.4. Parameters (XSLT)
single quotes and: 4.4.1. Defining a Parameter in a Template (XSLT)
parent axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
parent elements: 2.2.2.1. Parents and children (XML in a Nutshell)
selecting with .. (double period): 9.3.3. Selecting the Parent Element with .. (XML in a Nutshell)
22.3.1. Abbreviated Syntax (XML in a Nutshell)
parent nodes: 6.1. XML Trees (Perl & XML)
parentheses ( ) group character: 6.4.4. Oring and Grouping (XML Schema)
parentheses in element declarations: 3.2.6. Parentheses (XML in a Nutshell)
parenthesis (( )) in PYX notation: 4.5. XML::PYX (Perl & XML)
parentNode attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
parentNode property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
parse( ): 3.2.2. Parsing the Document (Java & XML)
5.2.2. DOM Parser Output (Java & XML)
5.2.2. DOM Parser Output (Java & XML)
invocation: 3.2.2. Parsing the Document (Java & XML)
SAXParser interface: 9.3.1.1. The road to SAX 2.0 (Java & XML)
parse( ) method: 3.2.1. Example: Well-Formedness Checker Revisited (Perl & XML)
5.7.3. SAX2 Parser Interface (Perl & XML)
parse( ) function: 2.5.1. SAX2 Exception Classes (SAX2)
3.1. Pull Mode Event Production with XMLReader (SAX2)
3.1.1. The XMLReader Interface (SAX2)
EntityResolver class, passing InputSource objects to: 3.1.2.2. Providing entity text (SAX2)
InputSource class and: 3.1.2. The InputSource Class (SAX2)
parse( ) (SAX): 19. Simple API for XML (SAX) (XML in a Nutshell)
using filters with: 19.3. Filters (XML in a Nutshell)
parse( ) (XMLReader interface): 25.1. The org.xml.sax Package (XML in a Nutshell)
parsed character data: 2.5. Entity References (XML in a Nutshell)
3.1.1. A Simple DTD Example (XML in a Nutshell)
parsed general entities: 3.5. External Parsed General Entities (XML in a Nutshell)
attribute values and: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
no recursion allowed: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
text declarations and: 5.3. Text Declarations (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
parsed spaces: 4.1. Lexical and Value Spaces (XML Schema)
parsefile( ) method: 3.2.2. Parsing Styles (Perl & XML)
7.3. XML::DOM (Perl & XML)
XML::Parser module, using: 3.2.1. Example: Well-Formedness Checker Revisited (Perl & XML)
parser configuration: 2.4. Producer-Side Validation (SAX2)
Parser interface: 3.2. SAX Readers (Java & XML)
A.1.1.11. Parser (Java & XML)
A.1.11. The Parser Interface (SAX2)
parser-level APIs: 1.1. Types of XML APIs (SAX2)
ParserAdapter class: 3.5.1. My Parser Doesn't Support SAX 2.0 (Java & XML)
A.1.3.6. ParserAdapter (Java & XML)
5.2. SAX1 Support (SAX2)
A.2.6. The ParserAdapter Class (SAX2)
ParserAdapter class (SAX): 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
ParserConfigurationException class: A.3.1.4. ParserConfigurationException (Java & XML)
ParserFactory class: 9.2.1. Starting with SAX (Java & XML)
A.1.3.7. ParserFactory (Java & XML)
5.2. SAX1 Support (SAX2)
A.2.7. The ParserFactory Class (SAX2)
parsers: 1.3.2. A Parser (Java & XML)
1.5. Keep in Mind... (Perl & XML)
3.1. XML Parsers (Perl & XML)
1. The Simple API for XML (SAX2)
advantages of SAX: 1.2.1. Stream-Based Processing (SAX2)
callback methods: 3.2.2. Parsing the Document (Java & XML)
distributions for SAX 2.0: 1.6. Some Popular SAX2 Parser Distributions (SAX2)
DOM: 5.2.1. Getting a DOM Parser (Java & XML)
DOM, and: 5.1.1. Language Bindings (Java & XML)
DOM Level 2, sources: 6.3. DOM Level 2 Modules (Java & XML)
event streams: 4.3. The Parser as Commodity (Perl & XML)
feature flags and: 2.4.1. SAX2 Feature Flags (SAX2)
features, proprietary: 4.1.3. Proprietary Properties and Features (Java & XML)
I/O streams as input: 3.2.3. Using an InputSource (Java & XML)
not reentrant: 3.5.2. The SAX XMLReader: Reused and Reentrant (Java & XML)
obtaining: 3.1.1. Obtaining a Parser (Java & XML)
online sources: 1.3.2. A Parser (Java & XML)
parsing styles: 3.2. XML::Parser (Perl & XML)
3.2.2. Parsing Styles (Perl & XML)
properties, proprietary: 4.1.3. Proprietary Properties and Features (Java & XML)
SAX 2.0: 1.4.2. SAX2 (SAX2)
installing: 1.7. Installing a SAX2 Parser (SAX2)
SAX 2 interface: 5.7.3. SAX2 Parser Interface (Perl & XML)
installing: 5.7.5. Installing Your Own Parser (Perl & XML)
SAX 2.0 not supported: 3.5.1. My Parser Doesn't Support SAX 2.0 (Java & XML)
standardization, lack of: 4.1. Properties and Features (Java & XML)
Sun version: 9.1.1. Sun's JAXP and Parser (Java & XML)
using: 3.4. Putting Parsers to Work (Perl & XML)
UTF-32 encoding and: 3.9.2.3. UTF-32 (Perl & XML)
vendor independence, and: 9.1.1. Sun's JAXP and Parser (Java & XML)
well-formedness checkers: 2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
writing: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
Xerces: 3.1.1. Obtaining a Parser (Java & XML)
XML parser classes: 3.1.1. Obtaining a Parser (Java & XML)
XML::Parser module and: 3.2. XML::Parser (Perl & XML)
XML::RSS module and: 9.2.2.1. Parsing (Perl & XML)
parsers, validating: 1.3. How XML Works (XML in a Nutshell)
1.3. How XML Works (XML in a Nutshell)
18.6. Parsing a Document with DOM (XML in a Nutshell)
associating schema documents with instance documents: 16.1. Overview (XML in a Nutshell)
16.2. Schema Basics (XML in a Nutshell)
parsing: 3. SAX (Java & XML)
exception handling: 3.2.2. Parsing the Document (Java & XML)
flow of events: 4.3.1. XMLFilters (Java & XML)
parent-child consistency in the pipeline: 4.5.3. Parsing on the Reader Instead of the Filter (Java & XML)
XML documents: 3.3.5. Element Callbacks (Java & XML)
parsing styles: 3.2. XML::Parser (Perl & XML)
3.2.2. Parsing Styles (Perl & XML)
particles (element roles): 7.4.1.1. Compositors and particles (XML Schema)
Pascal language: 1.4.4. Is SAX2 a "Standard"? (SAX2)
passive APIs: 1.1. Types of XML APIs (SAX2)
PassiveTeX library: 13.3.2. Generating the Finished Document (XML in a Nutshell)
<path> element (SVG): 8.1.2.1. Example: A library of trigonometric functions (XSLT)
pattern facet element: 16.6.2.5. Enforcing format (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
patterns: 6. Using Regular Expressions to Specify Simple Datatypes (XML Schema)
(see also regular expressions)
atoms in (see atoms in patterns)
commonly used: 6.5. Common Patterns (XML Schema)
constraining simple datatypes: 6.1. The Swiss Army Knife (XML Schema)
quantifiers in: 6.3. Quantifying (XML Schema)
simplest forms of: 6.2. The Simplest Possible Patterns (XML Schema)
#PCDATA keyword: 2.2.1.1. Elements (Java & XML)
3.1.1. A Simple DTD Example (XML in a Nutshell)
3.2.1. #PCDATA (XML in a Nutshell)
in narrative-oriented documents: 6.2. Narrative Document Structures (XML in a Nutshell)
PDF files
converting "Hello World" document into: 2.5.2. The Hello World PDF File (XSLT)
page formats and: 9.1. About the Toot-O-Matic (XSLT)
9.3.7. PDF Files (XSLT)
generating: 9.5.6. Generating the PDF Files (XSLT)
icons displaying: 9.3.1. Menu Panel (XSLT)
PDF format for XSL-FO documents: 13.5. Choosing Between CSS and XSL-FO (XML in a Nutshell)
peer-to-peer (P2P): 6.2.1. XML/Internet Versus Older Technologies (SAX2)
percent sign (%) in parameter entities: 3.3.2. XMLReader Feature Flags (SAX2)
4.3.1. The DeclHandler Interface (SAX2)
period (.) indicating self axis: 3.2.6.2. Axis roll call (XSLT)
period (.) wildcard character: 6.4.2. Wildcard (XML Schema)
Perl CGI scripts, generating XML output with: 14.1.2. Entering the Books (Java & XML)
Perl character classes: 6.4.3.1. Classical Perl character classes (XML Schema)
Perl language: 1.4.4. Is SAX2 a "Standard"? (SAX2)
Perl, using with XML: 1.1. Why Use Perl with XML? (Perl & XML)
namespaces: 10.1. Perl and XML Namespaces (Perl & XML)
perl-xml mailing list: 0.3.1. The perl-xml Mailing List (Perl & XML)
1.4. A Myriad of Modules (Perl & XML)
XML programming tools: 9.3. XML Programming Tools (Perl & XML)
PerlSAX: 3.4. Putting Parsers to Work (Perl & XML)
PerlSax handlers: 5.2. DTD Handlers (Perl & XML)
PHP, syntax of: 4.1.1. Other ContentHandler Methods (SAX2)
pi( ) Writer method: 3.8. XML::Writer (Perl & XML)
pictographic symbols, Unicode block for: 26.3.25. Miscellaneous Symbols (XML in a Nutshell)
pie charts
generating: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
legends for: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
Pinard, Francois: 5.8. Converting Between Character Sets (XML in a Nutshell)
pipeline stage: 4.5. XML Pipelines (SAX2)
pipelines: 4.3.1. XMLFilters (Java & XML)
4.5. XML Pipelines (SAX2)
events: 2.2.3.1. Event pipelines (SAX2)
using SAX parsers: 1.2.1. Stream-Based Processing (SAX2)
PIs (see Processing Instructions)
PIs (processing instructions)
DOM and: 5.2.3.3. Working with nodes (Java & XML)
SAX and: 3.3.3. Processing Instructions (Java & XML)
XML documents: 2.1.1. XML 1.0 (Java & XML)
pixels (relative unit of length): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
platform-dependent character sets: 5.7. Platform-Dependent Character Sets (XML in a Nutshell)
platform-specific technologies, drawbacks: 10.1.1. Stability (Java & XML)
<plist> element: 8.2. XPath (Perl & XML)
.png files, declaring with NDATA annotations: 4.3.2. The DTDHandler Interface (SAX2)
POD (Plain Old Documentation): 2.8. Processing Instructions and Other Markup (Perl & XML)
points in XPointers: 11.6. Points (XML in a Nutshell)
polymorphic accessors: 11.2.1. Defining Simple Types (XML Schema)
polymorphism: 4.3. Invoking Templates by Name (XSLT)
popContext( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
portability, extension functions and: 5.2.3.5. Solution #4: Use an extension function (XSLT)
portType element: 13.3. WSDL (Java & XML)
position( ) function: C. XSLT and XPath Function Reference (XSLT)
position( ) (XPath): 9.7. XPath Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
POST (HTTP) request: 6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
Post Schema Validation Infoset (PSVI): 21. Schemas Reference (XML in a Nutshell)
postal addresses
grouping: 6.2.1. Our First Attempt (XSLT)
sorting: 6.1.1. Our First Example (XSLT)
6.1.3. Another Example (XSLT)
po.xsdXMLSchema: 13.4.1. A Messaging Service (Java & XML)
P2P (peer-to-peer): 6.2.1. XML/Internet Versus Older Technologies (SAX2)
preceding axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
preceding-sibling axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
predefined character references: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
predefined datatypes: 4. Using Predefined Simple Datatypes (XML Schema)
used in schemas: 4.8. Back to Our Library (XML Schema)
predicates: 3.2.7. Predicates (XSLT)
in location paths: 9.4. Predicates (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
22.4. Predicates (XML in a Nutshell)
optional part of location steps: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
prefix attribute: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
prefix attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
prefix mapping: 3.3.4. Namespace Callbacks (Java & XML)
2.6.4. ContentHandler and Prefix Mappings (SAX2)
prefix property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
prefixed names in XPointers: 11.5. Namespaces (XML in a Nutshell)
prefixes
binding to URIs: 4.2.2. Binding Prefixes to URIs (XML in a Nutshell)
controlling namespace qualification: 16.3.2. Controlling Qualification (XML in a Nutshell)
default namespaces and: 4.2.4. Setting a Default Namespace with the xmlns Attribute (XML in a Nutshell)
20.3.4. Namespaces (XML in a Nutshell)
element names and: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
namespace constraints for: 20.4.3. Namespace Constraints (XML in a Nutshell)
using parameter entity references to change: 4.4.1. Parameter Entity References for Namespace Prefixes (XML in a Nutshell)
prefixes in namespace: 2.3. Namespaces (Perl & XML)
prefixes, namespace (see namespaces, prefixes)
preorder traversal: 8.6. Applying Templates with xsl:apply-templates (XML in a Nutshell)
presentation module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
presentational markup languages: 2.1. A Brief History of XML (Perl & XML)
markup, elements and structure and: 2.2. Markup, Elements, and Structure (Perl & XML)
Previous link: 8.1.1.1. Example: Generating multiple output files (XSLT)
previousSibling attribute (Node interface): 24.2. Object Reference (XML in a Nutshell)
previousSibling property for Node class (DOM): 7.2.4.1. Properties (Perl & XML)
primitive types for schemas: 16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
defining: 16.6.1. Defining New Simple Types (XML in a Nutshell)
procedural design: 4.6.1.1. Procedural design (XSLT)
procedural logic: 2.4. Producer-Side Validation (SAX2)
processContents attribute: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
associating schemas with instance documents: 11.1. Associating Schemas with Instance Documents (XML Schema)
open schemas and: 13.2.2. Wildcards (XML Schema)
processing-instruction( )
location step: 9.2.4. The comment( ), text( ), and processing-instruction( ) Location Steps (XML in a Nutshell)
node test: 22.3.3. Node Tests (XML in a Nutshell)
Processing Instruction Information Items: B.6. Processing Instruction Information Items (SAX2)
processing-instruction( ) node test: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
B.2. XPath Node Tests (XSLT)
processing instruction nodes
default template for: 8.7.3. The Default Template Rule for Comment and Processing Instruction Nodes (XML in a Nutshell)
XPath data model and: 22.1. The XPath Data Model (XML in a Nutshell)
processing instruction nodes (XPath): 3.1.6. Processing Instruction Nodes (XSLT)
B.1.6. Processing-Instruction Nodes (XSLT)
processing instructions: 0.6. Acknowledgments (Java & XML)
14.4. XML 1.0 Comments (XML Schema)
2.8. Processing Instructions (XML in a Nutshell)
?> closing: 3.1.6. Processing Instruction Nodes (XSLT)
notations for targets of: 3.6.3. Notations for Processing Instruction Targets (XML in a Nutshell)
parsers and: 17.2.3. Processing Instructions (XML in a Nutshell)
robots: 7.4.3. Robots (XML in a Nutshell)
selecting: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
web browser problems with: 7.1.3.1. The XML declaration and processing instructions (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
Processing Instructions (PIs): 2.8. Processing Instructions and Other Markup (Perl & XML)
node type: 6.1. XML Trees (Perl & XML)
processing_instruction( ) method: 5.1. SAX Event Handlers (Perl & XML)
5.7.2.1. Content event handlers (Perl & XML)
ProcessingInstruction class: A.4.1.10. ProcessingInstruction (Java & XML)
ProcessingInstruction class (DOM): 7.2.12.1. Properties (Perl & XML)
processingInstruction( ) function: 4.1.1. Other ContentHandler Methods (SAX2)
ProcessingInstruction interface: A.2.1.17. ProcessingInstruction (Java & XML)
ProcessingInstruction interface (DOM): 5.2.3.3. Working with nodes (Java & XML)
18.4.1.2. ProcessingInstruction (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
attributes for: 24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
processingInstruction( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
overriding, using filters: 19.3. Filters (XML in a Nutshell)
PROCESSING_INSTRUCTION_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
processName( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
processors (see XSLT processors)
processors (XML): 1.3. XML Processors (Perl & XML)
whitespace and: 1.6. XML Gotchas (Perl & XML)
producer-side validation: 2.4. Producer-Side Validation (SAX2)
handling errors for: 2.4.2. Handling Validity Errors (SAX2)
producer.parse ( ): 2.2.1. How Do the Parts Fit Together? (SAX2)
producers: 2.1. Producers and Consumers (SAX2)
producing events: 3. Producing SAX2 Events (SAX2)
programming languages, functional: 1.1. The Design of XSLT (XSLT)
programming models used with XML: 17. Programming Models (XML in a Nutshell)
programming tools (XML): 9.3. XML Programming Tools (Perl & XML)
prolog (XML document): 3.1.2. The Document Type Declaration (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
properties: 4.1.1. Setting Properties and Features (Java & XML)
of Cascading Style Sheets: 12.5. The Display Property (XML in a Nutshell)
controlling parser behavior for SAX: 19.2. SAX Features and Properties (XML in a Nutshell)
25.3. SAX Features and Properties (XML in a Nutshell)
DOM: 7.2. DOM Class Interface Reference (Perl & XML)
Attr class: 7.2.9.1. Properties (Perl & XML)
CharacterData class: 7.2.7.1. Properties (Perl & XML)
Document class: 7.2.1.1. Properties (Perl & XML)
DocumentType class: 7.2.3.1. Properties (Perl & XML)
Element node: 7.2.8.1. Properties (Perl & XML)
NamedNodeMap class: 7.2.6.1. Properties (Perl & XML)
Node class: 7.2.4.1. Properties (Perl & XML)
NodeList class: 7.2.5.1. Properties (Perl & XML)
Notation class: 7.2.16.1. Properties (Perl & XML)
ProcessingInstruction class: 7.2.12.1. Properties (Perl & XML)
DOM nodes: 4.1.2.4. DOM nodes and literal strings (Java & XML)
elements, distinction between textual and attribute values: 7.3. XMLProperties (Java & XML)
literal strings: 4.1.2.4. DOM nodes and literal strings (Java & XML)
of XSL-FO: 13.4. XSL-FO Properties (XML in a Nutshell)
properties and features, methods for setting: 4.1.1. Setting Properties and Features (Java & XML)
property and feature URIs: 4.1.1. Setting Properties and Features (Java & XML)
property files, Java: 7.2.1. Java Properties Files (Java & XML)
XML, conversion to: 7.2.2. Converting to XML (Java & XML)
property names, splitting on period (.) delimiters: 7.2.3. Creating XML with JDOM (Java & XML)
property objects: 3.3. Configuring XMLReader Behavior (SAX2)
property values, conversion to attributes: 7.2.3. Creating XML with JDOM (Java & XML)
propertyNames( ): 7.2.3. Creating XML with JDOM (Java & XML)
proprietary character sets: 5.7. Platform-Dependent Character Sets (XML in a Nutshell)
proprietary properties and features: 4.1.3. Proprietary Properties and Features (Java & XML)
PropsToXML class: 7.2. PropsToXML (Java & XML)
skeleton for the class: 7.2.2. Converting to XML (Java & XML)
pseudoattributes for xml-stylesheet processing instruction: 7.2.1.1. The required href and type pseudoattributes (XML in a Nutshell)
12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
pseudoclass selectors: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
pseudoelement selectors: 12.4.5. Pseudoelement Selectors (XML in a Nutshell)
PSVI (Post Schema Validation Infoset): 21. Schemas Reference (XML in a Nutshell)
defining simple types: 11.2.1. Defining Simple Types (XML Schema)
defining XLink attributes: 10.7.2. XLink Attributes (XML Schema)
object-oriented XML schema languages and: A.2.3. Object-Oriented XML Schema Languages (XML Schema)
serialization: B.3.1. PSVI Serialization (XML Schema)
public ID: 5.2.3.3. Working with nodes (Java & XML)
public identifiers: 2.1.1. XML 1.0 (Java & XML)
public IDs for entities: 4.3.1. The DeclHandler Interface (SAX2)
PUBLIC keyword: 2.1.1. XML 1.0 (Java & XML)
PUBLIC keyword (document type declaration): 3.1.2.1. Public IDs (XML in a Nutshell)
publicId attribute
DocumentType interface: 24.2. Object Reference (XML in a Nutshell)
Entity interface: 24.2. Object Reference (XML in a Nutshell)
publicId parameter: B.10. Document Type Declaration Information Item (SAX2)
publicId property
DocumentType class: 7.2.3.1. Properties (Perl & XML)
Entity node: 7.2.15.1. Properties (Perl & XML)
Notation class: 7.2.16.1. Properties (Perl & XML)
pull mode event producer
with XMLReader: 3.1. Pull Mode Event Production with XMLReader (SAX2)
with XSLT: 4.5.3.2. SAX in Pull-Mode with XSLT (SAX2)
pull-to-push adapter: 3.1. Pull Mode Event Production with XMLReader (SAX2)
punctuation characters, Unicode block for: 26.3.17. General Punctuation (XML in a Nutshell)
purchase orders
displaying total for: 7.2.1.3. Using format-number( ) to control output (XSLT)
illustrating document( ) function: 7. Combining XML Documents (XSLT)
purposes of related resources: 14.4. Purposes (XML in a Nutshell)
push mode event producers: 3.1. Pull Mode Event Production with XMLReader (SAX2)
3.5.2. Push Mode Event Production (SAX2)
with XSLT: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
pushContext( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
pushing data to clients: 14.3. Push Versus Pull (Java & XML)
Python language: 1.4.4. Is SAX2 a "Standard"? (SAX2)
PYX module: 4.5. XML::PYX (Perl & XML)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

Q names: 3.3.5. Element Callbacks (Java & XML)
qualified names: 2.3. Namespaces (Perl & XML)
4.3.3.2. Qualified names (XML Schema)
4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
defining: 4.4.1. Parameter Entity References for Namespace Prefixes (XML in a Nutshell)
XML specification for: 20.3.4. Namespaces (XML in a Nutshell)
qualified names, naming attributes and elements: 2.6.2. Element and Attribute Naming with Namespaces (SAX2)
qualified vs. unqualified attributes: 10.3. To Qualify Or Not to Qualify? (XML Schema)
quantifiers in patterns: 6.3. Quantifying (XML Schema)
quantum mechanics symbols, Unicode block for: 26.3.22. Miscellaneous Technical (XML in a Nutshell)
Query Language, XML: 1.4. The Evolution of XML (XML in a Nutshell)
querying support for XML documents: 1.1.3. Querying Support (XML Schema)
question mark (?) in PYX notation: 4.5. XML::PYX (Perl & XML)
&quot; entity, quoting attribute values and: D.1. How Do I Put Quotes Inside an Attribute Value? (Java and XSLT)
quotation marks: 2.1.1.3. Attributes (Java & XML)
quotes (see double quotes)
D.1. How Do I Put Quotes Inside an Attribute Value? (XSLT)
"Hello World" sample document
converting into
Java program: 2.5.3. The Hello World Java Program (XSLT)
PDF file: 2.5.2. The Hello World PDF File (XSLT)
SVG file: 2.5.1. The Hello World SVG File (XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

\r special character (carriage return): 6.4.1. Special Characters (XML Schema)
Ragget, Dave: 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
Rahtz, Sebastian: 13.3.2. Generating the Finished Document (XML in a Nutshell)
random access, providing for XML data: 1.3. Why Not to Choose SAX? (SAX2)
range( ): 11.7.1. The range( ) function (XML in a Nutshell)
range-inside( ): 11.7.2. The range-inside( ) function (XML in a Nutshell)
Range module: 6.3.3. Range (Java & XML)
online resources: 6.3.3. Range (Java & XML)
range-to( ): 11.7.3. The range-to( ) function (XML in a Nutshell)
ranges in user-defined character classes, defining: 6.4.3.3. User-defined character classes (XML Schema)
ranges in XPointers: 11.6. Points (XML in a Nutshell)
11.7. Ranges (XML in a Nutshell)
raw names: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
defining: 4.4.1. Parameter Entity References for Namespace Prefixes (XML in a Nutshell)
RDDL (Resource Directory Description Language): 0.. What This Book Covers (XML in a Nutshell)
14. Resource Directory Description Language (RDDL) (XML in a Nutshell)
document example: 14.2. RDDL Syntax (XML in a Nutshell)
namespace URIs and: 4.2.3. Namespace URIs (XML in a Nutshell)
namespace URLs and: 14. Resource Directory Description Language (RDDL) (XML in a Nutshell)
natures of related resources: 14.3. Natures (XML in a Nutshell)
purposes of related resources: 14.4. Purposes (XML in a Nutshell)
syntax for: 14.2. RDDL Syntax (XML in a Nutshell)
rddl:resource element: 14. Resource Directory Description Language (RDDL) (XML in a Nutshell)
RDF (Resource Description Framework): 0.. What This Book Covers (XML in a Nutshell)
7.4.1. RDF (XML in a Nutshell)
Dublin Core and: 4.1. The Need for Namespaces (XML in a Nutshell)
7.4.2. Dublin Core (XML in a Nutshell)
RDF (Resource Description Framework) Site Summary: 6.1. Rich Site Summary: RSS (SAX2)
RDF (Resources Description Framework) specification
element types and: 3.2.3. Where Have the Element Types Gone? (XML Schema)
W3C XML Schema's impact on: B.1.3. RDF (XML Schema)
Really Simple Syndication (RSS): 9.2.1. Introduction to RSS (Perl & XML)
recode (conversion tool): 5.8. Converting Between Character Sets (XML in a Nutshell)
recurrence modifiers: 2.2.1.1. Elements (Java & XML)
recursion: 1.1. The Design of XSLT (XSLT)
examples of: 4.6. Using Recursion to Do Most Anything (XSLT)
performing mathematics with: 7.2.1. An Aside: Doing Math with Recursion (XSLT)
recursion when processing XML: 3.5. XML::LibXML (Perl & XML)
recursive design: 4.6.1.2. Recursive design (XSLT)
7.2.1.1. Recursive design (XSLT)
recursive schemas: 3.2.1. Depth Versus Modularity? (XML Schema)
redefinition, schema inclusion with: 8.2. Schema Inclusion with Redefinition (XML Schema)
8.2. Schema Inclusion with Redefinition (XML Schema)
(see also xs:redefine element)
redirect namespace prefix: 8.1.1.1. Example: Generating multiple output files (XSLT)
refer attribute (xs:keyref element): 10.5. Namespaces and XPath Expressions (XML Schema)
references (entity): 2.5. Entities (Perl & XML)
region-after child element (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
region-before child element (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
region-body child element (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
region-end child element (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
region-start child element (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
registration of services: 13.2. UDDI (Java & XML)
13.4.2. Registering with UDDI (Java & XML)
regular expressions: 6. Using Regular Expressions to Specify Simple Datatypes (XML Schema)
(see also patterns)
specifying simple datatypes with: 6. Using Regular Expressions to Specify Simple Datatypes (XML Schema)
treating XML as text: 17.1.1. Treating XML as Text (XML in a Nutshell)
xs:pattern element and: 21.2. Schema Elements (XML in a Nutshell)
related resources: 14.2. RDDL Syntax (XML in a Nutshell)
natures of: 14.3. Natures (XML in a Nutshell)
purposes of: 14.4. Purposes (XML in a Nutshell)
relational operators in XPath: 9.4. Predicates (XML in a Nutshell)
9.6.2. Strings (XML in a Nutshell)
relative expressions: 3.2.3. Relative and Absolute Expressions (XSLT)
relative location paths: 22.3. Location Paths (XML in a Nutshell)
building compound location paths: 9.3.1. Building Compound Location Paths from Location Steps with / (XML in a Nutshell)
relative locators in XPath: 8.2. XPath (Perl & XML)
relative pathnames: 6.3.3. Source Code (Java and XSLT)
relative units of length (CSS properties): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
relative XPath expressions, defining constraints with: 9.2.1. Uniqueness (XML Schema)
relative XPointers: 11.7.5. Relative XPointers (XML in a Nutshell)
RELAX NG schema language: A.1.3. Intrusive Modeling Tools (XML Schema)
A.3.3. The RELAX NG Family (XML Schema)
1.1. The Benefits of XML (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
16.1. Overview (XML in a Nutshell)
sample application: A.7. RELAX NG (XML Schema)
RelaxNG: 3.7.2. Schemas (Perl & XML)
remote method invocation (see RMI)
Remote Procedure Call (RPC): 6.2.1. XML/Internet Versus Older Technologies (SAX2)
remote resources (extended links): 10.4. Extended Links (XML in a Nutshell)
third-party links and: 10.5. Linkbases (XML in a Nutshell)
removeAttribute( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
removeAttribute method for Element node: 7.2.8.2. Methods (Perl & XML)
removeAttributeNode( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
removeAttributeNode method for Element node: 7.2.8.2. Methods (Perl & XML)
removeAttributeNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
removeAttributeNS method for Element node: 7.2.8.2. Methods (Perl & XML)
removeChild( ): 6.1.2. Modifying a DOM Tree (Java & XML)
removeChild( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
removeEvent( ): 11.3.1. A Shared Handler (Java & XML)
11.3.1. A Shared Handler (Java & XML)
removeNamedItem class for Node class (DOM): 7.2.6.2. Methods (Perl & XML)
removeNamedItem( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
removeNamedItemNS class for Node class (DOM): 7.2.6.2. Methods (Perl & XML)
removeNamedItemNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
Renderer.java: 7.4. Servlet Implementation (Java and XSLT)
RenderX formatting program (XEP): 13.3.2. Generating the Finished Document (XML in a Nutshell)
replaceChild( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
replaceChild method for Node class (DOM): 7.2.4.2. Methods (Perl & XML)
replaceData( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
replaceData method for CharacterData class (DOM): 7.2.7.2. Methods (Perl & XML)
repository of DTDs: 3.10. Locating Standard DTDs (XML in a Nutshell)
ReqHandler.java: 7.4. Servlet Implementation (Java and XSLT)
ReqHandlerRegistry.java: 7.4. Servlet Implementation (Java and XSLT)
RequestDispatcher class: 4.1.5. The Hybrid Approach (Java and XSLT)
RequestHandler: 4.3. XSLT and EJB (Java and XSLT)
#REQUIRED attribute: 4.3.1. The DeclHandler Interface (SAX2)
#REQUIRED default declaration: 3.3.2. Attribute Defaults (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
value must be assigned to attributes: 20.4.2. Validity Constraints (XML in a Nutshell)
reserved characters, escaping: 2.1.1.4. Entity references and constants (Java & XML)
reset( ) function: 5.1.3. The NamespaceSupport Class (SAX2)
resolveEntity( ), avoiding errors with: 4.5.1. Return Values from an EntityResolver (Java & XML)
resolve_entity( ) method: 5.7.2.2. Entity resolver (Perl & XML)
resolveIDREFS template: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
Resource Description Framework (see RDF)
Resource Description Framework (RDF) Site Summary: 6.1. Rich Site Summary: RSS (SAX2)
Resource Directory Description Language (see RDDL)
resource element (RDDL): 14. Resource Directory Description Language (RDDL) (XML in a Nutshell)
resource elements: 10.4.3. Local Resources (XML in a Nutshell)
resources and RDF documents: 7.4.1. RDF (XML in a Nutshell)
restrictions
applying new ones to datatypes: 5.1.2. Multiple Restrictions and Fixed Attribute (XML Schema)
derivation by (see derivation methods, by restriction)
redefining attribute/element groups: 8.2.2.2. Restriction (XML Schema)
of substitution groups: 12.1.4.2. Restricting substitution groups (XML Schema)
Result interface: 5.2.1. JAXP 1.1 Implementation (Java and XSLT)
Result interface: A.3.2.3. Result (Java & XML)
5.3.2. JAXP I/O Design (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
result tree: 2.1. XSLT Introduction (Java and XSLT)
result-tree fragment: B.5. Datatypes (XSLT)
reusable attributes/elements
creating: 7.4.1.2. Element and attribute groups (XML Schema)
8. Creating Building Blocks (XML Schema)
reusable schemas, creating: 13. Creating Extensible Schemas (XML Schema)
Rich Site Summary (see RSS)
6.1. Rich Site Summary: RSS (SAX2)
Rich Site Summary (RSS): 9.2.1. Introduction to RSS (Perl & XML)
Ritchie, Dennis: 2.2. Transforming Hello World (XSLT)
RMI (remote method invocation): 11.1.1. What Is RMI? (Java & XML)
method calls: 11.2.3. Writing the Server (Java & XML)
registry: 11.1.1. What Is RMI? (Java & XML)
RPC, compared to: 11.1. RPC Versus RMI (Java & XML)
robots processing instruction: 7.4.3. Robots (XML in a Nutshell)
role attribute (Xlink): 10.3. Link Semantics (XML in a Nutshell)
role attributes for arcs: 10.4.2.3. Arc roles (XML in a Nutshell)
root( ) module: 8.2. XPath (Perl & XML)
root element: 14.3.2. Creating an XML RSS Document (Java & XML)
2.2.2.2. The root element (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
determining name of: 20.4.2. Validity Constraints (XML in a Nutshell)
in narrative-oriented documents: 6.2. Narrative Document Structures (XML in a Nutshell)
of TEI documents: 6.3. TEI (XML in a Nutshell)
vs. root node: 9.1. The Tree Structure of an XML Document (XML in a Nutshell)
of XSL-FO documents: 13.2. The Structure of an XSL-FO Document (XML in a Nutshell)
WML: 10.3.1. A WML Example (Java and XSLT)
root elements: 2.1.1.1. The root element (Java & XML)
2.5. Entities (Perl & XML)
root interface (DOM Core): 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
root location path: 9.2.1. The Root Location Path (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
root node
default template for: 8.7.2. The Default Template Rule for Element and Root Nodes (XML in a Nutshell)
vs. root element: 9.1. The Tree Structure of an XML Document (XML in a Nutshell)
XPath data model and: 22.1. The XPath Data Model (XML in a Nutshell)
root node (XPath): 3.1.1. The Root Node (XSLT)
B.1.1. The Root Node (XSLT)
XML data tree: 2.1.1. An XSLT Example (Java and XSLT)
2.2.1. XML Tree Data Structure (Java and XSLT)
round( ) function: C. XSLT and XPath Function Reference (XSLT)
round( ) (XPath): 9.7. XPath Functions (XML in a Nutshell)
9.7.4. Number Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
RPC (Remote Procedure Call): 6.2.1. XML/Internet Versus Older Technologies (SAX2)
RPC (remote procedure calls): 11.1.2. What Is RPC? (Java & XML)
handlers, registering: 11.2.3. Writing the Server (Java & XML)
limitations: 11.1.2. What Is RPC? (Java & XML)
method calls: 11.2.3. Writing the Server (Java & XML)
RMI, compared to: 11.1. RPC Versus RMI (Java & XML)
SOAP, using: 12.3.2. An RPC Service (Java & XML)
RSS (Rich Site Summary): 14.3.1. Rich Site Summary (Java & XML)
9.2.1. Introduction to RSS (Perl & XML)
6.1. Rich Site Summary: RSS (SAX2)
applications, building with: 6.1.3. Building Applications with RSS (SAX2)
channels: 14.3.1. Rich Site Summary (Java & XML)
data model for: 6.1.1. Data Model for RSS Classic (SAX2)
online resources: 14.3.2. Creating an XML RSS Document (Java & XML)
parsing events, consuming and producing: 6.1.2. Consuming and Producing RSS Parsing Events (SAX2)
RSS file creation: 14.3.2. Creating an XML RSS Document (Java & XML)
RSS (Rich Style Sheets): 14.3.2. Creating an XML RSS Document (Java & XML)
RssConsumer class: 6.1.2. Consuming and Producing RSS Parsing Events (SAX2)
RssHandler interface: 6.1.2. Consuming and Producing RSS Parsing Events (SAX2)
Ruby Annotation (W3C specification): 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
ruby module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
rule-based logic: 2.4. Producer-Side Validation (SAX2)
rule-based XML schema languages: A.2.1. Rule-Based XML Schema Languages (XML Schema)
rules
for built-in templates: 2.4.5. Built-in Template Rules (XSLT)
for XML documents: 1.2.2. XML Document Rules (XSLT)
Russian doll design
defining attributes using: 13.1.1.2. Attributes (XML Schema)
defining elements using: 13.1.1.1. Elements (XML Schema)
13.1.1.1. Elements (XML Schema)
documenting schemas: 14.1.2. Think Globally (XML Schema)
object orientation and: 3.2.2. Russian Doll and Object-Oriented Design (XML Schema)
Russian language, Unicode block for: 26.3.7. Cyrillic (XML in a Nutshell)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

\s character class: 6.4.3.1. Classical Perl character classes (XML Schema)
SAF (Schema Adjunct Framework): 14.2. The W3C XML Schema Annotation Element (XML Schema)
foreign attributes and: 14.3. Foreign Attributes (XML Schema)
Sanskrit language, Unicode block for: 26.3.11. Devanagari (XML in a Nutshell)
save( ): 7.3. XMLProperties (Java & XML)
SAX 2.1: 1.4. The Evolution of XML (XML in a Nutshell)
SAX parsers: 3.1. Pull Mode Event Production with XMLReader (SAX2)
SAX (Simple API for XML): 1.2.1. Low-Level APIs (Java & XML)
3. SAX (Java & XML)
3.4. Putting Parsers to Work (Perl & XML)
5. SAX (Perl & XML)
0.. What This Book Covers (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
19. Simple API for XML (SAX) (XML in a Nutshell)
1.2.3. DOM and SAX (XSLT)
APIs and: 1. The Simple API for XML (SAX2)
Attributes interface: 25.1. The org.xml.sax Package (XML in a Nutshell)
AttributesImpl class: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
classes: 3.1.2. Getting the SAX Classes and Interfaces (Java & XML)
ContentHandler interface: 19.1. The ContentHandler Interface (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
controlling parser behavior with URIs: 19.2. SAX Features and Properties (XML in a Nutshell)
25.3. SAX Features and Properties (XML in a Nutshell)
DBI and: 9.3.2. Further Ruminations on DBI and SAX (Perl & XML)
DeclHandler interface: 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
DefaultHandler class: 19.1. The ContentHandler Interface (XML in a Nutshell)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
documentation: 3.1.2. Getting the SAX Classes and Interfaces (Java & XML)
drivers for non-XML sources: 5.4. Drivers for Non-XML Sources (Perl & XML)
DTDHandler interface: 25.1. The org.xml.sax Package (XML in a Nutshell)
EntityResolver interface: 25.1. The org.xml.sax Package (XML in a Nutshell)
ErrorHandler interface: 25.1. The org.xml.sax Package (XML in a Nutshell)
exceptions, thrown by DOM parsers: 5.4.3. DOM Parsers Throwing SAX Exceptions (Java & XML)
external entity resolution: 5.3. External Entity Resolution (Perl & XML)
features of: 19.2. SAX Features and Properties (XML in a Nutshell)
25.3. SAX Features and Properties (XML in a Nutshell)
filters: 4.3.1. XMLFilters (Java & XML)
19.3. Filters (XML in a Nutshell)
HandlerBase class: 9.2.1. Starting with SAX (Java & XML)
handlers: 3.3. Content Handlers (Java & XML)
5.5. A Handler Base Class (Perl & XML)
DTD: 5.2. DTD Handlers (Perl & XML)
event: 5.1. SAX Event Handlers (Perl & XML)
hierarchical character: 5.1.3.2. SAX siblings (Java & XML)
history of: 1.4. A Short History of SAX (SAX2)
InputSource class: 25.1. The org.xml.sax Package (XML in a Nutshell)
InputSource vs. URI: 3.2.3. Using an InputSource (Java & XML)
Javadocs: 3.1.2. Getting the SAX Classes and Interfaces (Java & XML)
JAXP, versions supported under: 9.1.2. The Old and the New (Java & XML)
LexicalHandler interface: 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
limitations with XML: 6.1. Changes (Java & XML)
Locator interface: 25.1. The org.xml.sax Package (XML in a Nutshell)
LocatorImpl class: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
methods called by parser in ContentHandler: 19.1. The ContentHandler Interface (XML in a Nutshell)
NamespaceSupport class: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
nondeterministic code details: 19.1. The ContentHandler Interface (XML in a Nutshell)
org.xml.sax package: 19. Simple API for XML (SAX) (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
org.xml.sax.ext package: 25.4. The org.xml.sax.ext Package (XML in a Nutshell)
org.xml.sax.helpers package: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
org.xml.sax.XMLReader interface: 3.2. SAX Readers (Java & XML)
instantiating: 3.2.1. Instantiating a Reader (Java & XML)
parser interface: 5.7.3. SAX2 Parser Interface (Perl & XML)
ParserAdapter class: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
ParserFactory class: 9.2.1. Starting with SAX (Java & XML)
parsing a document with: 19. Simple API for XML (SAX) (XML in a Nutshell)
properties of: 19.2. SAX Features and Properties (XML in a Nutshell)
25.3. SAX Features and Properties (XML in a Nutshell)
reasons for using: 5.1.3.3. Why use SAX at all? (Java & XML)
reference: 25. SAX Reference (XML in a Nutshell)
Release 2.0, parsers, not supported by: 3.5.1. My Parser Doesn't Support SAX 2.0 (Java & XML)
SAX1 vs. SAX2: 19. Simple API for XML (SAX) (XML in a Nutshell)
25. SAX Reference (XML in a Nutshell)
SAXException class: 19. Simple API for XML (SAX) (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
SAXNotRecognizedException class: 19.2. SAX Features and Properties (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
25.4. The org.xml.sax.ext Package (XML in a Nutshell)
SAXNotSupportedException class: 19.2. SAX Features and Properties (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
SAXParseException class: 25.1. The org.xml.sax Package (XML in a Nutshell)
sequential model: 5.1.3.1. SAX is sequential (Java & XML)
specification for: 1.2.4.4. The Simple API for XML (SAX) (XSLT)
standardization: 7.4.2. SAX and DOM as Standards (Java & XML)
TrAX and: 23.4. TrAX (XML in a Nutshell)
uses: 5.1.3.3. Why use SAX at all? (Java & XML)
Version 1.0: 3.2. SAX Readers (Java & XML)
vs. DOM: 19.1. The ContentHandler Interface (XML in a Nutshell)
web publishing frameworks, support by: 10.1.2. Integration with Other XML Tools and APIs (Java & XML)
XML-RPC server, driver for: 11.2.3. Writing the Server (Java & XML)
XMLFilter interface: 25.1. The org.xml.sax Package (XML in a Nutshell)
XMLFilterImpl class: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
XMLReader interface: 19. Simple API for XML (SAX) (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
XMLReaderAdapter class: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
XMLReaderFactory class: 19. Simple API for XML (SAX) (XML in a Nutshell)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
XML::SAX module and: 5.7. XML::SAX: The Second Generation (Perl & XML)
XSLT, limitations in use with: 5.1.3.2. SAX siblings (Java & XML)
SAXBuilder class: 7.1.3. Input and Output (Java & XML)
A.4.3.4. SAXBuilder (Java & XML)
SAXException class: 3.3.2. The Beginning and the End of a Document (Java & XML)
A.1.1.12. SAXException (Java & XML)
2.4.2. Handling Validity Errors (SAX2)
2.5.1. SAX2 Exception Classes (SAX2)
3.1.1. The XMLReader Interface (SAX2)
A.1.12. SAXException (SAX2)
19. Simple API for XML (SAX) (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
error and diagnostics when using: 2.5.3. Errors and Diagnostics (SAX2)
SAXExceptions: 3.3.2. The Beginning and the End of a Document (Java & XML)
SAXNot SupportedExceptions: 4.1.2.2. Namespaces (Java & XML)
SAXNotRecognizedException: A.1.13. SAXNotRecognizedException (SAX2)
SAXNotRecognizedException class: A.1.1.13. SAXNotRecognizedException (Java & XML)
19.2. SAX Features and Properties (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
25.4. The org.xml.sax.ext Package (XML in a Nutshell)
SAXNotRecognizedExceptions: 4.1.2.2. Namespaces (Java & XML)
SAXNotSupportedException: A.1.14. SAXNotSupportedException (SAX2)
SAXNotSupportedException class: A.1.1.14. SAXNotSupportedException (Java & XML)
19.2. SAX Features and Properties (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
SAXON Java XSLT engine: 4.5.3. The javax.xml.transform.sax Package (SAX2)
Saxon processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
extension elements, defining for: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
extension functions and: 8.2. Extending the Saxon Processor (XSLT)
global parameters and: 4.4.3.4. Saxon (XSLT)
sorting and: 8.2. Extending the Saxon Processor (XSLT)
SAXON (XSLT processor): 8.3. Stylesheet Processors (XML in a Nutshell)
SAXOutputter: 7.1.3. Input and Output (Java & XML)
7.2.4. Outputting XML with JDOM (Java & XML)
8.1.3. The Namespace Class (Java & XML)
A.4.4. Package: org.jdom.output (Java & XML)
SAXParseException: A.1.15. SAXParseException (SAX2)
SAXParseException class: A.1.1.15. SAXParseException (Java & XML)
2.5.3. Errors and Diagnostics (SAX2)
Locator class and: 4.1.2. The Locator Interface (SAX2)
SAXParseException class (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
SAXParseExceptions: 3.4. Error Handlers (Java & XML)
SAXParser class: 9.2.1.2. Working with the SAXParser class (Java & XML)
A.3.1.5. SAXParser (Java & XML)
SAXParser interface
parse( ): 9.3.1.1. The road to SAX 2.0 (Java & XML)
SAXParserFactory class: 9.2.1.1. A look at the SAXParserFactory class (Java & XML)
A.3.1.6. SAXParserFactory (Java & XML)
JAXP v. 1.1: 9.3.1. Updating the Standards (Java & XML)
SAXResult class: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
sax.SAXCount class: 2.10. Checking Documents for Well-Formedness (XML in a Nutshell)
3.1.4. Validating a Document (XML in a Nutshell)
SAXTransformerFactory class: 4.5.3. The javax.xml.transform.sax Package (SAX2)
4.5.3.2. SAX in Pull-Mode with XSLT (SAX2)
SAXTreeViewer class: 3.2. SAX Readers (Java & XML)
3.2.1. Instantiating a Reader (Java & XML)
Scalable Vector Graphics (see entries at SVG)
1.2.4.9. Scalable Vector Graphics (SVG) (XSLT)
Scheduler class: 11.3.1. A Shared Handler (Java & XML)
import statement: 11.3.1. A Shared Handler (Java & XML)
returning events: 11.3.1. A Shared Handler (Java & XML)
sorting algorithm: 11.3.1. A Shared Handler (Java & XML)
SchedulerClient class: 11.3.3. A Useful Client (Java & XML)
schema adornments (SAF): 14.2. The W3C XML Schema Annotation Element (XML Schema)
schema-based XML-RPC, defining: 11.2.1. Defining Simple Types (XML Schema)
schema extensions and error messages: B.3.3. Schema Extensions: Error Messages (XML Schema)
Schema for Object-Oriented XML (SOX) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
schema languages: A.1. What Is a XML Schema Language? (XML Schema)
1.1. The Benefits of XML (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
schema validators
guessing element types: 11.2. Defining Element Types (XML Schema)
locating schemas: 11.1. Associating Schemas with Instance Documents (XML Schema)
schema (XML): 2.11. Schemas (Perl & XML)
3.7.2. Schemas (Perl & XML)
schemaLocation attribute: 2.1.2. Namespaces (Java & XML)
8.2. Schema Inclusion with Redefinition (XML Schema)
schema.net (DTD repository): 3.10. Locating Standard DTDs (XML in a Nutshell)
schemas: 0.. What This Book Covers (XML in a Nutshell)
16. XML Schemas (XML in a Nutshell)
1.2.2.8. Document Type Definitions (DTDs) and XML Schemas (XSLT)
abstract attribute: 16.9.1. Abstract Elements and Types (XML in a Nutshell)
adding constraints on datatypes: 5.5. Back to Our Library (XML Schema)
annotation elements in: 16.2.2. Annotations (XML in a Nutshell)
associating, with instance documents: 11.1. Associating Schemas with Instance Documents (XML Schema)
attribute declarations and: 16.2.5. Attribute Declarations (XML in a Nutshell)
attributes (see attributes)
complex types and: 16.4. Complex Types (XML in a Nutshell)
constructing: 16.2. Schema Basics (XML in a Nutshell)
controlling namespace qualification: 16.3.2. Controlling Qualification (XML in a Nutshell)
creating: 2.2. Our First Schema (XML Schema)
document organization: 16.2.1. Document Organization (XML in a Nutshell)
documenting: 14. Documenting Schemas (XML Schema)
element content
any type: 16.8. Allowing Any Content (XML in a Nutshell)
empty elements: 16.5. Empty Elements (XML in a Nutshell)
mixed: 16.7. Mixed Content (XML in a Nutshell)
simple: 16.6. Simple Content (XML in a Nutshell)
elements (see elements)
elements of: 21.2. Schema Elements (XML in a Nutshell)
enforcing element uniqueness: 16.9.4. Uniqueness and Keys (XML in a Nutshell)
extensible: 13. Creating Extensible Schemas (XML Schema)
facets and: 16.6.2. Facets (XML in a Nutshell)
final attribute: 16.9.2. The Final Attribute (XML in a Nutshell)
fixed attribute: 16.9.3. Setting fixed Facets (XML in a Nutshell)
flat design style: 2.2. Our First Schema (XML Schema)
2.3.3. Flat Design, Global Components (XML Schema)
global components: 2.3.3. Flat Design, Global Components (XML Schema)
importing, with no namespaces: 10.9. Importing Schemas with No Namespaces (XML Schema)
inclusion: 8.1. Schema Inclusion (XML Schema)
isolating simple/complex type definitions: 8.4. Simplifying the Library (XML Schema)
using xs:include and xs:redefine: 10.10. Chameleon Design (XML Schema)
with redefinition: 8.2. Schema Inclusion with Redefinition (XML Schema)
instance documents (see instance documents)
keys and references: 16.9.4.2. Keys and references (XML in a Nutshell)
lexical spaces: 4.1. Lexical and Value Spaces (XML Schema)
lists and: 16.6.2.6. Lists (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
locating: 11.1. Associating Schemas with Instance Documents (XML Schema)
using multiple documents: 16.8.1. Using Multiple Documents (XML in a Nutshell)
namespace behavior of imported components: 10.8. Namespace Behavior of Imported Components (XML Schema)
namespace declarations: 10.2. Namespace Declarations (XML Schema)
namespaces and: 16.1.2. Namespace Issues (XML in a Nutshell)
16.3. Working with Namespaces (XML in a Nutshell)
21.1. The Schema Namespaces (XML in a Nutshell)
open: 13.2. The Need for Open Schemas (XML Schema)
patterns used in: 6.6. Back to Our Library (XML Schema)
qualified/unqualified attributes and elements: 10.3. To Qualify Or Not to Qualify? (XML Schema)
reference: 21. Schemas Reference (XML in a Nutshell)
referencing other namespaces: 10.6. Referencing Other Namespaces (XML Schema)
regular expressions used in: 6.6. Back to Our Library (XML Schema)
simple types: 16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
defining: 16.6.1. Defining New Simple Types (XML in a Nutshell)
specification for: 1.2.4.3. XML Schemas (XSLT)
splitting components: 13.1.3. Splitting Schema Components (XML Schema)
targetNamespace attribute and: 16.3.1. Target Namespaces (XML in a Nutshell)
unions and: 16.6.2.7. Unions (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
valid/invalid documents and: 1.1. The Benefits of XML (XML in a Nutshell)
value spaces: 4.1. Lexical and Value Spaces (XML Schema)
vs. DTDs: 16.1.1. Schemas Versus DTDs (XML in a Nutshell)
Schematron: 3.7.2. Schemas (Perl & XML)
Schematron schema language: A.2.1. Rule-Based XML Schema Languages (XML Schema)
A.3.4. Schematron (XML Schema)
1.1. The Benefits of XML (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
16.1. Overview (XML in a Nutshell)
annotations and: 16.2.2.2. The xs:appinfo element (XML in a Nutshell)
sample application: A.8. Schematron (XML Schema)
xs:appinfo element and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
schemes: 2.4.1. SAX2 Feature Flags (SAX2)
EntityResolver interface and: 3.4. The EntityResolver Interface (SAX2)
scripting languages, writing extensions and: 8.1.2.2. Example: Writing extensions in other languages (XSLT)
scripting module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
search( ): 6.3.2.1. NodeIterator (Java & XML)
search methods on the web: 7.4. Prospects for Improved Web-Search Methods (XML in a Nutshell)
section indexes, generating: 9.5.4. Generating the Section Indexes (XSLT)
section menu (tutorials): 9.3.3. Section Menu (XSLT)
select attribute
<xsl:for-each> element and: 4.2.3.1. <xsl:for-each> example (XSLT)
<xsl:param> element and: 4.4.1. Defining a Parameter in a Template (XSLT)
<xsl:sort> element and: 6.1.2.2. Attributes (XSLT)
selecting
attributes: 3.2.4.1. Selecting attributes (XSLT)
comments: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
namespace nodes: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
processing instructions: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes (XSLT)
text of element: 3.2.4.2. Selecting the text of an element (XSLT)
selector programs for stream processing: 4.4. Stream Applications (Perl & XML)
selectors for Cascading Style Sheets: 12.4. Selectors (XML in a Nutshell)
self axis: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
22.3.2. Axes (XML in a Nutshell)
3.2.6.2. Axis roll call (XSLT)
semicolons (;): 3.1. XML Parsers (Perl & XML)
sequences in element declarations: 3.2.3. Sequences (XML in a Nutshell)
Sergeant, Matt: 4.5. XML::PYX (Perl & XML)
5.7. XML::SAX: The Second Generation (Perl & XML)
8.2. XPath (Perl & XML)
9.3. XML Programming Tools (Perl & XML)
serialization: 5.2.3. DOMSerializer (Java & XML)
3.5.2. Push Mode Event Production (SAX2)
DOM node types: 5.2.3.2. Launching serialization (Java & XML)
recursion: 5.2.3.2. Launching serialization (Java & XML)
serialization, PSVI: B.3.1. PSVI Serialization (XML Schema)
serialization spaces: 4.1. Lexical and Value Spaces (XML Schema)
serialize( ): 5.2.3.1. Getting started (Java & XML)
5.2.3.2. Launching serialization (Java & XML)
invocation: 5.2.4. The Results (Java & XML)
serializeNode( ): 5.2.3.2. Launching serialization (Java & XML)
SerializerTest class: 5.2.1. Getting a DOM Parser (Java & XML)
server-side image map module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
servers: 1.1. Why Use Perl with XML? (Perl & XML)
server.xml file: 10.2.2. Configuring the Servlet Engine (Java & XML)
service element: 13.3. WSDL (Java & XML)
service registry: 13.1. Web Services (Java & XML)
ServletConfig class: 6.2.4. Servlet API Highlights (Java and XSLT)
ServletContext class: 6.2.4. Servlet API Highlights (Java and XSLT)
6.3.3. Source Code (Java and XSLT)
ServletOutputStream class: 8.4.1. Filter Overview (Java and XSLT)
ServletRequestWrapper class: 8.4.1. Filter Overview (Java and XSLT)
ServletResponse interface: 8.4.1. Filter Overview (Java and XSLT)
ServletResponseWrapper class: 8.4.1. Filter Overview (Java and XSLT)
servlets
API: 6.2.4. Servlet API Highlights (Java and XSLT)
classes: 6.2.4. Servlet API Highlights (Java and XSLT)
client identification: 10.3.2. Servlets and WML (Java and XSLT)
compared to XML-RPC: 11.4.3. To Servlet or Not To Servlet (Java & XML)
definition: 6.2.2. Deployment Descriptor (Java and XSLT)
deployment: 4.1.2. Servlets as CGI Replacements (Java and XSLT)
design: 6.3.1. Design (Java and XSLT)
development: 10. Web Publishing Frameworks (Java & XML)
DOM and XSLT, using: 6.3. Another Servlet Example (Java and XSLT)
engine configuration for Cocoon: 10.2.2. Configuring the Servlet Engine (Java & XML)
filters: 8.4. Servlet Filters (Java and XSLT)
drawbacks: 8.4.3. Closing Thoughts on Filters (Java and XSLT)
Version 2.3 wrapper classes: 8.4.1. Filter Overview (Java and XSLT)
XSLT transformation: 8.4.2. XSLT Transformation Filter (Java and XSLT)
LanguageDemo.java: 8.6.3.3. Web page and servlet (Java and XSLT)
session tracking using: 8.2.1. Servlet Session-Tracking API (Java and XSLT)
stylesheets, location: 6.3.6. Locating Stylesheets with Initialization Parameters (Java and XSLT)
syntax: 6.1. Servlet Syntax (Java and XSLT)
threading: 6.5. Servlet Threading Issues (Java and XSLT)
SingleThreadModel interface: 6.5.1. Servlet Threading Model (Java and XSLT)
User-Agent, ascertaining, code for: 8.3. Identifying the Browser (Java and XSLT)
WML implementation: 10.3.3.4. Servlet implementation (Java and XSLT)
XSLT from: 14.2.2. XSLT from a Servlet (Java & XML)
session identifier: 8.2.1. Servlet Session-Tracking API (Java and XSLT)
session tracking: 8.2. Session Tracking Without Cookies (Java and XSLT)
without cookies: 8.2.1. Servlet Session-Tracking API (Java and XSLT)
XSLT, using: 8.2.2. Session Tracking with Java and XSLT (Java and XSLT)
Session-tracking code: 8.2.2. Session Tracking with Java and XSLT (Java and XSLT)
Session-tracking XSLT stylesheet: 8.2.2. Session Tracking with Java and XSLT (Java and XSLT)
set*( ) method: 3.3. Configuring XMLReader Behavior (SAX2)
setAttribute( ): 6.1.1. Creating a New DOM Tree (Java & XML)
7.2.3. Creating XML with JDOM (Java & XML)
9.3.2.1. Working with the factory (Java & XML)
setAttribute( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
setAttribute method for Element node: 7.2.8.2. Methods (Perl & XML)
setAttributeNode( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
setAttributeNode method for Element node: 7.2.8.2. Methods (Perl & XML)
setAttributeNodeNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
setAttributeNodeNS method for Element node: 7.2.8.2. Methods (Perl & XML)
setAttributeNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
setAttributeNS method for Element node: 7.2.8.2. Methods (Perl & XML)
setContentHandler( ): 3.3. Content Handlers (Java & XML)
3.3.9. The Results (Java & XML)
setContentHandler( ) (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
setBufferSize( ): 8.4.2. XSLT Transformation Filter (Java and XSLT)
setDocumentLocator( ), Locator instance: 3.5.3. The Misplaced Locator (Java & XML)
set_document_locator( ) method: 5.7.2.1. Content event handlers (Perl & XML)
setDocumentLocator( ) callback method: 4.1.1. Other ContentHandler Methods (SAX2)
setDocumentLocator( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
setDriver( ): 11.2.3. Writing the Server (Java & XML)
11.2.4. Writing the Client (Java & XML)
ClassNotFoundException: 11.2.3. Writing the Server (Java & XML)
setDTDHandler( ): 3.3. Content Handlers (Java & XML)
setDTDHandler( ) (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
setEncoding( ) function: 3.1.2.2. Providing entity text (SAX2)
setEndAfter( ): 6.3.3. Range (Java & XML)
setEntityResolver( ): 3.3. Content Handlers (Java & XML)
setEntityResolver( ) (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
setErrorHandler( ): 3.3. Content Handlers (Java & XML)
3.4. Error Handlers (Java & XML)
setErrorHandler( ) (SAX): 25.1. The org.xml.sax Package (XML in a Nutshell)
setErrorListener( ): 9.3.2.1. Working with the factory (Java & XML)
setExpandEntities( ): 8.1.2. The EntityRef Class (Java & XML)
setFactory( ): 8.2.2. Building with Custom Classes (Java & XML)
setFeature( ): 4.1.1. Setting Properties and Features (Java & XML)
4.1.2.2. Namespaces (Java & XML)
9.3.1.2. Changes in SAX classes (Java & XML)
setFeature( ) function: 2.4.1. SAX2 Feature Flags (SAX2)
3.1.1. The XMLReader Interface (SAX2)
namespace feature flags and: 2.6.3. Namespace Feature Flags (SAX2)
setFeature( ) (SAX): 19.2. SAX Features and Properties (XML in a Nutshell)
setLocale( ) function: 5.2. SAX1 Support (SAX2)
setNamedItem class for Node class (DOM): 7.2.6.2. Methods (Perl & XML)
setNamedItem( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
setNamedItemNS class for Node class (DOM): 7.2.6.2. Methods (Perl & XML)
setNamedItemNS( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
SetNameSpaceProcessing( ): 4.1.2.2. Namespaces (Java & XML)
setParent( ): 4.3.1. XMLFilters (Java & XML)
setParent( ) (SAX): 19.3. Filters (XML in a Nutshell)
setProperty( ): 4.1.1. Setting Properties and Features (Java & XML)
9.3.1.2. Changes in SAX classes (Java & XML)
setProperty( ) function: 3.1.1. The XMLReader Interface (SAX2)
3.2.1. The XMLReaderFactory Class (SAX2)
setProperty( ) (SAX): 19.2. SAX Features and Properties (XML in a Nutshell)
25.4. The org.xml.sax.ext Package (XML in a Nutshell)
setRootPart( ): 13.4.1. A Messaging Service (Java & XML)
sets, manipulating: 6.4.3.3. User-defined character classes (XML Schema)
setStartBefore( ): 6.3.3. Range (Java & XML)
setSystemId( ): 5.3.1. System Identifiers, Files, and URLs (Java and XSLT)
3.1.2.2. Providing entity text (SAX2)
5.3.3. JAXP Stream I/O (Java and XSLT)
6.3.3. Source Code (Java and XSLT)
setText( ): 7.2.3. Creating XML with JDOM (Java & XML)
element, invocation on: 8.1.1. The Text Class (Java & XML)
SGML and XML: 1.4. The Evolution of XML (XML in a Nutshell)
6.1. SGML's Legacy (XML in a Nutshell)
SGML Formal Public Identifiers: 4.3.1. The DeclHandler Interface (SAX2)
SGML (Standard Generalized Markup Language): 2. An XML Recap (Perl & XML)
1.2.1. XML's Heritage (XSLT)
Sharpe, Peter: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
Shift_JIS encoding: 5.4. XML-Defined Character Sets (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
show attribute (Xlink): 10.2.1. xlink:show (XML in a Nutshell)
Show classes: 4.1.2. Servlets as CGI Replacements (Java and XSLT)
showErrorPage( ): 6.3.3. Source Code (Java and XSLT)
showPage( ): 6.3.3. Source Code (Java and XSLT)
Showtimes XSLT: 10.3.3.5. XSLT stylesheets (Java and XSLT)
sibling elements: 2.2.2.1. Parents and children (XML in a Nutshell)
sibling nodes: 6.1. XML Trees (Perl & XML)
Signatures, XML: 1.4. The Evolution of XML (XML in a Nutshell)
Silverstream: 13.4.4. WSDL (Java & XML)
Simple API for XML (see SAX)
3.4. Putting Parsers to Work (Perl & XML)
0.. What This Book Covers (XML in a Nutshell)
1.2.3. DOM and SAX (XSLT)
simple content and schemas: 16.6. Simple Content (XML in a Nutshell)
simple content models: 2.2. Our First Schema (XML Schema)
creating: 7.3.1. Creation of Simple Content Models (XML Schema)
empty content models as: 7.6.1.1. As simple content models (XML Schema)
defining: 2.2. Our First Schema (XML Schema)
derivation by extension: 7.3.2.1. Derivation by extension (XML Schema)
derivation by restriction: 7.3.2.2. Derivation by restriction (XML Schema)
simple datatypes: 2.2. Our First Schema (XML Schema)
4. Using Predefined Simple Datatypes (XML Schema)
complications with: 5.4. Some Oddities of Simple Types (XML Schema)
controlling derivations: 12.2.4. Simple Types (XML Schema)
creating: 5. Creating Simple Datatypes (XML Schema)
defining: 2.2. Our First Schema (XML Schema)
11.2.1. Defining Simple Types (XML Schema)
globally: 13.1.1.2. Attributes (XML Schema)
extensibility features of different styles: 13.1.1.2. Attributes (XML Schema)
included schemas: 8.1. Schema Inclusion (XML Schema)
example of: 8.4. Simplifying the Library (XML Schema)
redefining: 8.2.1. Redefining of Simple and Complex Types (XML Schema)
specifying, using regular expressions: 6. Using Regular Expressions to Specify Simple Datatypes (XML Schema)
used in schemas: 4.8. Back to Our Library (XML Schema)
vs. complex datatypes: 7.1. Simple Versus Complex Types (XML Schema)
simple links: 10.1. Simple Links (XML in a Nutshell)
Simple Object Access Protocol (see SOAP)
Simple Object Access Protocol (SOAP): 9.4. SOAP::Lite (Perl & XML)
15.1.2. Communications (XML in a Nutshell)
simple-page-master element (XSL-FO): 13.3. Laying Out the Master Pages (XML in a Nutshell)
simple types for schemas: 16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
defining: 16.6.1. Defining New Simple Types (XML in a Nutshell)
facet elements for: 21.2. Schema Elements (XML in a Nutshell)
SimpleJaxp.java: 5.2.1. JAXP 1.1 Implementation (Java and XSLT)
SimpleSaxon.java: 5.1.3. SAXON Implementation (Java and XSLT)
SimpleXalan1.java: 5.1.2. Xalan 1 Implementation (Java and XSLT)
SimpleXPathViewer class: 8.3.4. Endgame (Java & XML)
single quotes (` ')
controlling: D.13. How Do I Control Angle Brackets and Quote Marks in My Output? (XSLT)
literal values and: 4.5. Variables (XSLT)
parameters and: 4.4.1. Defining a Parameter in a Template (XSLT)
placing inside attribute value: D.1. How Do I Put Quotes Inside an Attribute Value? (XSLT)
replacing one single quote with two: 4.6.1. Implementing a String Replace Function (XSLT)
SingleThreadModel interface: 6.5.1. Servlet Threading Model (Java and XSLT)
6.5.1. Servlet Threading Model (Java and XSLT)
skeleton XSLT stylesheet: 2.2.4. A Skeleton Stylesheet (Java and XSLT)
skipped entities and SAX: 3.5.1. My Parser Doesn't Support SAX 2.0 (Java & XML)
skipped_entity( ) method: 5.7.2.1. Content event handlers (Perl & XML)
skippedEntity( ) function: 4.1.1. Other ContentHandler Methods (SAX2)
slash (/): 3.1.1. The Root Node (XSLT)
beginning absolute XPath expressions: 3.2.3. Relative and Absolute Expressions (XSLT)
slash (\) escape character: 6.2. The Simplest Possible Patterns (XML Schema)
special characters: 6.4.1. Special Characters (XML Schema)
user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
slashes (/): 2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
well-formed documents and: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
XPath expressions: 8.2. XPath (Perl & XML)
Slavic languages, Unicode block for: 26.3.7. Cyrillic (XML in a Nutshell)
SOAP: 6.2.2. Roles for Java in XML Messaging (SAX2)
SOAP-RPC calls, necessary operations: 12.3.3. An RPC Client (Java & XML)
SOAP (Simple Object Access Protocol): 1.2.3. XML-Based Applications (Java & XML)
12. SOAP (Java & XML)
9.4. SOAP::Lite (Perl & XML)
15.1.2. Communications (XML in a Nutshell)
Apache Axis: 12.2.1.3. What's Axis? (Java & XML)
Apache implementation: 12.2. Setting Up (Java & XML)
Apache, stackTrace element: 12.4.2. Better Error Handling (Java & XML)
basic components: 12.1. Starting Out (Java & XML)
custom parameters: 12.4.1. Custom Parameter Types (Java & XML)
encodingStyle attribute: 12.4.1. Custom Parameter Types (Java & XML)
differences in implementations: 12.2.1.3. What's Axis? (Java & XML)
DOMFaultListener: 12.4.2. Better Error Handling (Java & XML)
encoding: 12.1.2. Encoding (Java & XML)
envelope: 12.1.1. The Envelope (Java & XML)
error handling: 12.4.2. Better Error Handling (Java & XML)
IBM SOAP4J: 12.2.1.1. What about IBM SOAP4J? (Java & XML)
installation: 12.2.2. Installation (Java & XML)
admin client: 12.2.2.3. The router servlet and admin client (Java & XML)
clients: 12.2.2.1. The client (Java & XML)
online resources: 12.2.2.3. The router servlet and admin client (Java & XML)
12.2.2.3. The router servlet and admin client (Java & XML)
router servlets: 12.2.2.3. The router servlet and admin client (Java & XML)
invocation: 12.1.3. Invocation (Java & XML)
Microsoft implementation: 12.2.1.2. Isn't Microsoft a player? (Java & XML)
online resources: 12.1. Starting Out (Java & XML)
RPC calls: 12.3.2. An RPC Service (Java & XML)
RPC clients: 12.3.3. An RPC Client (Java & XML)
RPC services: 12.3.2.1. Code artifacts (Java & XML)
deploying: 12.3.2.3. Deploying the service (Java & XML)
updating: 12.3.2.3. Deploying the service (Java & XML)
system design: 12.3. Getting Dirty (Java & XML)
SOAP::Lite module: 9.4. SOAP::Lite (Perl & XML)
SOAPMappingRegistry class: 12.4.1. Custom Parameter Types (Java & XML)
SOAPWiz: 13.4.4. WSDL (Java & XML)
SOCAT (OASIS SGML/Open Catalog): 3.4. The EntityResolver Interface (SAX2)
sockets network API: 1.4.4. Is SAX2 a "Standard"? (SAX2)
software development, testing and debugging: 9.2. Testing and Debugging
software development environments: 9. Development Environment, Testing, and Performance (Java and XSLT)
sorting: 6. Sorting and Grouping Elements (XSLT)
(see also grouping)
elements: 6. Sorting and Grouping Elements (XSLT)
postal addresses: 6.1.1. Our First Example (XSLT)
6.1.3. Another Example (XSLT)
Saxon processor and: 8.2. Extending the Saxon Processor (XSLT)
with document( ) function: 7.4.1. The document( ) Function and Sorting (XSLT)
XML documents: 7.4.1. The document( ) Function and Sorting (XSLT)
source code samples and CDATA sections: 2.6. CDATA Sections (XML in a Nutshell)
Source interface: 5.2.1. JAXP 1.1 Implementation (Java and XSLT)
A.3.2.4. Source (Java & XML)
5.3.1. System Identifiers, Files, and URLs (Java and XSLT)
5.3.2. JAXP I/O Design (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
Source parameter (SAX parser interface): 5.7.3. SAX2 Parser Interface (Perl & XML)
sourcecode, online resources: 0.3. Software and Versions (Java & XML)
SourceLocator interface: A.3.2.5. SourceLocator (Java & XML)
SOX (Schema for Object-Oriented XML) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
spaces, replacing whitespace with: 4.2. Whitespace Processing (XML Schema)
spacing in XML: 2.4. Spacing (Perl & XML)
Spacing Modifier Letters Unicode block: 26.3.4. Spacing Modifier Letters (XML in a Nutshell)
Spanish language, sorting and: 8.2. Extending the Saxon Processor (XSLT)
special attributes: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
special characters (atoms): 6.4.1. Special Characters (XML Schema)
special characters entity set: 5.10. Character References (XML in a Nutshell)
26.2. HTML4 Entity Sets (XML in a Nutshell)
26.2. HTML4 Entity Sets (XML in a Nutshell)
specifications: 1.2.4. XML Standards (XSLT)
XPath, reference guide to: B. XPath Reference (XSLT)
XSLT
extensions and: 8. Extending XSLT (XSLT)
reference guide to: A. XSLT Reference (XSLT)
specified attribute (Attr interface): 24.2. Object Reference (XML in a Nutshell)
specified property of Attr class (DOM): 7.2.9.1. Properties (Perl & XML)
Sperberg-McQueen, C.M.: 1.4. The Evolution of XML (XML in a Nutshell)
SplashScreenServlet.java: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
Tomcat, deploying to: 6.2.3. Deploying SplashScreenServlet to Tomcat (Java and XSLT)
web.xml: 6.2.2. Deployment Descriptor (Java and XSLT)
splitText( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
splitText method of Text class (DOM): 7.2.10.1. Methods (Perl & XML)
splitting schema components: 13.1.3. Splitting Schema Components (XML Schema)
SQL statements: 4.6.1. Implementing a String Replace Function (XSLT)
square brackets ([ ]): 2.5. Entities (Perl & XML)
square brackets [ ] enclosing predicates: 3.2.7. Predicates (XSLT)
St. Laurent, Simon: A.3.2. The W3C XML Schema Family (XML Schema)
stacks, writing XML document checkers: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
stages (pipeline): 4.5. XML Pipelines (SAX2)
standalone declaration: 2.9.2. standalone (XML in a Nutshell)
external DTD subsets and: 3.1.3. Internal DTD Subsets (XML in a Nutshell)
setting value to 'no': 20.4.2. Validity Constraints (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
standard-completeness of parsers: 3.2. XML::Parser (Perl & XML)
Standard Generalized Markup Language (see SGML)
Standard Generalized Markup Language (SGML): 1.2.1. XML's Heritage (XSLT)
standards (see specifications)
start-point( ): 11.6. Points (XML in a Nutshell)
start-tags: 2.2. Elements, Tags, and Character Data (XML in a Nutshell)
attributes and: 2.3. Attributes (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.3. Document Body (XML in a Nutshell)
start_cdata( ) event handler: 5.1. SAX Event Handlers (Perl & XML)
5.7.2.3. Lexical event handlers (Perl & XML)
startCDATA( ) function: 4.2. The LexicalHandler Interface (SAX2)
startDocument( ): 3.3.2. The Beginning and the End of a Document (Java & XML)
start_document( ) method: 5.1. SAX Event Handlers (Perl & XML)
5.7.2.1. Content event handlers (Perl & XML)
startDocument( ) function: 4.1.1. Other ContentHandler Methods (SAX2)
startDocument( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
19.1. The ContentHandler Interface (XML in a Nutshell)
start_dtd( ) event handler: 5.7.2.3. Lexical event handlers (Perl & XML)
startDTD( ) function: 4.2. The LexicalHandler Interface (SAX2)
startElement( ): 3.3.5. Element Callbacks (Java & XML)
3.3.5. Element Callbacks (Java & XML)
callback report: 3.3.4. Namespace Callbacks (Java & XML)
start_element( ) method: 5.1. SAX Event Handlers (Perl & XML)
5.7.2.1. Content event handlers (Perl & XML)
startElement( ) function: 2.3.2. Example: Elements and Text (SAX2)
2.3.2. Example: Elements and Text (SAX2)
2.3.3.2. Attribute lookup by index (SAX2)
2.3.4. Essential ContentHandler Callbacks (SAX2)
2.6.4. ContentHandler and Prefix Mappings (SAX2)
attributeDecl( ) and: 4.3.1. The DeclHandler Interface (SAX2)
NamespaceSupport class and: 5.1.3. The NamespaceSupport Class (SAX2)
using for elements and naming: 2.6.1. What Namespaces Do to XML (SAX2)
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags (SAX2)
startElement( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
using filters with: 19.3. Filters (XML in a Nutshell)
start_entity( ) event handler: 5.7.2.3. Lexical event handlers (Perl & XML)
startEntity( ) function: 4.2. The LexicalHandler Interface (SAX2)
starting resources: 10.1. Simple Links (XML in a Nutshell)
startPrefixMapping( ): 3.3.4. Namespace Callbacks (Java & XML)
start_prefix_mapping( ) method: 5.7.2.1. Content event handlers (Perl & XML)
startPrefixMapping( ) function: 2.6.3. Namespace Feature Flags (SAX2)
2.6.4. ContentHandler and Prefix Mappings (SAX2)
2.6.4. ContentHandler and Prefix Mappings (SAX2)
3.3.2. XMLReader Feature Flags (SAX2)
startPrefixMapping( ) (SAX): 19.1. The ContentHandler Interface (XML in a Nutshell)
starts-with( ) function: C. XSLT and XPath Function Reference (XSLT)
starts-with( ) (XPath): 9.7. XPath Functions (XML in a Nutshell)
9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
startTag( ) Writer method: 3.8. XML::Writer (Perl & XML)
<state> element: 7.4.1. The document( ) Function and Sorting (XSLT)
state information: 1.3.1. Web Development Challenges (Java and XSLT)
stateless protocols: 4.2.2. Model-View-Controller (Java and XSLT)
static content generation: 10. Web Publishing Frameworks (Java & XML)
Sterin, Ilya: 5.4. Drivers for Non-XML Sources (Perl & XML)
9.3. XML Programming Tools (Perl & XML)
store( ): 7.3. XMLProperties (Java & XML)
7.3.1. Storing XML (Java & XML)
7.3.4. Backtracking (Java & XML)
storyboarding: 7.1.2. Screen Flow (Java and XSLT)
strategies in XML processing: 3.3. Stream-Based Versus Tree-Based Processing (Perl & XML)
stream-based processing: 1.2.1. Stream-Based Processing (SAX2)
stream-based processing for parsers: 3.3. Stream-Based Versus Tree-Based Processing (Perl & XML)
stream validator in Ælfred parsers: 1.6.1. Ælfred2 (SAX2)
streaming parsers API: 1.1. Types of XML APIs (SAX2)
StreamResult class: 9.3.2.2. Transforming XML (Java & XML)
streams: 4.1. Working with Streams (Perl & XML)
applications: 4.4. Stream Applications (Perl & XML)
event: 4. Event Streams (Perl & XML)
JAXP I/O: 5.3.3. JAXP Stream I/O (Java and XSLT)
Streams class (example): 5.3.3. JAXP Stream I/O (Java and XSLT)
parsing style: 3.2.2. Parsing Styles (Perl & XML)
StreamSource class: 5.2.1. JAXP 1.1 Implementation (Java and XSLT)
9.3.2.2. Transforming XML (Java & XML)
5.3.3. JAXP Stream I/O (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
strict DTD (XHTML): 7.1.2. Three DTDs for XHTML (XML in a Nutshell)
String class, parsing data with no URI: 3.1.2. The InputSource Class (SAX2)
string datatype: 3.4. XPath Datatypes (XSLT)
converting to boolean values: 4.2.1.1. Converting to boolean values (XSLT)
string datatypes: 4.3. String Datatypes (XML Schema)
regular expressions and: 6.5.1. String Datatypes (XML Schema)
string( ) function: C. XSLT and XPath Function Reference (XSLT)
string functions (XPath): 9.7.2. String Functions (XML in a Nutshell)
string-interning: 4.1.2.3. Interning and entities (Java & XML)
Javadoc sources: 4.1.2.3. Interning and entities (Java & XML)
string-length( ) function: C. XSLT and XPath Function Reference (XSLT)
string-length( ) (XPath): 9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
String local element: 2.3.4. Essential ContentHandler Callbacks (SAX2)
String qName element: 2.3.4. Essential ContentHandler Callbacks (SAX2)
string-range( ): 11.7.4. The string-range( ) function (XML in a Nutshell)
string replace function: 4.6.1. Implementing a String Replace Function (XSLT)
String uri element: 2.3.4. Essential ContentHandler Callbacks (SAX2)
string( ) (XPath): 9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
StringBuffers: 2.3.4. Essential ContentHandler Callbacks (SAX2)
String.equals( ) function: 3.3.2. XMLReader Feature Flags (SAX2)
String.intern( ) function: 3.3.2. XMLReader Feature Flags (SAX2)
StringReader class: 4.1.2. The Locator Interface (SAX2)
StringWriter class: 5.3.6.3. JDOM to text approach (Java and XSLT)
strings
collapsed: 4.3.3. Collapsed Strings (XML Schema)
in XPath: 9.6.2. Strings (XML in a Nutshell)
22.2. Data Types (XML in a Nutshell)
internalized (SAX feature): 25.3. SAX Features and Properties (XML in a Nutshell)
limiting size of: 5.5. Back to Our Library (XML Schema)
normalized: 4.2. Whitespace Processing (XML Schema)
tokenized: 4.3.3.1. Tokenss (XML Schema)
structural nodes (DOM Core): 18.4.1. Structural Nodes (XML in a Nutshell)
structure module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
structure of XML: 2.2. Markup, Elements, and Structure (Perl & XML)
style attribute module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
style attribute, pie charts and: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
style attributes: 8.2. XPath (Perl & XML)
style class: 7.2.1. XHTML Prototypes (Java and XSLT)
StylesheetCache class: 5.4.2. A Stylesheet Cache (Java and XSLT)
6.4. Stylesheet Caching Revisited (Java and XSLT)
Style module: 6.3.4.3. Style (Java & XML)
style rules: 12.2. CSS Syntax (XML in a Nutshell)
stylesheet element (XSLT): 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
stylesheet module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
stylesheets: 7.2. Direct Display of XML in Browsers (XML in a Nutshell)
2. The Obligatory Hello World Example (XSLT)
2.1. Goals of This Chapter (XSLT)
0.6. Acknowledgments (Java and XSLT)
1.1. Java, XSLT, and the Web (Java and XSLT)
associating with XML documents, specification for: 1.2.4.8. Associating stylesheets with XML documents (XSLT)
compilation: 5.4. Stylesheet Compilation (Java and XSLT)
displaying XML in web browsers: 7.2. Direct Display of XML in Browsers (XML in a Nutshell)
document permanence and: 6.5. Document Permanence (XML in a Nutshell)
emulating for loop (example): 4.7. A Stylesheet That Emulates a for Loop (XSLT)
example of: 2.2.2. A Sample Stylesheet (XSLT)
alternate versions for: 2.4.7. Other Approaches (XSLT)
2.1. XSLT Introduction (Java and XSLT)
how to guide for: D. XSLT Guide (XSLT)
illustrating nodes: 3.5. The XPath View of an XML Document (XSLT)
JavaXML.fo.xsl: 10.4.1. Creating an XSP Page (Java & XML)
PDF transformation: 10.3.2. Viewing PDFs from XML (Java & XML)
processing of: 2.3. How a Stylesheet Is Processed (XSLT)
structure of: 2.4. Stylesheet Structure (XSLT)
stylesheet-generating with for loop emulation (example): 4.8. A Stylesheet That Generates a Stylesheet That Emulates a for Loop (XSLT)
Toot-O-Matic case study and: 9.2.3. See Just How Much We Could Accomplish Through Stylesheets (XSLT)
tutorials and, transformations for: 9.5. XSLT Source Code (XSLT)
using id( ) function: 5.1.3. A Stylesheet That Uses the id( ) Function (XSLT)
using key( ) function: 5.2.3. Stylesheets That Use the key( ) Function (XSLT)
WML: 10.3.4. WAP and XML (Java & XML)
XSP: 10.4. XSP (Java & XML)
subclassing: 10.2. Subclassing (Perl & XML)
subclassing, JDOM: 8.4.1. More on Subclassing (Java & XML)
subcodes (languages): 5.11.2. Subcodes (XML in a Nutshell)
subdocuments, including: 6.3. Including Subdocuments (SAX2)
subs parsing style: 3.2.2. Parsing Styles (Perl & XML)
subsets (external and internal): 2.5. Entities (Perl & XML)
substitution groups: 12.1. Substitution Groups (XML Schema)
16.8.3. Substitution Groups (XML in a Nutshell)
controlling usage of derivations: 12.2. Controlling Derivations (XML Schema)
defining content models using: 12.1.2.1. Using substitution groups (XML Schema)
head elements of: 12.1.2.1. Using substitution groups (XML Schema)
open schemas and: 13.2.3. And Substitution Groups? (XML Schema)
restricting: 12.1.4.2. Restricting substitution groups (XML Schema)
trees of: 12.1.2.3. Trees of substitution groups (XML Schema)
vs. element groups: 12.1.3. Traditional Declarations or Substitution Groups? (XML Schema)
substitutionGroup attribute: 12.1.2.1. Using substitution groups (XML Schema)
12.1.3. Traditional Declarations or Substitution Groups? (XML Schema)
substring-after( ) function: 4.6.1.1. Procedural design (XSLT)
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
C. XSLT and XPath Function Reference (XSLT)
substring-after( ) (XPath): 9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
substring-before( ) function: 4.6.1.1. Procedural design (XSLT)
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
C. XSLT and XPath Function Reference (XSLT)
substring-before( ) (XPath): 9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
substring( ) function: C. XSLT and XPath Function Reference (XSLT)
substring( ) (XPath): 9.7.2. String Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
substringData( ) (DOM): 24.2. Object Reference (XML in a Nutshell)
substringData method for CharacterData class (DOM): 7.2.7.2. Methods (Perl & XML)
suffixes in element declarations: 3.2.4. The Number of Children (XML in a Nutshell)
sum( ) function: 7.2.1. An Aside: Doing Math with Recursion (XSLT)
C. XSLT and XPath Function Reference (XSLT)
sum( ) (XPath): 9.7.4. Number Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
summarizer program for stream processing: 4.4. Stream Applications (Perl & XML)
Sun Microsystems Project X: 1.3.2. A Parser (Java & XML)
Sun Solaris, internationalization support: 8.6.3. Putting It All Together (Java and XSLT)
Sun, using JAXP: 1.4.5. Sun's Java API for XML Processing (JAXP) (SAX2)
super keyword and SAX filters: 19.3. Filters (XML in a Nutshell)
supplyBooks.pl: 14.1.3. Providing a Listing of Available Books (Java & XML)
suppress_errors( ) function: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
surrogate pairs (Java char values): 4.1.3. Internationalization Concerns (SAX2)
SVG, allowed in schema annotations: 14.2. The W3C XML Schema Annotation Element (XML Schema)
SVG files
converting "Hello World" document into: 2.5.1. The Hello World SVG File (XSLT)
trigonometric functions and: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
SVG (Scalable Vector Graphics): 0.. What This Book Covers (XML in a Nutshell)
namespaces
need for: 4.1. The Need for Namespaces (XML in a Nutshell)
specification for: 1.2.4.9. Scalable Vector Graphics (SVG) (XSLT)
syntax for: 4.2.1. Qualified Names, Prefixes, and Local Parts (XML in a Nutshell)
xmlns attribute and: 4.2.4. Setting a Default Namespace with the xmlns Attribute (XML in a Nutshell)
prefixes and namespace URIs: 4.2.3. Namespace URIs (XML in a Nutshell)
Swiss army knife, metaphor for regular expressions: 6.1. The Swiss Army Knife (XML Schema)
switch construct (_ _ Java): 5.2.3.3. Working with nodes (Java & XML)
switch statement (see <xsl:choose> element)
symbols entity set: 5.10. Character References (XML in a Nutshell)
26.2. HTML4 Entity Sets (XML in a Nutshell)
26.2. HTML4 Entity Sets (XML in a Nutshell)
synchronized keyword: 6.5.1. Servlet Threading Model (Java and XSLT)
syntax
abbreviated vs. unabbreviated: 3.2.6.1. Unabbreviated syntax (XSLT)
for <xsl:sort> element: 6.1.2.1. What's the deal with that syntax? (XSLT)
syntax errors in XML documents: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
syntax structures of XML documents: 20.3.1. Global Syntax Structures (XML in a Nutshell)
SYNTAX_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
system ID: 5.2.3.3. Working with nodes (Java & XML)
in XML documents: 3.2.3. Using an InputSource (Java & XML)
system ID for notations: 4.3.2. The DTDHandler Interface (SAX2)
system identifiers: 5.1.2. Xalan 1 Implementation (Java and XSLT)
5.3.1. System Identifiers, Files, and URLs (Java and XSLT)
2.5. Entities (Perl & XML)
StreamSource, providing for: 5.3.3. JAXP Stream I/O (Java and XSLT)
SYSTEM keyword: 2.1.1. XML 1.0 (Java & XML)
SYSTEM keyword (document type declaration): 3.1.2.1. Public IDs (XML in a Nutshell)
system-property( ) function: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
C. XSLT and XPath Function Reference (XSLT)
system-property( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
system resources, DOM and: 5.4.1. Memory, Performance, and Deferred DOMs (Java & XML)
systemId attribute
DocumentType interface: 24.2. Object Reference (XML in a Nutshell)
Entity interface: 24.2. Object Reference (XML in a Nutshell)
systemId parameter: 3.1.2.1. Always provide absolute URIs (SAX2)
B.10. Document Type Declaration Information Item (SAX2)
EntityResolver interface and: 3.4. The EntityResolver Interface (SAX2)
systemId property
DocumentType class: 7.2.3.1. Properties (Perl & XML)
Entity node: 7.2.15.1. Properties (Perl & XML)
Notation class: 7.2.16.1. Properties (Perl & XML)
System.setProperty( ) function: 3.2.1. The XMLReaderFactory Class (SAX2)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

\t special character (tab): 6.4.1. Special Characters (XML Schema)
table-and-caption element (XSL-FO): 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
table-caption value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table-cell value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table-column-group value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table-column value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table element (XSL-FO): 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
table elements (CSS display property): 12.5.5. Table Elements (XML in a Nutshell)
table-footer-group value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table-header-group value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table mode: 3.2.5. Template Modes (Java and XSLT)
table-row-group value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table-row value (display property): 12.5.5. Table Elements (XML in a Nutshell)
table value (display property): 12.5.5. Table Elements (XML in a Nutshell)
tables module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
tag handlers (JSP): 4.1.3. JSP (Java and XSLT)
tag sets: 1.1. The Benefits of XML (XML in a Nutshell)
tagName attribute (Element interface): 24.2. Object Reference (XML in a Nutshell)
tagname property for Element node: 7.2.8.1. Properties (Perl & XML)
tags: 2.1.1.2. Elements (Java & XML)
1.2. XML Review (Java and XSLT)
7.2.1. XHTML Prototypes (Java and XSLT)
7.2.1. XHTML Prototypes (Java and XSLT)
consolidation of opening and closing tags in an element: 2.1.1.2. Elements (Java & XML)
empty-element: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.3. Document Body (XML in a Nutshell)
in markup languages: 2.2. Markup, Elements, and Structure (Perl & XML)
matching: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
navigation areas, XHTML pages: 8.1. XSLT Page Layout Templates (Java and XSLT)
overlapping not allowed: 2.2.2.1. Parents and children (XML in a Nutshell)
rules governing: 1.2.2.4. XML tags are case-sensitive (XSLT)
start/end: 2.2. Elements, Tags, and Character Data (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.3. Document Body (XML in a Nutshell)
syntax of: 2.2.1. Tag Syntax (XML in a Nutshell)
vs. elements: 1.2.2.10. Tags versus elements (XSLT)
target attribute (ProcessingInstruction interface): 24.2. Object Reference (XML in a Nutshell)
target module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
target namespaces: 10.2. Namespace Declarations (XML Schema)
defining prefixes in schemas for: 10.3. To Qualify Or Not to Qualify? (XML Schema)
importing schemas: 10.8. Namespace Behavior of Imported Components (XML Schema)
with no namespaces: 10.9. Importing Schemas with No Namespaces (XML Schema)
qualified/unqualified attributes and elements: 10.3. To Qualify Or Not to Qualify? (XML Schema)
XPath, locating using: 8.1. XSLT Page Layout Templates (Java and XSLT)
xs:include and xs:redefine elements: 10.10. Chameleon Design (XML Schema)
xsi:noNamespaceSchemaLocation attribute and: 11.1. Associating Schemas with Instance Documents (XML Schema)
xsi:schemaLocation attribute and: 11.1. Associating Schemas with Instance Documents (XML Schema)
target parameter for processingInstruction( ) function: 4.1.1. Other ContentHandler Methods (SAX2)
target parameters: 2.8. Processing Instructions and Other Markup (Perl & XML)
target property for ProcessingInstruction class (DOM): 7.2.12.1. Properties (Perl & XML)
targetNamespace attribute: 10.2. Namespace Declarations (XML Schema)
16.3.1. Target Namespaces (XML in a Nutshell)
##targetNamespace wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
targets: 2.1.1. XML 1.0 (Java & XML)
3.6.1. Ant Basics (Java and XSLT)
Tauber, James: 3.10. Locating Standard DTDs (XML in a Nutshell)
TEI (Text Encoding Initiative): 6.3. TEI (XML in a Nutshell)
template driven pattern matching: 2.5. Looping and Sorting (Java and XSLT)
template engines: 4.1.4. Template Engines (Java and XSLT)
template keyword: 2.3.3.1. Template matching (Java & XML)
template matching: 2.3.3.1. Template matching (Java & XML)
template modes: 3.2.5. Template Modes (Java and XSLT)
template rules, built-in: 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
8.7. The Built-in Template Rules (XML in a Nutshell)
template rules in XSLT scripts: 2.12. Transformations (Perl & XML)
templates: 2.1.1. An XSLT Example (Java and XSLT)
adding to XSLT stylesheets: 8.4. Templates and Template Rules (XML in a Nutshell)
breaking stylesheets into: 2.2.4. A Skeleton Stylesheet (Java and XSLT)
built-in, rules for: 2.4.5. Built-in Template Rules (XSLT)
createTopNavbar: 8.1. XSLT Page Layout Templates (Java and XSLT)
empty templates: 3.5.3. Transforming Elements and Attributes (Java and XSLT)
identity transformations: 3.5.2. The Identity Transformation (Java and XSLT)
invoking by name: 4.3. Invoking Templates by Name (XSLT)
passing parameters to: 4.4. Parameters (XSLT)
pattern matching, multiple templates: 3.5.3. Transforming Elements and Attributes (Java and XSLT)
result file: 3.5.4. The Result File (Java and XSLT)
xsl:apply-templates and: 8.6. Applying Templates with xsl:apply-templates (XML in a Nutshell)
Templates interface: A.3.2.6. Templates (Java & XML)
5.2.1. JAXP 1.1 Implementation (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
thread safety: 6.5.2.4. Tip 4: Understand the Templates interface (Java and XSLT)
templatePage.xslt: 8.1. XSLT Page Layout Templates (Java and XSLT)
TemplateServlet.java: 8.1. XSLT Page Layout Templates (Java and XSLT)
TemplatesHandler interface: B. JAXP API Reference (Java and XSLT)
test( ): 5.2.2. DOM Parser Output (Java & XML)
test attribute
<xsl:if> element and: 4.2.1.1. Converting to boolean values (XSLT)
<xsl:when> element and: 4.2.2. The <xsl:choose> Element (XSLT)
testing: 9.2. Testing and Debugging (Java and XSLT)
TestXMLProperties class: 7.3.4. Backtracking (Java & XML)
TeX: 2.2. Markup, Elements, and Structure (Perl & XML)
text: 2.3.2. Example: Elements and Text (SAX2)
entity text, providing with InputSource: 3.1.2.2. Providing entity text (SAX2)
formatting: 3.4.2. Text Formatting (Java and XSLT)
generating to output stream: D.12. How Do I Generate Some Text? (XSLT)
input normalization: B.3. Document Information Item (SAX2)
of element, selecting: 3.2.4.2. Selecting the text of an element (XSLT)
writing XML output: 2.2.3.2. Concerns when writing XML text (SAX2)
text( ) method in XPath: 8.2. XPath (Perl & XML)
text-align property (CSS): 12.8. Text Properties (XML in a Nutshell)
Text class: A.4.1.11. Text (Java & XML)
JDOM: 8.1.1. The Text Class (Java & XML)
Text class (DOM): 7.2.10.1. Methods (Perl & XML)
text construct: 2.3.3.2. Looping (Java & XML)
text declarations
external entities and: 3.5.1. Text Declarations (XML in a Nutshell)
vs. XML declarations: 5.3. Text Declarations (XML in a Nutshell)
XML specification for: 20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
text-decoration property (CSS): 12.8. Text Properties (XML in a Nutshell)
Text Encoding Initiative (TEI): 6.3. TEI (XML in a Nutshell)
text-indent property (CSS): 12.8. Text Properties (XML in a Nutshell)
Text interface: A.2.1.18. Text (Java & XML)
Text interface (DOM): 18.4.2.8. Text (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
method for: 24.2. Object Reference (XML in a Nutshell)
nodeName attribute and: 24.2. Object Reference (XML in a Nutshell)
nodeValue attribute and: 24.2. Object Reference (XML in a Nutshell)
object hierarchy: 24.1. Object Hierarchy (XML in a Nutshell)
text module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
text( ) node test: 3.1.5. Comment Nodes (XSLT)
B.2. XPath Node Tests (XSLT)
using to select text of element: 3.2.4.2. Selecting the text of an element (XSLT)
text nodes: 2.2. Our First Schema (XML Schema)
default template for: 8.7.1. The Default Template Rule for Text and Attribute Nodes (XML in a Nutshell)
restricting length of: 7.3.2.2. Derivation by restriction (XML Schema)
XPath data model and: 22.1. The XPath Data Model (XML in a Nutshell)
text nodes (XPath): 3.1.4. Text Nodes (XSLT)
B.1.4. Text Nodes (XSLT)
text properties
Cascading Style Sheets: 12.8. Text Properties (XML in a Nutshell)
XSL-FO: 13.1. XSL Formatting Objects (XML in a Nutshell)
text-transform property (CSS): 12.8. Text Properties (XML in a Nutshell)
Text::Iconv Perl module: 3.9.4.1. iconv and Text::Iconv (Perl & XML)
TEXT_NODE( ) method: 7.3. XML::DOM (Perl & XML)
TEXT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces (XML in a Nutshell)
24.2. Object Reference (XML in a Nutshell)
textual data: 3.3.6. Element Data (Java & XML)
textual tools for XML documents: 17.1.1. Treating XML as Text (XML in a Nutshell)
text/xml MIME media type: 5.1. Character-Set Metadata (XML in a Nutshell)
Thai characters
ISO-8859-11 encoding: 5.6. ISO Character Sets (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
Unicode block for: 26.3.12. Thai (XML in a Nutshell)
The Mind Electric, Glue: 13.4.4. WSDL (Java & XML)
third-party links: 10.5. Linkbases (XML in a Nutshell)
Thompson, Henry S.: B.3.1. PSVI Serialization (XML Schema)
Thread class: 11.3.1. A Shared Handler (Java & XML)
thread safety: 6.5.2. Thread Safety Tips (Java and XSLT)
controlled access: 6.5.2.3. Tip 3: Provide a single point of entry (Java and XSLT)
local variables: 6.5.2.1. Tip 1: Local variables are thread-safe (Java and XSLT)
Templates interface: 6.5.2.4. Tip 4: Understand the Templates interface (Java and XSLT)
threaded discussion forum: 7.1.1. Requirements (Java and XSLT)
Tibetan Unicode block: 26.3.13. Tibetan (XML in a Nutshell)
Tidy (open source tool): 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
time datatypes: 4.5. Date and Time Datatypes (XML Schema)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
regular expressions and: 6.5.3. Datetimes (XML Schema)
time zones
ISO 8601 standard and: 4.5.1. The Realm of ISO 8601 (XML Schema)
specifying using datatypes: 4.5.2. Datatypes (XML Schema)
specifying using patterns: 6.5.3.1. Time zones (XML Schema)
xs:enumeration facet and: 5.1.1.4.1. (XML Schema)
title attribute (Xlink): 10.3. Link Semantics (XML in a Nutshell)
title attributes for arcs: 10.4.2.2. Arc titles (XML in a Nutshell)
title elements (extended links): 10.4.4. Title Elements (XML in a Nutshell)
title pseudoattribute: 7.2.1.4. The alternate and title pseudoattributes (XML in a Nutshell)
12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
titles, JPEG files and: 8.3.1. Generating JPEG Files from XML Content (XSLT)
9.5.7. Generating the JPEG Files (XSLT)
to attribute (XLink): 10.4.2. Arcs (XML in a Nutshell)
Tobin, Richard: B.3.1. PSVI Serialization (XML Schema)
3.1.4. Validating a Document (XML in a Nutshell)
token (character streams): 4.1. Working with Streams (Perl & XML)
tokenize function: 5.2.3.5. Solution #4: Use an extension function (XSLT)
tokenized strings: 4.3.3.1. Tokenss (XML Schema)
tokens: 4.3.3.1. Tokenss (XML Schema)
Tomcat servlet engine: 1.4. Getting Started (Java and XSLT)
9.1.3. Tomcat (Java and XSLT)
Cocoon, configuration for: 10.2.2. Configuring the Servlet Engine (Java & XML)
context, configuration: 10.2.2. Configuring the Servlet Engine (Java & XML)
libraries, loading of: 10.2.2. Configuring the Servlet Engine (Java & XML)
jar files, alphabetization problem: 10.2.2. Configuring the Servlet Engine (Java & XML)
online resources: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
root directory: 6.1.1. Splash Screen Servlet Example (Java and XSLT)
v. 4.0, online resources: 0.2. Software and Versions (Java and XSLT)
SOAP, servlet installation: 12.2.2.3. The router servlet and admin client (Java & XML)
tool for XML programming: 9.3. XML Programming Tools (Perl & XML)
tooltips: 9.3.1. Menu Panel (XSLT)
Toot-O-Matic: 9. Case Study: The Toot-O-Matic (XSLT)
design goals for: 9.2. Design Goals (XSLT)
stylesheet transformations and: 9.5. XSLT Source Code (XSLT)
tutorial layout and: 9.3. Tutorial Layout (XSLT)
XML document design and: 9.4. XML Document Design (XSLT)
top-level elements: 2.4.6. Top-Level Elements (XSLT)
top property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
toString( ) method: 3.8.1. Other Methods of Output (Perl & XML)
7.3. XML::DOM (Perl & XML)
totalDigits facet element: 16.6.2.4.2. Length and precision (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
<tr> element: 8.3.2. Accessing a Database with an Extension Element (XSLT)
transform( ): 23.4. TrAX (XML in a Nutshell)
9.3.2.2. Transforming XML (Java & XML)
transform attribute, pie charts and: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
Transformation API for XML (TrAX), global parameters and: 4.4.3.6. Setting global parameters in a Java program (XSLT)
transformation languages: 6.6. Transformation and Presentation (XML in a Nutshell)
transformations: 0.6. Acknowledgments (Java and XSLT)
1. Introduction (Java and XSLT)
1.1. Java, XSLT, and the Web (Java and XSLT)
2.2. Transformation Process (Java and XSLT)
AntÕs task: 9.1.2.3. Transforming using Ant's style task (Java and XSLT)
client or server: 2.1. XSLT Introduction (Java and XSLT)
client-side: 1.5. Web Browser Support for XSLT (Java and XSLT)
CVS files, on: 5.3.5.4. Using the parser (Java and XSLT)
DTDs, validating with: 9.2. Testing and Debugging (Java and XSLT)
dynamic XML: 7.3. Making the XML Dynamic (Java and XSLT)
filters and: 8.4.3. Closing Thoughts on Filters (Java and XSLT)
javax.xml.transform package: B. JAXP API Reference (Java and XSLT)
javax.xml.transform.dom package: B. JAXP API Reference (Java and XSLT)
javax.xml.transform.stream: B. JAXP API Reference (Java and XSLT)
javax.xml.transfrom.sax: B. JAXP API Reference (Java and XSLT)
JAXP support: 1.4.1.4. JAXP (Java and XSLT)
maximizing performance: 9.3. Performance Techniques (Java and XSLT)
Microsoft products, support within: 1.5. Web Browser Support for XSLT (Java and XSLT)
process, recursion: 2.2.2. Recursive Processing with Templates
required components: 2.1.1. An XSLT Example (Java and XSLT)
SAX-based transformations: 5.3.5.2. Detailed CSV to SAX design (Java and XSLT)
task, Ant: 9.1.2.3. Transforming using Ant's style task (Java and XSLT)
Templates interface, performance optimization with: 5.4.2. A Stylesheet Cache (Java and XSLT)
TemplateServlet, using: 8.1. XSLT Page Layout Templates (Java and XSLT)
testing and debugging: 9.2. Testing and Debugging (Java and XSLT)
Transformer interface: 5.4.1. Templates API (Java and XSLT)
validation: 9.2.1. JUnit (Java and XSLT)
XML to XML: 3.5. Schema Evolution (Java and XSLT)
XSLT, predominance for: 2. XSLT Part 1 -- The Basics (Java and XSLT)
XSLT processors, independence from servlets: 4.2.4. Development and Maintenance Benefits of XSLT (Java and XSLT)
XSLT stylesheet reuse: 3.5. Schema Evolution (Java and XSLT)
XSLTRenderHelper class: 7.4. Servlet Implementation (Java and XSLT)
performance penalties: 9.3. Performance Techniques (Java and XSLT)
in XML: 2.12. Transformations (Perl & XML)
XSL (see XSLT)
Transformer class: 5.2.3. The Transformer Class (Java and XSLT)
A.3.2.7. Transformer (Java & XML)
5.4.1. Templates API (Java and XSLT)
5.4.1. Templates API (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
thread safety, and: 5.2.3. The Transformer Class (Java and XSLT)
TransformerConfigurationException class: 5.2.4. JAXP XSLT Design (Java and XSLT)
9.1.1.3. Other exceptions and errors (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
Transformer instance, obtaining: 9.3.2.1. Working with the factory (Java & XML)
TransformerFactory class: 9.3.2.1. Working with the factory (Java & XML)
A.3.2.8. TransformerFactory (Java & XML)
TransformerFactoryConfigurationError class: 5.2.4. JAXP XSLT Design (Java and XSLT)
B. JAXP API Reference (Java and XSLT)
TransformerHandler class: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
Transformer.transform( ) function: 4.5.3. The javax.xml.transform.sax Package (SAX2)
4.5.3.2. SAX in Pull-Mode with XSLT (SAX2)
transforming XML documents: 2.2.3. Transforming the XML Document (XSLT)
transitional DTD (XHTML): 7.1.2. Three DTDs for XHTML (XML in a Nutshell)
translate( ) function: C. XSLT and XPath Function Reference (XSLT)
translate( ) (XPath): 22.5. XPath Functions (XML in a Nutshell)
traversal
Node interface and: 18.3.3. Relating Document Structure to Nodes (XML in a Nutshell)
xsl:apply-templates and: 8.6. Applying Templates with xsl:apply-templates (XML in a Nutshell)
Traversal module: 6.3.2. Traversal (Java & XML)
TRAX API: 1.4.5. Sun's Java API for XML Processing (JAXP) (SAX2)
TrAX (Transformation API for XML), global parameters and: 4.4.3.6. Setting global parameters in a Java program (XSLT)
TrAX (Transformations API for XML): 23.4. TrAX (XML in a Nutshell)
tree-based processing: 3.2.2. Parsing Styles (Perl & XML)
6. Tree Processing (Perl & XML)
8. Beyond Trees: XPath, XSLT, and More (Perl & XML)
optimized: 8.4. Optimized Tree Processing (Perl & XML)
vs. stream-based: 3.3. Stream-Based Versus Tree-Based Processing (Perl & XML)
XML structure and: 2.2. Markup, Elements, and Structure (Perl & XML)
XML::Grove module: 6.6. XML::Grove (Perl & XML)
XML::LibXML module and: 3.5. XML::LibXML (Perl & XML)
XML::Parser: 6.3. XML::Parser's Tree Mode (Perl & XML)
XML::Simple module: 6.2. XML::Simple (Perl & XML)
XML::TreeBuilder module and: 6.5. XML::TreeBuilder (Perl & XML)
XPath and: 8.2. XPath (Perl & XML)
XSLT: 8.3. XSLT (Perl & XML)
tree climbers: 8.1. Tree Climbers (Perl & XML)
tree data structure, XML documents: 2.2.1. XML Tree Data Structure (Java and XSLT)
tree models for XML documents: 17.1.3. Treating XML as Tree Models (XML in a Nutshell)
tree processing (see tree-based processing)
tree structure of XSL processing stages: 2.3.1.1. XSL and trees (Java & XML)
tree-walking: 6.3.2. Traversal (Java & XML)
trees of substitution groups: 12.1.2.3. Trees of substitution groups (XML Schema)
TreeWalker interface: 6.3.2.2. TreeWalker (Java & XML)
online resources: 6.3.2.2. TreeWalker (Java & XML)
TREX (Tree Regular Expressions for XML) schema: A.3.3. The RELAX NG Family (XML Schema)
trigonometric functions, extension for providing: 8.1.2.1. Example: A library of trigonometric functions (XSLT)
troff text formatting language: 2.1. A Brief History of XML (Perl & XML)
true( ) function: C. XSLT and XPath Function Reference (XSLT)
true( ) (XPath): 9.7.3. Boolean Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
try/catch blocks, JDOM, for catching null values: 7.5.2. Null Return Values (Java & XML)
Turbine: 4.2.1. Web Frameworks (Java and XSLT)
BrowserDetector class: 8.3. Identifying the Browser (Java and XSLT)
online resources: 8.3. Identifying the Browser (Java and XSLT)
Turing, Alan: 2.1. XML Documents and XML Files (XML in a Nutshell)
3.1.1. A Simple DTD Example (XML in a Nutshell)
3.2.8. Empty Elements (XML in a Nutshell)
3.9.1. Data-Oriented DTDs (XML in a Nutshell)
8.1. An Example Input Document (XML in a Nutshell)
9.1. The Tree Structure of an XML Document (XML in a Nutshell)
9.2.2. Child Element Location Steps (XML in a Nutshell)
9.4. Predicates (XML in a Nutshell)
9.7.1. Node-Set Functions (XML in a Nutshell)
JPEG image of: 3.6. External Unparsed Entities and Notations (XML in a Nutshell)
10.2. Link Behavior (XML in a Nutshell)
Turing complete programming languages: 1.1. The Benefits of XML (XML in a Nutshell)
tutorials: 9. Case Study: The Toot-O-Matic (XSLT)
9. Case Study: The Toot-O-Matic (XSLT)
parts of: 9.3. Tutorial Layout (XSLT)
stylesheet transformations for: 9.5. XSLT Source Code (XSLT)
type attribute, referencing global definitions: 7.2.2. Named Versus Anonymous Types (XML Schema)
type attribute (Xlink): 10.1. Simple Links (XML in a Nutshell)
type derivation (see derived types)
type libraries: 16.8.1.3. Importing schemas for other namespaces (XML in a Nutshell)
type pseudoattribute: 7.2.1.1. The required href and type pseudoattributes (XML in a Nutshell)
12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
type substitutions and block attribute: 12.2.2.1. Block attribute (XML Schema)
types element: 13.3. WSDL (Java & XML)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

UCS-4 encoding: 5.9. The Default Character Set for XML Documents (XML in a Nutshell)
UDDI (Universal Discovery, Description, and Integration): 1.2.3. XML-Based Applications (Java & XML)
13.2. UDDI (Java & XML)
implementations: 13.2. UDDI (Java & XML)
registry: 13.2. UDDI (Java & XML)
searching: 13.4.3. Searching a UDDI Registry (Java & XML)
unabbreviated location paths: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
unabbreviated syntax: 3.2.6.1. Unabbreviated syntax (XSLT)
unassigned code points (Unicode): 26.1. Character Tables (XML in a Nutshell)
UnavailableException: 6.3.3. Source Code (Java and XSLT)
Underwood, Walter: 7.4.3. Robots (XML in a Nutshell)
Unexpanded Entity Reference Information Items: B.7. Unexpanded Entity Reference Information Items (SAX2)
Unicoce Transformation Format (see UTF)
Unicode
blocks: 6.4.3.2. Unicode character classes (XML Schema)
checking string datatypes: 6.5.1.1. Unicode blocks (XML Schema)
categories: 6.4.3.2. Unicode character classes (XML Schema)
character classes: 6.4.3.2. Unicode character classes (XML Schema)
Unicode blocks: 26.3. Other Unicode Blocks (XML in a Nutshell)
Arabic: 26.3.10. Arabic (XML in a Nutshell)
Armenian: 26.3.8. Armenian (XML in a Nutshell)
Arrows: 26.3.20. Arrows (XML in a Nutshell)
Currency Symbols: 26.3.18. Currency Symbols (XML in a Nutshell)
Cyrillic: 26.3.7. Cyrillic (XML in a Nutshell)
Devanagari: 26.3.11. Devanagari (XML in a Nutshell)
Dingbat: 26.3.26. Dingbats (XML in a Nutshell)
General Punctuation: 26.3.17. General Punctuation (XML in a Nutshell)
Geometric Shapes: 26.3.24. Geometric Shapes (XML in a Nutshell)
Greek Extended: 26.3.16. Greek Extended (XML in a Nutshell)
Greek/Coptic: 26.3.6. Greek and Coptic (XML in a Nutshell)
Hebrew: 26.3.9. Hebrew (XML in a Nutshell)
IPA Extensions: 26.3.3. IPA Extensions (XML in a Nutshell)
Latin Extended-A: 26.3.1. Latin Extended-A (XML in a Nutshell)
Latin Extended Additional: 26.3.15. Latin Extended Additional (XML in a Nutshell)
Latin Extended-B: 26.3.2. Latin Extended-B (XML in a Nutshell)
Letter-like Symbols: 26.3.19. Letter-Like Symbols (XML in a Nutshell)
Mathematical Operators: 26.3.21. Mathematical Operators (XML in a Nutshell)
Miscellaneous Symbols: 26.3.25. Miscellaneous Symbols (XML in a Nutshell)
Miscellaneous Technical: 26.3.22. Miscellaneous Technical (XML in a Nutshell)
Optical Character Recognition: 26.3.23. Optical Character Recognition (XML in a Nutshell)
Spacing Modifier Letters: 26.3.4. Spacing Modifier Letters (XML in a Nutshell)
Thai: 26.3.12. Thai (XML in a Nutshell)
Tibetan: 26.3.13. Tibetan (XML in a Nutshell)
Unicode character classes: 21.2. Schema Elements (XML in a Nutshell)
Unicode character encodings: 1.1. Why Use Perl with XML? (Perl & XML)
2.6. Unicode, Character Sets, and Encodings (Perl & XML)
3.9. Character Sets and Encodings (Perl & XML)
conversion: 3.9.4. Encoding Conversion (Perl & XML)
Perl support for: 3.9.3.1. Core Perl support (Perl & XML)
XML: 3.9.1. Unicode, Perl, and XML (Perl & XML)
vs. ASCII: 1.6. XML Gotchas (Perl & XML)
Unicode characters: 5. Internationalization (XML in a Nutshell)
character references and: 20.3.1. Global Syntax Structures (XML in a Nutshell)
character set names: 26. Character Sets (XML in a Nutshell)
character tables for: 26.1. Character Tables (XML in a Nutshell)
encoding attribute in XML declaration: 2.9.1. encoding (XML in a Nutshell)
Unicode Consortium: 2.6. Unicode, Character Sets, and Encodings (Perl & XML)
Unicode for internationalization: 4.1.3. Internationalization Concerns (SAX2)
Unicode Normalization Form C: B.3. Document Information Item (SAX2)
internationalizing character encodings: 4.1.3. Internationalization Concerns (SAX2)
Unicode::String module: 3.9.4.2. Unicode::String (Perl & XML)
Uniform Resource Identifiers (see URIs)
Uniform Resource Identifiers (URIs): 10.1. Perl and XML Namespaces (Perl & XML)
uniform resource indicators (URIs): 3.2.1. Instantiating a Reader (Java & XML)
uniform resource locators (see URLs)
Uniform Resource Names (URNs): 3.1.2. The Document Type Declaration (XML in a Nutshell)
union
of character classes: 6.4.3.3. User-defined character classes (XML Schema)
derivation by: 5.3. Derivation By Union (XML Schema)
11.2.1. Defining Simple Types (XML Schema)
Unique Particle Attribution Rule: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
xsi:type as workaround against: 11.2.2. Defining Complex Types (XML Schema)
uniqueness, defining tests for: 9.2.1. Uniqueness (XML Schema)
units of length (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
UCS (Universal Character Set): 8.6.2. Encodings (Java and XSLT)
Universal Discovery, Description, and Integration (see UDDI)
universal names for attributes and elements: 2.6.2. Element and Attribute Naming with Namespaces (SAX2)
Universal Resource Identifier (see URI)
Universal Resource Locator (URL): 2.4.1. SAX2 Feature Flags (SAX2)
Universal Resource Name (see URM)
UNIX, writing XML output: 2.2.3.2. Concerns when writing XML text (SAX2)
unknown validation behavior: 2.4.1. SAX2 Feature Flags (SAX2)
unmarshal( ): 15.4.3. Marshalling and Unmarshalling (Java & XML)
unmarshaling: 3.5.2.2. Turning objects into SAX events (SAX2)
4.4. Turning SAX Events into Data Structures (SAX2)
unmarshalling: 15.1.2. Unmarshalling (Java & XML)
unordered content models, limitations on: 7.4.1.5. Limitations on unordered content models (XML Schema)
unparsed data: 2.1.1.5. Unparsed data (Java & XML)
unparsed entities, datatypes for: 4.3.3.1. Tokenss (XML Schema)
unparsed entity information items: B.11. Unparsed Entity Information Items (SAX2)
unparsed-entity-uri( ) function: C. XSLT and XPath Function Reference (XSLT)
unparsed-entity-uri( ) (XSLT): 23.3. XSLT Functions (XML in a Nutshell)
unparsed general entities: 3.6. External Unparsed Entities and Notations (XML in a Nutshell)
17.2.5. Unparsed Entities (XML in a Nutshell)
declaring notations: 20.4.2. Validity Constraints (XML in a Nutshell)
not allowed in entity references: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
unparsedEntityDecl( ) callback function: 4.3.2. The DTDHandler Interface (SAX2)
unparsed_entity_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers (Perl & XML)
unqualified names, XML specification for: 20.3.4. Namespaces (XML in a Nutshell)
unqualified vs. qualified attributes: 10.3. To Qualify Or Not to Qualify? (XML Schema)
unstructured XML documents: 5.3. Generating Links in Unstructured Documents (XSLT)
UpdateItemServlet class: 6.1.1. Creating a New DOM Tree (Java & XML)
6.3.3. Range (Java & XML)
UP.SDK: 10.3.4. WAP and XML (Java & XML)
URI mappings, storage in data structures: 3.3.4. Namespace Callbacks (Java & XML)
URI (Universal Resource Identifier): 2.4.1. SAX2 Feature Flags (SAX2)
base: B.2.2. Base URIs, xml:base, and Locator Data (SAX2)
defining new handlers and features: 3.3. Configuring XMLReader Behavior (SAX2)
filenames vs.: 3.1.3. Filenames Versus URIs (SAX2)
InputSource class and: 3.1.2.1. Always provide absolute URIs (SAX2)
namespaces and: 2.6.1. What Namespaces Do to XML (SAX2)
URIResolver interface: 9.3.2.1. Working with the factory (Java & XML)
A.3.2.9. URIResolver (Java & XML)
URIs (uniform resource indicators): 3.2.1. Instantiating a Reader (Java & XML)
10.1. Perl and XML Namespaces (Perl & XML)
3.1.2. The Document Type Declaration (XML in a Nutshell)
absolute vs. relative: 11.1. Associating Schemas with Instance Documents (XML Schema)
assigning elements to: 4.2. Namespace Syntax (XML in a Nutshell)
base: 7.3. Invoking the document( ) Function (XSLT)
binding prefixes to: 4.2.2. Binding Prefixes to URIs (XML in a Nutshell)
controlling parser behavior for SAX: 19.2. SAX Features and Properties (XML in a Nutshell)
25.3. SAX Features and Properties (XML in a Nutshell)
conventions for using: 4.2.2. Binding Prefixes to URIs (XML in a Nutshell)
namespace: 8.1.1.1. Example: Generating multiple output files (XSLT)
namespace attribute and: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
property and feature IDs as: 4.1.1. Setting Properties and Features (Java & XML)
referencing schemas with target namespaces: 11.1. Associating Schemas with Instance Documents (XML Schema)
targetNamespace attribute and: 10.2. Namespace Declarations (XML Schema)
using schemas at original locations: 10.7.1. XML Attributes (XML Schema)
XML documents and: 7. Combining XML Documents (XSLT)
XPointer syntax and: 0.. What This Book Covers (XML in a Nutshell)
xs:anyURI datatype: 4.3.3.3. URIs (XML Schema)
xs:anyURI type and: 16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
URL (Universal Resource Locator): 2.4.1. SAX2 Feature Flags (SAX2)
URLs (uniform resource locators): 3.2.1. Instantiating a Reader (Java & XML)
Adobe SVG plug-ins: 2.5.1. The Hello World SVG File (XSLT)
Apache Jetspeed project: 14.3.1. Rich Site Summary (Java & XML)
developerWorks: 9. Case Study: The Toot-O-Matic (XSLT)
DOMit servlet: 1.2.3.1.1. A sample DOM tree (XSLT)
EXSLT project: 8.3.2. Accessing a Database with an Extension Element (XSLT)
file names, converting to system IDs: 5.3.1. System Identifiers, Files, and URLs (Java and XSLT)
Java and XML web site: 3.1.3. Have an XML Document on Hand (Java & XML)
rewriting: 8.2.1. Servlet Session-Tracking API (Java and XSLT)
SAX: 1.2.3. DOM and SAX (XSLT)
SAX API Javadocs: 3.1.2. Getting the SAX Classes and Interfaces (Java & XML)
Saxon processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
W3C: 1.2.3. DOM and SAX (XSLT)
Xalan processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
Xerces parser: 3.1.1. Obtaining a Parser (Java & XML)
XML documents and: 7. Combining XML Documents (XSLT)
XML parser (Oracle): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
and XPointers: 11.1. XPointers on URLs (XML in a Nutshell)
XSLT processor (Microsoft): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
XT processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
URNs (Uniform Resource Names): 3.1.2. The Document Type Declaration (XML in a Nutshell)
2.4.1. SAX2 Feature Flags (SAX2)
FPIs (Formal Public Identifiers) and: 3.4. The EntityResolver Interface (SAX2)
use cases, APIs and data binding: 15.1.4. Use Cases (Java & XML)
user-defined character classes: 6.4.3.3. User-defined character classes (XML Schema)
User-Agent HTTP header: 8.3. Identifying the Browser (Java and XSLT)
10.3.2.1. Identifying the client (Java and XSLT)
User-Agent values: 8.3. Identifying the Browser (Java and XSLT)
user interface prototypes: 7.2.1. XHTML Prototypes (Java and XSLT)
UTF-8: 8.6.2. Encodings (Java and XSLT)
UTFs (UCS Transformation Formats): 8.6.2. Encodings (Java and XSLT)
UTF-8/16 character encoding: 2.2.3.2. Concerns when writing XML text (SAX2)
3.1.2.2. Providing entity text (SAX2)
B.3. Document Information Item (SAX2)
UTF-8 character encodings: 1.1. Why Use Perl with XML? (Perl & XML)
2.6. Unicode, Character Sets, and Encodings (Perl & XML)
3.9. Character Sets and Encodings (Perl & XML)
3.9.2.1. UTF-8 (Perl & XML)
UTF-16 encoding: 5.4. XML-Defined Character Sets (XML in a Nutshell)
5.5.1. UCS-2 and UTF-16 (XML in a Nutshell)
26. Character Sets (XML in a Nutshell)
encoding declaration and: 20.3.1. Global Syntax Structures (XML in a Nutshell)
omitting encoding declaration: 5.2. The Encoding Declaration (XML in a Nutshell)
UTF-32 character encodings: 3.9.2.3. UTF-32 (Perl & XML)
UTF (Unicode Transformation Format): 3.9.2. Unicode Encodings (Perl & XML)
utils.xslt: 7.2.3. XSLT Stylesheets (Java and XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

valid documents, vs. well-formedness: 1.2.2.9. Well-formed versus valid documents (XSLT)
valid XML documents: 2.1.1.2. Elements (Java & XML)
1.2.3. Validation (Java and XSLT)
validating parsers: 3.1. XML Parsers (Perl & XML)
3.7. Document Validation (Perl & XML)
1.3. How XML Works (XML in a Nutshell)
1.3. How XML Works (XML in a Nutshell)
18.6. Parsing a Document with DOM (XML in a Nutshell)
associating schema documents with instance documents: 16.1. Overview (XML in a Nutshell)
16.2. Schema Basics (XML in a Nutshell)
validating XML documents: 1.1.1. Validation (XML Schema)
2.3.2. W3C XML Schema Is Both About Structure and Datatyping (XML Schema)
validating XML parsers: 1.3.2. A Parser (Java & XML)
validation: 4.1.2.1. Validation (Java & XML)
validation (document): 3.7. Document Validation (Perl & XML)
validation (producer-side): 2.4. Producer-Side Validation (SAX2)
handling errors for: 2.4.2. Handling Validity Errors (SAX2)
validity constraints: 20.4.2. Validity Constraints (XML in a Nutshell)
validity errors: 1.3. How XML Works (XML in a Nutshell)
3. Document Type Definitions (DTDs) (XML in a Nutshell)
3.1.4. Validating a Document (XML in a Nutshell)
value attribute (Attr interface): 24.2. Object Reference (XML in a Nutshell)
value-of construct: 2.3.3.1. Template matching (Java & XML)
value property of Attr class (DOM): 7.2.9.1. Properties (Perl & XML)
value spaces: 4.1. Lexical and Value Spaces (XML Schema)
multiple facet definitions and: 5.1.2.1. Facet that can be changed but needs to be more restrictive (XML Schema)
variable bindings: B.6. The XPath Context (XSLT)
variable scope: 4.5.2. Variable Scope (XSLT)
variables: 3.2. Parameters and Variables (Java and XSLT)
variables: 4.5. Variables (XSLT)
defining: 3.2.1.1. Defining variables (Java and XSLT)
HTML attribute values, using within: 3.2.1.2. Using variables (Java and XSLT)
incrementing: 3.2.4. Incrementing Variables (Java and XSLT)
initializing: 7.2.1.2. Generating output to initialize a variable (XSLT)
limitations: 3.2.1.2. Using variables (Java and XSLT)
set of (context): 3.2.1. The Context (XSLT)
B.6. The XPath Context (XSLT)
thread safety: 6.5.2.1. Tip 1: Local variables are thread-safe (Java and XSLT)
using: 3.2.1.2. Using variables (Java and XSLT)
Vector: 11.3.1. A Shared Handler (Java & XML)
vendor-neutral parsing code and JAXP: 9.2. JAXP 1.0 (Java & XML)
vendor property: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
vendor-specific code: 6.4.3. Bootstrapping (Java & XML)
version information attribute, XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
vertical bar (|), escaping: 4.6.1. Implementing a String Replace Function (XSLT)
Vietnamese language, Unicode block most useful for: 26.3.15. Latin Extended Additional (XML in a Nutshell)
view: 4.2.2. Model-View-Controller (Java and XSLT)
Views module: 6.3.4.2. Views (Java & XML)
views of XML documents: 7. Combining XML Documents (XSLT)
ViewMessageJDOM.java: 7.3.3. JDOM XML Production (Java and XSLT)
ViewMonthJDOM.java: 7.3.3. JDOM XML Production (Java and XSLT)
ViewMonthRenderer.java: 7.4. Servlet Implementation (Java and XSLT)
ViewMonthReqHandler.java: 7.4. Servlet Implementation (Java and XSLT)
viewMonth.xslt: 7.2.3. XSLT Stylesheets (Java and XSLT)
ViewMsgRenderer.java: 7.4. Servlet Implementation (Java and XSLT)
ViewMsgReqHandler.java: 7.4. Servlet Implementation (Java and XSLT)
ViewMsg.xml: 7.2.2. XML Samples (Java and XSLT)
visited pseudoclass: 12.4.4. Pseudoclass Selectors (XML in a Nutshell)
vocabularies
multi-namespace, limitations for: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
qualified vs. unqualified attributes: 10.3. To Qualify Or Not to Qualify? (XML Schema)
10.4. Disruptive Attributes (XML Schema)
referencing other namespaces: 10.6. Referencing Other Namespaces (XML Schema)
VRML files, converting "Hello World" document into: 2.5.4. The Hello World VRML File (XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

\w character class: 6.4.3.1. Classical Perl character classes (XML Schema)
walker (tree climbers): 8.1. Tree Climbers (Perl & XML)
Wall, Larry: 3.2. XML::Parser (Perl & XML)
Walsh, Norman: 6.4. DocBook (XML in a Nutshell)
WAP (Wireless Application Protocol): 10.1. Wireless Technologies (Java and XSLT)
1.3.4. Wireless (Java and XSLT)
gateways: 10.2. The Wireless Architecture (Java and XSLT)
online resources: 10.3.4. WAP and XML (Java & XML)
specifications, online resources: 10.3.4. WAP and XML (Java & XML)
WAR (Web Application Archive) files: 6.2. WAR Files and Deployment (Java and XSLT)
4.1.2. Servlets as CGI Replacements (Java and XSLT)
contents, viewing: 6.2.1. WAR Files (Java and XSLT)
6.3.5. How to Compile, Deploy, and Run (Java and XSLT)
deployment descriptors: 6.2.2. Deployment Descriptor (Java and XSLT)
directory structure: 6.3.4. Deployment (Java and XSLT)
jar command, creation using: 6.2.1. WAR Files (Java and XSLT)
page layout templates: 8.1. XSLT Page Layout Templates (Java and XSLT)
WEB-INF directory: 6.2.1. WAR Files (Java and XSLT)
warning( ) exception handler: 5.7.2.4. Error event handlers and catching exceptions (Perl & XML)
warning, DOMSerializer class: 5.2.4. The Results (Java & XML)
warning( ) method: 2.5.2. ErrorHandler Interface (SAX2)
warnings: 3.4.1. Warnings (Java & XML)
W3C
7.1. DOM and Perl (Perl & XML)
1.2. W3C XML Schema (XML Schema)
1.1. The Design of XSLT (XSLT)
recommended form of XHTML: 7.1.2. Three DTDs for XHTML (XML in a Nutshell)
specifications and: 1.2.4. XML Standards (XSLT)
standard DTDs and: 3.10. Locating Standard DTDs (XML in a Nutshell)
standards endorsed by: 0.. What This Book Covers (XML in a Nutshell)
styling XML documents: 1.4. The Evolution of XML (XML in a Nutshell)
templates for
defining the namespace URI and prefix names: 7.3.3. Mixing Your Own XHTML (XML in a Nutshell)
importing standard modules: 7.3.3. Mixing Your Own XHTML (XML in a Nutshell)
naming attributes and elements: 7.3.3. Mixing Your Own XHTML (XML in a Nutshell)
URI identifiers and: 10.1. Perl and XML Namespaces (Perl & XML)
XSL specification: 2. XSLT Part 1 -- The Basics (Java and XSLT)
XSL-FO specification and: 9.5.6. Generating the PDF Files (XSLT)
W3C Character Model, internationalizing character encodings: 4.1.3. Internationalization Concerns (SAX2)
W3C XML Schema: 1.2. W3C XML Schema (XML Schema)
Consistent Declaration Rule: 7.4.1.4. Consistent Declaration Rule (XML Schema)
datatypes reference guide: 16. Datatype Reference Guide (XML Schema)
elements reference guide: 15. Elements Reference Guide (XML Schema)
modularity of: 2.3.1. W3C XML Schema Is Modular (XML Schema)
vs. depth of schemas: 3.2.1. Depth Versus Modularity? (XML Schema)
namespace prefixes: 10.2. Namespace Declarations (XML Schema)
pattern matching in: 6.2. The Simplest Possible Patterns (XML Schema)
proposal: A.3.2. The W3C XML Schema Family (XML Schema)
proposals submitted: A.3.2. The W3C XML Schema Family (XML Schema)
time zone support in: 6.5.3.1. Time zones (XML Schema)
Unique Particle Attribution Rule: 7.4.1.3. Unique Particle Attribution Rule (XML Schema)
works in progress: B. Work in Progress (XML Schema)
WBMP (Wireless Bitmap): 10.3.1.1. WMLScript and WBMP (Java and XSLT)
image conversion: 10.3.1.1. WMLScript and WBMP (Java and XSLT)
Web (see World Wide Web)
web application development: 4. Java-Based Web Technologies (Java and XSLT)
design process: 7.2.2. XML Samples (Java and XSLT)
summary of approaches: 4.4. Summary of Key Approaches (Java and XSLT)
web applications: 4.1.2. Servlets as CGI Replacements (Java and XSLT)
client-only applications, vs.: 6. Servlet Basics and XSLT (Java and XSLT)
contrasted with web sites: 1.3.2. Web Applications (Java and XSLT)
development: 1.3.1. Web Development Challenges (Java and XSLT)
challenges: 1.3.1. Web Development Challenges (Java and XSLT)
hyperlinks: 7.2.3. XSLT Stylesheets (Java and XSLT)
nonbrowser clients: 1.3.3. Nonbrowser Clients (Java and XSLT)
phone simulators: 10.1. Wireless Technologies
security: 7.4. Servlet Implementation (Java and XSLT)
web artists: 10. Web Publishing Frameworks (Java & XML)
web browsers
CSS stylesheets in: 12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
13.5. Choosing Between CSS and XSL-FO (XML in a Nutshell)
displaying XML in: 7.2. Direct Display of XML in Browsers (XML in a Nutshell)
document validation and: 3.1.4. Validating a Document (XML in a Nutshell)
link behavior in: 10.2. Link Behavior (XML in a Nutshell)
linkbases and: 10.5. Linkbases (XML in a Nutshell)
support for XHTML: 7.1.3. Browser Support for XHTML (XML in a Nutshell)
XLinks support: 10. XLinks (XML in a Nutshell)
XPointers and: 11.1. XPointers on URLs (XML in a Nutshell)
web frameworks: 4.2.1. Web Frameworks (Java and XSLT)
advantages: 4.2.1. Web Frameworks (Java and XSLT)
Turbine: 4.2.1. Web Frameworks (Java and XSLT)
web publishing frameworks: 1.2.3. XML-Based Applications (Java & XML)
10. Web Publishing Frameworks (Java & XML)
Cocoon: 10.1.4. Making the Decision (Java & XML)
DOM support, importance of: 10.1.2. Integration with Other XML Tools and APIs (Java & XML)
installation: 10.2. Installation (Java & XML)
online resources: 10.1. Selecting a Framework (Java & XML)
platform-specific technologies, drawbacks of: 10.1.1. Stability (Java & XML)
SAX support, importance of: 10.1.2. Integration with Other XML Tools and APIs (Java & XML)
selection: 10.1. Selecting a Framework (Java & XML)
parser support: 10.1.2. Integration with Other XML Tools and APIs (Java & XML)
production usage: 10.1.3. Production Presence (Java & XML)
stability considerations: 10.1.1. Stability (Java & XML)
standardization, absence of: 10.1.4. Making the Decision (Java & XML)
web development, and: 10. Web Publishing Frameworks (Java & XML)
XML files, publication: 10.3.1. Viewing XML Converted to HTML (Java & XML)
XSP: 10.4. XSP (Java & XML)
web search methods: 7.4. Prospects for Improved Web-Search Methods (XML in a Nutshell)
web services: 13.1. Web Services (Java & XML)
alternatives to: 14. Content Syndication (Java & XML)
clients, writing: 13.4.5. Writing a Client (Java & XML)
example: 13.4. Putting It All Together (Java & XML)
messaging services: 13.4.1. A Messaging Service (Java & XML)
online resources: 13.4.6. Where Do I Go From Here? (Java & XML)
SOAP, in addition to: 13.3. WSDL (Java & XML)
testing a client: 13.4.5. Writing a Client (Java & XML)
UDDI, registering with: 13.4.2. Registering with UDDI (Java & XML)
UDDI registry, searching in: 13.4.3. Searching a UDDI Registry (Java & XML)
web site for SAX: 1.4. A Short History of SAX (SAX2)
well-formed documents: 2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
3.1. XML Parsers (Perl & XML)
checkers: 2.9. Free-Form XML and Well-Formed Documents (Perl & XML)
writing: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker (Perl & XML)
3.2.1. Example: Well-Formedness Checker Revisited (Perl & XML)
WEB-INF directory: 6.2.1. WAR Files (Java and XSLT)
web pages, navigation areas: 8.1. XSLT Page Layout Templates
web.xml: 6.2.2. Deployment Descriptor (Java and XSLT)
page layout templates: 8.1. XSLT Page Layout Templates (Java and XSLT)
well-formed XML documents: 2.1.1.2. Elements (Java & XML)
well-formedness: 1.1. The Benefits of XML (XML in a Nutshell)
2. XML Fundamentals (XML in a Nutshell)
changing HTML to XHTML: 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
checking documents for: 2.10. Checking Documents for Well-Formedness (XML in a Nutshell)
constraints recognized by XML 1.0 parsers: 20.4.1. Well-Formedness Constraints (XML in a Nutshell)
errors: 1.3. How XML Works (XML in a Nutshell)
2.10. Checking Documents for Well-Formedness (XML in a Nutshell)
3. Document Type Definitions (DTDs) (XML in a Nutshell)
root element: 1.2.2. XML Syntax (Java and XSLT)
SAX filters, problems with: 19.3. Filters (XML in a Nutshell)
SAXParseException and: 25.1. The org.xml.sax Package (XML in a Nutshell)
summary of rules: 1.2.2. XML Syntax (Java and XSLT)
XSLT stylesheets and: 8.4. Templates and Template Rules (XML in a Nutshell)
well-formedness, vs. valid documents: 1.2.2.9. Well-formed versus valid documents (XSLT)
white-space property (CSS): 12.8. Text Properties (XML in a Nutshell)
whitespace: 1.6. XML Gotchas (Perl & XML)
1.2.2. XML Syntax (Java and XSLT)
4.1.1. Other ContentHandler Methods (SAX2)
empty tags and: 1.2.2.6. Empty tags can contain the end marker (XSLT)
ignorable_whitespace( ) method and: 5.7.2.1. Content event handlers (Perl & XML)
in element contents: 2.2. Elements, Tags, and Character Data (XML in a Nutshell)
in XML names: 2.4. XML Names (XML in a Nutshell)
preserving/removing: 2.4.6. Top-Level Elements (XSLT)
3.1.3. Attribute Nodes (XSLT)
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
reporting: 3.3.6. Element Data (Java & XML)
RSS and: 14.3.2. Creating an XML RSS Document (Java & XML)
stripping: 23.2. XSLT Elements (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
xs:whiteSpace facet element: 16.6.2.1. Handling whitespace (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
whitespace characters: 1.2.3.1.1. A sample DOM tree (XSLT)
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype (XSLT)
whitespace collapse: 4.2. Whitespace Processing (XML Schema)
4.8. Back to Our Library (XML Schema)
facets for: 5.1.1.1. Whitespace collapsed strings (XML Schema)
whitespace processing
collapsed strings and: 4.3.3. Collapsed Strings (XML Schema)
defining atomic datatypes that allow whitespace: 5.2.1. List Datatypes (XML Schema)
exceptions in: 4.3. String Datatypes (XML Schema)
lexical spaces and: 4.1. Lexical and Value Spaces (XML Schema)
whitespace replacement: 4.2. Whitespace Processing (XML Schema)
xs:string and xs:normalizedString types: 4.3.1. No Whitespace Replacement (XML Schema)
width property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell)
wildcard character (.): 6.4.2. Wildcard (XML Schema)
wildcards: 3.2.5. Using Wildcards (XSLT)
accommodating attributes/elements from other namespaces: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
creating open schemas: 13.2.2. Wildcards (XML Schema)
illustrated in sample stylesheet: 3.5.2. The Stylesheet (XSLT)
wildcards and location paths: 9.2.5. Wildcards (XML in a Nutshell)
Windows ANSI character set: 5.7.1. Cp1252 (XML in a Nutshell)
wireless applications: 10. Wireless Applications (Java and XSLT)
content types: 10.3.2.2. Setting the content type (Java and XSLT)
deployment descriptors: 10.3.2.2. Setting the content type (Java and XSLT)
future developments: 10.4. The Future of Wireless (Java and XSLT)
XML data files: 10.3.3.2. XML data (Java and XSLT)
wireless architecture: 10.2. The Wireless Architecture (Java and XSLT)
WML (Wireless Markup Language): 1.3.4. Wireless (Java and XSLT)
10.1. Wireless Technologies (Java and XSLT)
10.3.1. A WML Example (Java and XSLT)
example: 10.3.3.1. Storyboard (Java and XSLT)
online resources: 10.3.1. A WML Example (Java and XSLT)
prototyping: 10.3.3.3. WML prototypes and screen shots (Java and XSLT)
root element: 10.3.1. A WML Example (Java and XSLT)
servlets, implementation by: 10.3.3.4. Servlet implementation (Java and XSLT)
stylesheet: 10.3.4. WAP and XML (Java & XML)
XSLT stylesheets: 10.3.3.5. XSLT stylesheets (Java and XSLT)
WMLScript: 10.3.1.1. WMLScript and WBMP (Java and XSLT)
words, counting: 6.5.1.2. Counting words (XML Schema)
World Wide Web
HTML and: 2.1. A Brief History of XML (Perl & XML)
Perl and: 1.1. Why Use Perl with XML? (Perl & XML)
World Wide Web Consortium (see W3C)
1.2.4. XML Standards (XSLT)
World Wide Web Consortium (W3C): 2.1. A Brief History of XML (Perl & XML)
7.1. DOM and Perl (Perl & XML)
1.2. W3C XML Schema (XML Schema)
URI identifiers: 10.1. Perl and XML Namespaces (Perl & XML)
wrapped exceptions: 2.5.1. SAX2 Exception Classes (SAX2)
wrappers: 8.3. Wrappers and Decorators (Java & XML)
write( ) (ServletOutputStream): 8.4.2. XSLT Transformation Filter (Java and XSLT)
WriteMakeFile( ) function, installing parsers: 5.7.5. Installing Your Own Parser (Perl & XML)
Writer methods: 3.8. XML::Writer (Perl & XML)
wrong document exception: 6.5.1. The Dreaded WRONG DOCUMENT Exception (Java & XML)
WRONG_DOCUMENT_ERR exception (DOM): 24.2. Object Reference (XML in a Nutshell)
WSDL (Web Services Description Language): 13.3. WSDL (Java & XML)
1.2.3. XML-Based Applications (Java & XML)
elements: 13.3. WSDL (Java & XML)
files, data included in: 13.3. WSDL (Java & XML)
future developments: 13.4.4. WSDL (Java & XML)
online resources: 13.3. WSDL (Java & XML)
XML Schema, current usable version: 13.3. WSDL (Java & XML)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

Xalan processor: 14.2.2. XSLT from a Servlet (Java & XML)
0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
0.2. Software and Versions (Java and XSLT)
2.1.1. An XSLT Example (Java and XSLT)
extension elements, defining for: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
extensions and: 8. Extending XSLT (XSLT)
global parameters and: 4.4.3.1. Xalan (XSLT)
installing: 1.3. Installing Xalan (XSLT)
JAR files and CLASSPATH: 2.1.2. Trying It Out (Java and XSLT)
Spanish stylesheet transformation: 8.6.1. XSLT Stylesheet Design (Java and XSLT)
XDR (XML-Data-Reduced) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
XDuce programming language: A.3.3. The RELAX NG Family (XML Schema)
XEP (RenderX formatting program): 13.3.2. Generating the Finished Document (XML in a Nutshell)
Xerces-J parser: 2.10. Checking Documents for Well-Formedness (XML in a Nutshell)
16.2. Schema Basics (XML in a Nutshell)
Xerces Native Interface (XNI): B.3.2. APIs (XML Schema)
Xerces parser: 3.1.1. Obtaining a Parser (Java & XML)
1.6.3. Xerces (SAX2)
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags (SAX2)
3.3.2. XMLReader Feature Flags (SAX2)
#xFEFF Unicode character: 5.5.1. UCS-2 and UTF-16 (XML in a Nutshell)
XForms specification: 1.1.5. Guided Editing (XML Schema)
XFragment: 1.4. The Evolution of XML (XML in a Nutshell)
XHTML: 3.5. Other Kinds of SAX2 Event Producers (SAX2)
0.. What This Book Covers (XML in a Nutshell)
7. XML on the Web (XML in a Nutshell)
1.2.1. SGML, XML, and Markup Languages (Java and XSLT)
1.2.1. SGML, XML, and Markup Languages (Java and XSLT)
allowed in schema annotations: 14.2. The W3C XML Schema Annotation Element (XML Schema)
creating a valid document: 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
DTDs for: 7.1.2. Three DTDs for XHTML (XML in a Nutshell)
example of a valid document: 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
generation from XML with XSLT: 2.3. Another XSLT Example, Using XHTML (Java and XSLT)
ouptput, specification: 3.6.3.1. Specifying XHTML output (Java and XSLT)
moving from HTML to: 7.1.1. Moving from HTML to XHTML (XML in a Nutshell)
replacing standard DTD: 7.3.3. Mixing Your Own XHTML (XML in a Nutshell)
vs. HTML: 7.1. XHTML (XML in a Nutshell)
web browser support for: 7.1.3. Browser Support for XHTML (XML in a Nutshell)
XSLT, page layout with: 8.1. XSLT Page Layout Templates (Java and XSLT)
XHTML 1.1: 7. XML on the Web (XML in a Nutshell)
7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
creating your own XHTML: 7.3.3. Mixing Your Own XHTML (XML in a Nutshell)
list of modules: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
mixing applications into: 7.3.2. Mixing Your Applications into XHTML (XML in a Nutshell)
mixing modules into applications: 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
Module-based XHTML (W3C specification): 7.3.1. Mixing XHTML into Your Applications (XML in a Nutshell)
%xhtml-applet.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-base.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
XHTML Basic and RDDL: 14.2. RDDL Syntax (XML in a Nutshell)
%xhtml-basic-form.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-basic-table.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-bdo.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-csismap.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-edit.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-events.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
XHTML files, processing with DOM: 7.3. XML::DOM (Perl & XML)
%xhtml-form.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-frames.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-hypertext.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-iframe.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-image.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-inlstyle.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-legacy.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-link.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-list.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-meta.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-nameident.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-object.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-param.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-pres.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml11-ruby.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-script.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-ssismap.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-struct.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-style.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-table.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-target.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
%xhtml-text.module: 7.3. Authoring Compound Documents with Modular XHTML (XML in a Nutshell)
XInclude: 1.8. What XML Are We Talking About? (SAX2)
6.3. Including Subdocuments (SAX2)
1.4. The Evolution of XML (XML in a Nutshell)
XInclude specification
using instead of xs:include and xs:reference: 8.3.2. XInclude (XML Schema)
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery (XML Schema)
XLink: 16.1. XLink (Java & XML)
XLink (Extensible Linking Language): 0.. What This Book Covers (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
XLink specification
attributes: 10.7.2. XLink Attributes (XML Schema)
foreign attributes accepted by: 14.3. Foreign Attributes (XML Schema)
linking resources in schema annotations: 14.2. The W3C XML Schema Annotation Element (XML Schema)
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery (XML Schema)
XLink (XML Linking Language), specification for: 1.2.4.13. XML Linking Language (XLink) Version 1.0 (XSLT)
xlink:actuate: 16.1. XLink (Java & XML)
xlink:actuate attribute: 10.2.2. xlink:actuate (XML in a Nutshell)
xlink:arcrole attribute: 10.4.2.3. Arc roles (XML in a Nutshell)
rddl:resource elements and: 14.2. RDDL Syntax (XML in a Nutshell)
xlink:from attribute: 10.4.2. Arcs (XML in a Nutshell)
xlink:href attribute: 10.7. Schemas for XML, XML Base and XLink (XML Schema)
10.1. Simple Links (XML in a Nutshell)
locator elements and: 10.4.1. Locators (XML in a Nutshell)
rddl:resource elements and: 14.2. RDDL Syntax (XML in a Nutshell)
xlink:label attribute: 10.4.1. Locators (XML in a Nutshell)
local resources and: 10.4.3. Local Resources (XML in a Nutshell)
xlink:role attribute: 10.3. Link Semantics (XML in a Nutshell)
local resources and: 10.4.3. Local Resources (XML in a Nutshell)
locator elements and: 10.4.1. Locators (XML in a Nutshell)
rddl:resource elements and: 14.2. RDDL Syntax (XML in a Nutshell)
XLinks: 10. XLinks (XML in a Nutshell)
DTDs for: 10.6. DTDs for XLinks (XML in a Nutshell)
extended links: 10.4. Extended Links (XML in a Nutshell)
link behavior: 10.2. Link Behavior (XML in a Nutshell)
simple links: 10.1. Simple Links (XML in a Nutshell)
third-party links: 10.5. Linkbases (XML in a Nutshell)
XPointers and: 11.2. XPointers in Links (XML in a Nutshell)
xlink:show attribute: 16.1. XLink (Java & XML)
xlink:title attribute: 10.3. Link Semantics (XML in a Nutshell)
arc elements and: 10.4.2.2. Arc titles (XML in a Nutshell)
local resources and: 10.4.3. Local Resources (XML in a Nutshell)
locator elements and: 10.4.1. Locators (XML in a Nutshell)
xlink:to attribute: 10.4.2. Arcs (XML in a Nutshell)
xlink:type attribute: 10.1. Simple Links (XML in a Nutshell)
arc elements and: 10.4.2. Arcs (XML in a Nutshell)
extended links and: 10.4. Extended Links (XML in a Nutshell)
local resources and: 10.4.3. Local Resources (XML in a Nutshell)
locator elements and: 10.4.1. Locators (XML in a Nutshell)
values of: 10.1. Simple Links (XML in a Nutshell)
XML (Extensible Markup Language): 1.1. XML Matters (Java & XML)
2. Nuts and Bolts (Java & XML)
2. An XML Recap (Perl & XML)
1.2. XML Basics (XSLT)
APIs for: 1. The Simple API for XML (SAX2)
basics: 3. XML Basics: Reading and Writing (Perl & XML)
benefits of: 1.1. The Benefits of XML (XML in a Nutshell)
browser-dependent styling: 10.3.3. Browser-Dependent Styling (Java & XML)
business-to-business real world model: 14. Content Syndication (Java & XML)
canonical, specification for: 1.2.4.10. Canonical XML Version 1.0 (XSLT)
case sensitivity: 1.2.2. XML Syntax (Java and XSLT)
comments: 2.7. Comments (XML in a Nutshell)
communications protocols and: 15.1.2. Communications (XML in a Nutshell)
constraints: 2.2. Constraints (Java & XML)
data binding: 1.2.2. High-Level APIs (Java & XML)
15. Data Binding (Java & XML)
Zeus: 15.3. Zeus (Java & XML)
datafiles, internationalization: 8.6.3.1. XML data (Java and XSLT)
data format: 1.2. Portable Data (XML in a Nutshell)
15. XML as a Data Format (XML in a Nutshell)
data sharing, applications and companies: 14. Content Syndication (Java & XML)
declarations: 2.7. The XML Declaration (Perl & XML)
design: 7.2.2. XML Samples (Java and XSLT)
displaying in web browsers: 7.2. Direct Display of XML in Browsers (XML in a Nutshell)
documents: 2.1. XML Documents and XML Files (XML in a Nutshell)
associating stylesheets with: 12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
counting elements/attributes: 19.1. The ContentHandler Interface (XML in a Nutshell)
default character set for: 5.9. The Default Character Set for XML Documents (XML in a Nutshell)
grammar for: 20.5. XML Document Grammar (XML in a Nutshell)
narrative-oriented: 6.2. Narrative Document Structures (XML in a Nutshell)
nodes in: 8.7. The Built-in Template Rules (XML in a Nutshell)
parsing with DOM: 18.6. Parsing a Document with DOM (XML in a Nutshell)
permanence of: 6.5. Document Permanence (XML in a Nutshell)
syntax structures: 20.3.1. Global Syntax Structures (XML in a Nutshell)
transforming and presenting: 6.6. Transformation and Presentation (XML in a Nutshell)
tree structure of: 9.1. The Tree Structure of an XML Document (XML in a Nutshell)
validating: 1.1. The Benefits of XML (XML in a Nutshell)
3.1. Validation (XML in a Nutshell)
16.1. Overview (XML in a Nutshell)
document fragments, working with: 14.1.2. Entering the Books (Java & XML)
discussion form, sample data: 7.2.2. XML Samples (Java and XSLT)
dynamic generation in web applications: 1.1. Java, XSLT, and the Web (Java and XSLT)
elements: 2.1.1.2. Elements (Java & XML)
empty: 1.2.2. XML Syntax (Java and XSLT)
naming: 3.3.5. Element Callbacks (Java & XML)
entities: 2.5. Entities (Perl & XML)
errors involving version of: 3.4.4. Breaking the Data (Java & XML)
files, viewing as HTML: 10.3.1. Viewing XML Converted to HTML (Java & XML)
history of: 2. An XML Recap (Perl & XML)
HTML, and: 1. Introduction (Java and XSLT)
HTML forms, data generation from: 14.1.2. Entering the Books (Java & XML)
identifying with namespaces: 2.1.2. Namespaces (Java & XML)
Infoset: 1.2.1. Stream-Based Processing (SAX2)
1.8. What XML Are We Talking About? (SAX2)
B. SAX2 and the XML Infoset (SAX2)
Internet versus older technologies: 6.2.1. XML/Internet Versus Older Technologies (SAX2)
interoperability: 1.1.2. Interoperability (Java & XML)
13.1. Web Services (Java & XML)
Java, and, future developments: 16. Looking Forward (Java & XML)
Java, roles for in messaging: 6.2.2. Roles for Java in XML Messaging (SAX2)
JAXP, using for: 1.4.5. Sun's Java API for XML Processing (JAXP) (SAX2)
JDOM, generation using: 1.2.4.3. JDOM (Java and XSLT)
JDOM, production using: 7.3.3. JDOM XML Production (Java and XSLT)
logging file example: 3.5.1. An Example XML File (Java and XSLT)
messaging: 13.4.1. A Messaging Service (Java & XML)
messaging and: 6.2. XML and Messaging (SAX2)
namespace declarations, WML: 10.3.4. WAP and XML (Java & XML)
namespaces: 2.1.2. Namespaces (Java & XML)
2.3. Namespaces (Perl & XML)
OÕReilly publications about: 2. Nuts and Bolts (Java & XML)
PDF files, conversion to: 10.3.2. Viewing PDFs from XML (Java & XML)
Perl CGI scripts, outputting from: 14.1.2. Entering the Books (Java & XML)
Perl, using with: 1.1. Why Use Perl with XML? (Perl & XML)
piplines: 4.5. XML Pipelines (SAX2)
portability: 1.1.1. Portability (Java & XML)
problems with: 1.6. XML Gotchas (Perl & XML)
programming tools: 9.3. XML Programming Tools (Perl & XML)
recent developments: 0. Preface (Java & XML)
related standards of: 1.8. What XML Are We Talking About? (SAX2)
related technologies: 2.4. And More... (Java & XML)
RSS, conversion to using JDOM: 14.3.2. Creating an XML RSS Document (Java & XML)
SAX parsers and: 1.2.1. Stream-Based Processing (SAX2)
software tools, used with: 0.2. Software and Versions (Java and XSLT)
spacing for: 2.4. Spacing (Perl & XML)
specification for: 1.2.4.1. XML 1.0 (XSLT)
standardization: 1.1.1. Portability (Java & XML)
syntax: 1.2.2. XML Syntax (Java and XSLT)
stylesheets, structure for: 6.3.2. XML and Stylesheets (Java and XSLT)
transformations: 9.3.2.2. Transforming XML (Java & XML)
transformations in: 2.12. Transformations (Perl & XML)
transformations through XSLT: 0. Preface (Java and XSLT)
transforming XML types: 3.5. Schema Evolution (Java and XSLT)
valid documents: 1.2.3. Validation (Java and XSLT)
validity and: 2.4. Producer-Side Validation (SAX2)
versions: 0.3. Software and Versions (Java & XML)
webpage presentation and: 1.1.3. The XSLT Solution (Java and XSLT)
web publishing framework: 10. Web Publishing Frameworks (Java & XML)
well-formed documents: 1.2.2. XML Syntax (Java and XSLT)
1.2.2. XML Syntax (Java and XSLT)
2.2.1. XML Tree Data Structure (Java and XSLT)
XML Schema bindings: 16.3. XML Schema Bindings (Java & XML)
XPointer references, document using: 16.2. XPointer (Java & XML)
enterprise applications and: 15.1.1. Mixed Environments (XML in a Nutshell)
evolution of: 1.4. The Evolution of XML (XML in a Nutshell)
files: 2.1. XML Documents and XML Files (XML in a Nutshell)
filters, implementing: 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
how it works: 1.3. How XML Works (XML in a Nutshell)
name tokens: 3.3.1.2. NMTOKEN (XML in a Nutshell)
names: 2.4. XML Names (XML in a Nutshell)
3.3.1.2. NMTOKEN (XML in a Nutshell)
20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
namespaces (see namespaces)
object serialization and: 15.1.3. Object Serialization (XML in a Nutshell)
parsers: 1.3. How XML Works (XML in a Nutshell)
checking documents for well-formedness: 2.10. Checking Documents for Well-Formedness (XML in a Nutshell)
checking the character set: 5.9. The Default Character Set for XML Documents (XML in a Nutshell)
comparing namespace URIs: 4.2.3. Namespace URIs (XML in a Nutshell)
determining character encoding: 5.1. Character-Set Metadata (XML in a Nutshell)
event-based: 17.1.2. Treating XML as Events (XML in a Nutshell)
handling namespaces: 4.3. How Parsers Handle Namespaces (XML in a Nutshell)
text-processing tools and: 17.1.1. Treating XML as Text (XML in a Nutshell)
XMLReader interface and: 25.1. The org.xml.sax Package (XML in a Nutshell)
processing instructions (see processing instructions)
processing issues: 17.2. Common XML Processing Issues (XML in a Nutshell)
comments: 17.2.2. Comments (XML in a Nutshell)
links: 17.2.6. Links and References (XML in a Nutshell)
notations: 17.2.4. Notations (XML in a Nutshell)
references: 17.2.6. Links and References (XML in a Nutshell)
unparsed entities: 17.2.5. Unparsed Entities (XML in a Nutshell)
processing models: 17.1. Common XML Processing Models (XML in a Nutshell)
abstracting XML: 17.1.5. Abstracting XML Away (XML in a Nutshell)
event-based: 17.1.2. Treating XML as Events (XML in a Nutshell)
extensions/standards: 17.1.6. Standards and Extensions (XML in a Nutshell)
text-based: 17.1.1. Treating XML as Text (XML in a Nutshell)
tree-based: 17.1.3. Treating XML as Tree Models (XML in a Nutshell)
resources: 17. Programming Models (XML in a Nutshell)
schema languages: 1.1. The Benefits of XML (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
SGML and: 1.4. The Evolution of XML (XML in a Nutshell)
stylesheets: 7.2. Direct Display of XML in Browsers (XML in a Nutshell)
trees: 2.2.2. XML Trees (XML in a Nutshell)
XML 1.0, Blueberry (see Blueberry)
XML 1.0 comments: 14.4. XML 1.0 Comments (XML Schema)
XML 1.0 mode: 2.6.3. Namespace Feature Flags (SAX2)
XML 1.0 namespace: 10.7.1. XML Attributes (XML Schema)
XML 1.0 Recommendation: 2.1. The Basics (Java & XML)
XML attributes: 10.7.1. XML Attributes (XML Schema)
XML-based applications: 1.2.3. XML-Based Applications (Java & XML)
XML Canonicalization: B.3. Document Information Item (SAX2)
1.4. The Evolution of XML (XML in a Nutshell)
17.2.1. What You Get Is Not What You Saw (XML in a Nutshell)
XML Data Binding Resources: 1.1.4. Data Binding (XML Schema)
XML-Data proposal: 1.2. W3C XML Schema (XML Schema)
A.3.2. The W3C XML Schema Family (XML Schema)
XML-Data-Reduced (XDR) proposal: A.3.2. The W3C XML Schema Family (XML Schema)
XML declaration: 2.9. The XML Declaration (XML in a Nutshell)
web browser problems with: 7.1.3.1. The XML declaration and processing instructions (XML in a Nutshell)
XML specification for: 20.2. Annotated Sample Documents (XML in a Nutshell)
20.3.1. Global Syntax Structures (XML in a Nutshell)
XML declarations: 1.2.2.7. XML declarations (XSLT)
DOM Level 3: 6.4.1. The XML Declaration (Java & XML)
DOM Level 2, lack of exposure in: 5.2.3.3. Working with nodes (Java & XML)
XML-defined character sets: 5.4. XML-Defined Character Sets (XML in a Nutshell)
XML-DEV mailing list: 1.2.3. DOM and SAX (XSLT)
XML documents: 2.1.1. XML 1.0 (Java & XML)
2. The Obligatory Hello World Example (XSLT)
(see also stylesheets)
advanced techniques for: 7.4. More Sophisticated Techniques (XSLT)
binding
data: 1.1.4. Data Binding (XML Schema)
early vs. late: A.1.4. Early Binding Tools (XML Schema)
combining: 7. Combining XML Documents (XSLT)
content, manipulation via data binding: 15.1. First Principles (Java & XML)
designing tutorials and: 9.4. XML Document Design (XSLT)
DTD: 2.2.1. DTDs (Java & XML)
graphical tree, viewing as: 3.2.1. Instantiating a Reader (Java & XML)
grouping: 7.4.3. Grouping Across Multiple Documents (XSLT)
guided editing: 1.1.5. Guided Editing (XML Schema)
locator: 3.3.1. The Document Locator (Java & XML)
master document and: 7.2. The document( ) Function (XSLT)
open schemas and: 13.2. The Need for Open Schemas (XML Schema)
parsing: 3. SAX (Java & XML)
parse( ): 3.2.2. Parsing the Document (Java & XML)
querying support for: 1.1.3. Querying Support (XML Schema)
"Hello World" example of: 2. The Obligatory Hello World Example (XSLT)
rules for: 1.2.2. XML Document Rules (XSLT)
sorting: 7.4.1. The document( ) Function and Sorting (XSLT)
specification for associating stylesheets with: 1.2.4.8. Associating stylesheets with XML documents (XSLT)
system ID: 3.2.3. Using an InputSource (Java & XML)
transforming: 2.2.3. Transforming the XML Document (XSLT)
unstructured, creating links in: 5.3. Generating Links in Unstructured Documents (XSLT)
valid vs. well-formedness: 1.2.2.9. Well-formed versus valid documents (XSLT)
validating: 1.1.1. Validation (XML Schema)
2.3.2. W3C XML Schema Is Both About Structure and Datatyping (XML Schema)
validation: 1.3.2. A Parser (Java & XML)
contrasted with processing of DTDs: 4.1.2.1. Validation (Java & XML)
validity: 2.1.1.2. Elements (Java & XML)
views of: 7. Combining XML Documents (XSLT)
well-formed: 2.1.1.2. Elements (Java & XML)
XPath for describing parts of: 3. XPath: A Syntax for Describing Needles and Haystacks (XSLT)
XPath view of: 3.5. The XPath View of an XML Document (XSLT)
XML Encryption: 1.4. The Evolution of XML (XML in a Nutshell)
XML Information Set: 1.4. The Evolution of XML (XML in a Nutshell)
XML Infoset: 1.8. What XML Are We Talking About? (SAX2)
XML Linking Language, specification for: 1.2.4.13. XML Linking Language (XLink) Version 1.0 (XSLT)
XML Namespaces: 1.8. What XML Are We Talking About? (SAX2)
specification of: 1.4.2. SAX2 (SAX2)
XML parser classes: 3.1.1. Obtaining a Parser (Java & XML)
XML parser (Oracle): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
global parameters and: 4.4.3.5. Oracle (XSLT)
XML parsers: 1.3.2. A Parser (Java & XML)
DOM, and: 5.1.1. Language Bindings (Java & XML)
DOM features, verifying support: 6.3.1. Branching Out (Java & XML)
online sources: 1.3.2. A Parser (Java & XML)
XML plus namespace mode: 2.6.3. Namespace Feature Flags (SAX2)
XML Pointer Language, specification for: 1.2.4.12. XML Pointer Language (XPointer) Version 1.0 (XSLT)
XML Query Language: 1.4. The Evolution of XML (XML in a Nutshell)
XML-RPC: 1.2.3. XML-Based Applications (Java & XML)
11. XML-RPC (Java & XML)
6.2.2. Roles for Java in XML Messaging (SAX2)
advantages: 11.1.3. XML-RPC (Java & XML)
client application: 11.2.4. Writing the Client (Java & XML)
handler class: 11.2.2. Writing the Handler (Java & XML)
handlers: 11.3.1. A Shared Handler (Java & XML)
ÕHello, WorldÕ: 11.2. Saying Hello (Java & XML)
HTTP listener, creating: 11.2.3. Writing the Server (Java & XML)
invocation: 12.1.3. Invocation (Java & XML)
Java datatypes, supported: 11.2.2. Writing the Handler (Java & XML)
libraries: 11.2.1. XML-RPC Libraries (Java & XML)
SAX 2.0, lack of support for: 11.2.3. Writing the Server (Java & XML)
server classes: 11.3. Putting the Load on the Server (Java & XML)
configuration: 11.3.2. A Configurable Server (Java & XML)
port conflicts: 11.3.4. Talk to Me (Again) (Java & XML)
sorting method: 11.3.1. A Shared Handler (Java & XML)
server, loading: 11.2.3. Writing the Server (Java & XML)
servlets, compared to: 11.4.3. To Servlet or Not To Servlet (Java & XML)
XML, and: 11.4.1. Where's the XML in XML-RPC? (Java & XML)
XML Schema: 2.2.2. XML Schema (Java & XML)
2.11. Schemas (Perl & XML)
Castor, classes, generating with: 15.2.2. Source Generation (Java & XML)
purchase order (PO) document: 13.4.1. A Messaging Service (Java & XML)
schema specification, similarity to namespace declarations: 2.1.2. Namespaces (Java & XML)
schemaLocation attribute: 2.1.2. Namespaces (Java & XML)
SOAP, integration in: 12.1.2. Encoding (Java & XML)
XML Schema Datatypes (XSD): 2.6.4. ContentHandler and Prefix Mappings (SAX2)
XML Schema Language (see entries under schemas)
XML schema languages: A.1. What Is a XML Schema Language? (XML Schema)
challenges presented by namespaces to: 10.1. Namespaces Present Two Challenges to Schema Languages (XML Schema)
classifying: A.2. Classification of XML Schema Languages (XML Schema)
history of: A.3. A Short History of XML Schema Languages (XML Schema)
modeling tools: A.1.3. Intrusive Modeling Tools (XML Schema)
open vs. closed: A.1.2. Firewalls Against Diversity (XML Schema)
sample applications: A.4. Sample Application (XML Schema)
XML Signatures: 1.4. The Evolution of XML (XML in a Nutshell)
XML Spy: 2.1.2. Trying It Out (Java and XSLT)
7.2.3. XSLT Stylesheets (Java and XSLT)
xml-string property: 4.1.2.4. DOM nodes and literal strings (Java & XML)
XML Style Language: Transformations (see XSLT)
XML Stylesheet Language for Transformations (XSLT): 2.12. Transformations (Perl & XML)
xml-stylesheet processing instruction: 2.8. Processing Instructions (XML in a Nutshell)
associating stylesheets with XML documents: 12.3. Associating Stylesheets with XML Documents (XML in a Nutshell)
pseudoattributes used in: 7.2.1. The xml-stylesheet Processing Instruction (XML in a Nutshell)
xml-stylesheet processing instructions
XSLT stylesheets and: 8.3.2. The xml-stylesheet Processing Instruction (XML in a Nutshell)
XML transformations: 2.3. Transformations (Java & XML)
9.3.2. The TrAX API (Java & XML)
XML Validation Form (Brown University): 3.1.4. Validating a Document (XML in a Nutshell)
XML vocabularies, documenting: 1.1.2. Documentation (XML Schema)
xml:base attribute: 1.8. What XML Are We Talking About? (SAX2)
5.1.1. The AttributesImpl Class (SAX2)
5.1.1. The AttributesImpl Class (SAX2)
B.2.2. Base URIs, xml:base, and Locator Data (SAX2)
10.7.1. XML Attributes (XML Schema)
xmlDecl( ) Writer method: 3.8. XML::Writer (Perl & XML)
xml_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers (Perl & XML)
XMLOutputter: 5.3.6.3. JDOM to text approach (Java and XSLT)
XML::DOM module: 7.1. DOM and Perl (Perl & XML)
7.3. XML::DOM (Perl & XML)
XMLFilter class: 4.3.1. XMLFilters (Java & XML)
A.1.1.16. XMLFilter (Java & XML)
XMLFilter interface: 3.5.4. The XMLFilter Interface (SAX2)
A.1.16. The XMLFilter Interface (SAX2)
pull modes with XSLT and: 4.5.3.2. SAX in Pull-Mode with XSLT (SAX2)
XMLFilter interface (SAX): 19.3. Filters (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
XMLFilterImpl class: 4.3.1. XMLFilters (Java & XML)
A.1.3.8. XMLFilterImpl (Java & XML)
3.5.4. The XMLFilter Interface (SAX2)
4.5.1. The XMLFilterImpl Class (SAX2)
6.1.2. Consuming and Producing RSS Parsing Events (SAX2)
A.2.8. The XMLFilterImpl Class (SAX2)
examples of: 4.5.2. XMLFilter Examples (SAX2)
XMLFilterImpl class (SAX): 19.3. Filters (XML in a Nutshell)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
XML::Generator::DBI module: 9.3.1. XML::Generator::DBI (Perl & XML)
XML::Grove module: 6.6. XML::Grove (Perl & XML)
XML::Handler::Subs module: 5.5. A Handler Base Class (Perl & XML)
XML::Handler::YAWriter module: 5.6. XML::Handler::YAWriter as a Base Handler Class (Perl & XML)
9.3.1. XML::Generator::DBI (Perl & XML)
XMLin( ) method: 1.2. XML Is Simple with XML::Simple (Perl & XML)
6.2. XML::Simple (Perl & XML)
XML4J (XML for Java) parser: 1.6.3. Xerces (SAX2)
xml.jar, installing SAX 2.0: 1.7. Installing a SAX2 Parser (SAX2)
xml:lang attribute: 5.1.1. The AttributesImpl Class (SAX2)
10.7.1. XML Attributes (XML Schema)
5.11. xml:lang (XML in a Nutshell)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
3.1.3. Attribute Nodes (XSLT)
XML::LibXML module: 3.5. XML::LibXML (Perl & XML)
7.1. DOM and Perl (Perl & XML)
7.4. XML::LibXML (Perl & XML)
8.2. XPath (Perl & XML)
xmlns attribute
SAX core feature: 25.3. SAX Features and Properties (XML in a Nutshell)
setting default namespaces with: 4.2.4. Setting a Default Namespace with the xmlns Attribute (XML in a Nutshell)
20.3.4. Namespaces (XML in a Nutshell)
in XPath data model: 9.1. The Tree Structure of an XML Document (XML in a Nutshell)
xmlns attributes, using XMLReader feature flags: 3.3.2. XMLReader Feature Flags (SAX2)
xml.org (DTD repository): 3.10. Locating Standard DTDs (XML in a Nutshell)
XMLout( ) subroutine: 1.2. XML Is Simple with XML::Simple (Perl & XML)
XMLOutputter: 7.1.3. Input and Output (Java & XML)
A.4.4. Package: org.jdom.output (Java & XML)
namespace declarations: 8.1.3. The Namespace Class (Java & XML)
XMLOutputter class: 7.2.4. Outputting XML with JDOM (Java & XML)
7.2.4. Outputting XML with JDOM (Java & XML)
XML::Parser module: 1.3. XML Processors (Perl & XML)
3.2. XML::Parser (Perl & XML)
character encodings and: 3.9.3. Other Encodings (Perl & XML)
event streams and: 4.6. XML::Parser (Perl & XML)
styles for: 3.2.2. Parsing Styles (Perl & XML)
tree mode: 6.3. XML::Parser's Tree Mode (Perl & XML)
using parsers: 3.4. Putting Parsers to Work (Perl & XML)
well-formed checkers, writing: 3.2.1. Example: Well-Formedness Checker Revisited (Perl & XML)
XML::RSS module and: 9.2.2.2. Inheriting from XML::Parser (Perl & XML)
XML::Parser::Expat module: 3.2. XML::Parser (Perl & XML)
XML::Parser::PerlSAX module: 5.2. DTD Handlers (Perl & XML)
XMLProperties class: 7.3. XMLProperties (Java & XML)
incompatible versions: 7.3.3. Taking a Test Drive (Java & XML)
skeleton: 7.3. XMLProperties (Java & XML)
testing: 7.3.3. Taking a Test Drive (Java & XML)
XML::PYX module: 4.5. XML::PYX (Perl & XML)
XMLReader class: 2.2.1. How Do the Parts Fit Together? (SAX2)
A.1.17. The XMLReader Interface (SAX2)
configuring behavior: 3.3. Configuring XMLReader Behavior (SAX2)
feature flags: 3.3.2. XMLReader Feature Flags (SAX2)
functional groups: 3.1.1. The XMLReader Interface (SAX2)
JAXP, using: 3.2.3. Using JAXP (SAX2)
obtaining: 3.2. Bootstrapping an XMLReader (SAX2)
properties: 3.3.1. XMLReader Properties (SAX2)
pull mode event production and: 3.1. Pull Mode Event Production with XMLReader (SAX2)
XMLReader interface: 3.2. SAX Readers (Java & XML)
3.2.1. Instantiating a Reader (Java & XML)
A.1.1.17. XMLReader (Java & XML)
methods for setting: 4.1.1. Setting Properties and Features (Java & XML)
parse( ): 3.2.2. Parsing the Document (Java & XML)
XMLReader interface (SAX): 19. Simple API for XML (SAX) (XML in a Nutshell)
25.1. The org.xml.sax Package (XML in a Nutshell)
ContentHandler interface and: 19.1. The ContentHandler Interface (XML in a Nutshell)
counting elements/attributes in a document: 19.1. The ContentHandler Interface (XML in a Nutshell)
SAX filters and: 19.3. Filters (XML in a Nutshell)
validating parsers: 19.2. SAX Features and Properties (XML in a Nutshell)
XMLReaderAdapter class: A.1.3.9. XMLReaderAdapter (Java & XML)
A.2.9. The XMLReaderAdapter Class (SAX2)
XMLReaderAdapter class (SAX): 25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
XMLReaderFactory class: A.1.3.10. XMLReaderFactory (Java & XML)
3.2.1. The XMLReaderFactory Class (SAX2)
5.2. SAX1 Support (SAX2)
A.2.10. The XMLReaderFactory Class (SAX2)
XMLReaderFactory class (SAX): 19. Simple API for XML (SAX) (XML in a Nutshell)
25.2. The org.xml.sax.helpers Package (XML in a Nutshell)
XMLReaderFactory.createXMLReader( ): 2.2.1. How Do the Parts Fit Together? (SAX2)
XMLReader.getFeature( ) function: B.3. Document Information Item (SAX2)
XMLReader.parse( ) function: 3.1. Pull Mode Event Production with XMLReader (SAX2)
6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
6.2.3. XML Messaging over HTTP with SAX2 (SAX2)
B.1. Event Producer Issues (SAX2)
B.3. Document Information Item (SAX2)
InputSource class and: 3.1.2. The InputSource Class (SAX2)
XMLReaders, pipelining events: 4.3.1. XMLFilters (Java & XML)
XMLReader.setDTDHandler( ) function, binding DTDHandler to parsers: 4.3.2. The DTDHandler Interface (SAX2)
XMLReader.setEntityResolver( ) method: 3.4. The EntityResolver Interface (SAX2)
XmlRpc class: 11.2.3. Writing the Server (Java & XML)
XmlRpcClient class: 11.2.4. Writing the Client (Java & XML)
XML::RSS module: 9.2. XML::RSS (Perl & XML)
input, user and file: 9.2.4. Input: User or File (Perl & XML)
object model: 9.2.3. The Object Model (Perl & XML)
output and: 9.2.5. Off-the-Cuff Output (Perl & XML)
parsing: 9.2.2.1. Parsing (Perl & XML)
using: 9.2.2. Using XML::RSS (Perl & XML)
XML::SAX module: 3.4. Putting Parsers to Work (Perl & XML)
5.7. XML::SAX: The Second Generation (Perl & XML)
handlers for: 5.7.2. SAX2 Handler Interface (Perl & XML)
XML::SAXDriver::Excel module: 5.4. Drivers for Non-XML Sources (Perl & XML)
XML::SAX::ParserFactory module: 5.7. XML::SAX: The Second Generation (Perl & XML)
XML::Schematron modules: 3.7.2. Schemas (Perl & XML)
XML::Simple module: 1.2. XML Is Simple with XML::Simple (Perl & XML)
6.2. XML::Simple (Perl & XML)
XML::SimpleObject module: 6.4. XML::SimpleObject (Perl & XML)
xml:space attribute: 5.1.1. The AttributesImpl Class (SAX2)
10.7.1. XML Attributes (XML Schema)
20.3.2. DTD (Document Type Definition) (XML in a Nutshell)
3.1.3. Attribute Nodes (XSLT)
XML::TreeBuilder module: 6.5. XML::TreeBuilder (Perl & XML)
XMLUtils class: 13.4.5. Writing a Client (Java & XML)
XMLWriter: 2.2.3. XMLWriter: an Event Consumer (SAX2)
event pipelines and: 2.2.3.1. Event pipelines (SAX2)
4.5. XML Pipelines (SAX2)
XMLWriter class: 4.3.2. XMLWriter (Java & XML)
XML::Writer module: 3.8. XML::Writer (Perl & XML)
XML::XPath module: 3.6. XML::XPath (Perl & XML)
8.2. XPath (Perl & XML)
XNI (Xerces Native Interface): B.3.2. APIs (XML Schema)
XP: 1.3.2. A Parser (Java & XML)
XP Java parser: 1.4.1. SAX1 (SAX2)
XPath: 2.3.3. XPath (Java & XML)
3.6. XML::XPath (Perl & XML)
8.2. XPath (Perl & XML)
6.3. Including Subdocuments (SAX2)
0.. What This Book Covers (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
9. XPath (XML in a Nutshell)
3. XPath: A Syntax for Describing Needles and Haystacks (XSLT)
arithmetic operators in: 9.6.1. Numbers (XML in a Nutshell)
axes: B.3. XPath Axes (XSLT)
Boolean expressions: 9.6.3. Booleans (XML in a Nutshell)
22.2. Data Types (XML in a Nutshell)
Boolean functions: 9.7.3. Boolean Functions (XML in a Nutshell)
comparison operators: 9.6.2. Strings (XML in a Nutshell)
contains( ) function for: 5.2.3.3. Solution #2: Use the XPath contains( ) function (XSLT)
context: B.6. The XPath Context (XSLT)
data model: 9.1. The Tree Structure of an XML Document (XML in a Nutshell)
22.1. The XPath Data Model (XML in a Nutshell)
data model for: 3.1. The XPath Data Model (XSLT)
datatypes: 3.4. XPath Datatypes (XSLT)
expressions: 9.6. General XPath Expressions (XML in a Nutshell)
22.2. Data Types (XML in a Nutshell)
3. XPath: A Syntax for Describing Needles and Haystacks (XSLT)
XSLT elements and: 8.5. Calculating the Value of an Element with xsl:value-of (XML in a Nutshell)
functions: 9.7. XPath Functions (XML in a Nutshell)
22.5. XPath Functions (XML in a Nutshell)
functions, reference guide listing: C. XSLT and XPath Function Reference (XSLT)
location paths: 9.2. Location Paths (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
axes: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
node tests: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
node-set functions: 9.7.1. Node-Set Functions (XML in a Nutshell)
node sets in: 22.2. Data Types (XML in a Nutshell)
node tests for: 3.2.4.2. Selecting the text of an element (XSLT)
B.2. XPath Node Tests (XSLT)
node types: 3.1.1. The Root Node (XSLT)
B.1. XPath Node Types (XSLT)
numbers in: 9.6.1. Numbers (XML in a Nutshell)
22.2. Data Types (XML in a Nutshell)
numeric functions: 9.7.4. Number Functions (XML in a Nutshell)
predicates in location paths: 9.4. Predicates (XML in a Nutshell)
22.3. Location Paths (XML in a Nutshell)
22.4. Predicates (XML in a Nutshell)
reference: 22. XPath Reference (XML in a Nutshell)
relational operators: 9.4. Predicates (XML in a Nutshell)
9.6.2. Strings (XML in a Nutshell)
specification for, reference guide to: B. XPath Reference (XSLT)
string functions: 9.7.2. String Functions (XML in a Nutshell)
strings in: 9.6.2. Strings (XML in a Nutshell)
22.2. Data Types (XML in a Nutshell)
unabbreviated location paths: 9.5. Unabbreviated Location Paths (XML in a Nutshell)
view of XML document: 3.5. The XPath View of an XML Document (XSLT)
XPath data model, reading comments: 4.2. The LexicalHandler Interface (SAX2)
XPath expressions: 3.2.3. Relative and Absolute Expressions (XSLT)
XPath specification
embedded Schematron rules and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
identity checks: 9.2. XPath-Based Identity Checks (XML Schema)
namespaces and: 10.5. Namespaces and XPath Expressions (XML Schema)
permitted XPath expressions: 9.2.5. Permitted XPath Expressions (XML Schema)
querying support and: 1.1.3. Querying Support (XML Schema)
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery (XML Schema)
XPathDisplayNode class: 8.3.3. Providing Support for XPath (Java & XML)
xpointer( ): 11.1. XPointers on URLs (XML in a Nutshell)
XPointer specification: 8.3.2. XInclude (XML Schema)
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery (XML Schema)
XPointer (XML Pointer Language), specification for: 1.2.4.12. XML Pointer Language (XPointer) Version 1.0 (XSLT)
XPointers: 0.. What This Book Covers (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
11. XPointers (XML in a Nutshell)
bare names: 11.3. Bare Names (XML in a Nutshell)
changing context nodes for: 11.7.5. Relative XPointers (XML in a Nutshell)
in links: 11.2. XPointers in Links (XML in a Nutshell)
namespaces and: 11.5. Namespaces (XML in a Nutshell)
points in: 11.6. Points (XML in a Nutshell)
ranges in: 11.6. Points (XML in a Nutshell)
11.7. Ranges (XML in a Nutshell)
on URLs: 11.1. XPointers on URLs (XML in a Nutshell)
XQuery specification: 1.1.3. Querying Support (XML Schema)
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery (XML Schema)
XS library: 3.2. XML::Parser (Perl & XML)
xs:all element: 16.7.2. Controlling Element Placement (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:all (outside a group): 15.1. Datatypes (XML Schema)
adapting document structures to constraints of: 7.4.1.5.2. Adapting the structure of your document (XML Schema)
limitations of: 7.4.1.5.1. Limitations of xs:all (XML Schema)
role as compositor: 7.4.1.1. Compositors and particles (XML Schema)
12.1.1. Using a "Traditional" Group (XML Schema)
using xs:choice (outside a group) instead: 7.4.1.5.3. Using xs:choice instead of xs:all (XML Schema)
xs:all (within a group): 15.1. Datatypes (XML Schema)
xs:annotation element: 14.2. The W3C XML Schema Annotation Element (XML Schema)
15.1. Datatypes (XML Schema)
16.2.2. Annotations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:any element: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
11.1. Associating Schemas with Instance Documents (XML Schema)
15.1. Datatypes (XML Schema)
16.8. Allowing Any Content (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:anyAttribute element: 10.11. Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)
15.1. Datatypes (XML Schema)
16.8. Allowing Any Content (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:anySimpleType type: 21.3. Primitive Types (XML in a Nutshell)
xs:anyType type: 12.1.2.1. Using substitution groups (XML Schema)
16.5.1. complexContent (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xs:anyURI type: 4.3.3.3. URIs (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
constraining strings with regular expressions: 6.5.1.3. URIs (XML Schema)
xs:appinfo element: 13.2.2. Wildcards (XML Schema)
15.1. Datatypes (XML Schema)
16.2.2.2. The xs:appinfo element (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
schema adornments and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
source attribute and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
xs:annotation element and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
xs:attribute element: 16.2.5. Attribute Declarations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:attribute (global definition): 15.1. Datatypes (XML Schema)
xs:attribute (reference or local definition): 15.1. Datatypes (XML Schema)
xs:attributeGroup element: 16.2.5.1. Attribute groups (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:attributeGroup (global definition): 7.4.1.2. Element and attribute groups (XML Schema)
15.1. Datatypes (XML Schema)
xs:attributeGroup (reference): 15.1. Datatypes (XML Schema)
xs:base64Binary type: 4.3.3.5. Binary string-encoded datatypes (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xs:boolean type: 4.4.3. (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.7. Booleans (XML Schema)
xs:byte type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:choice element: 16.7.2. Controlling Element Placement (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:choice (outside a group): 15.1. Datatypes (XML Schema)
avoiding use of, when redefining groups: 8.2.2.1. Extension (XML Schema)
complex content, deriving by extension: 7.4.2.1. Derivation by extension (XML Schema)
compositors and particles: 7.4.1.1. Compositors and particles (XML Schema)
creating, using substitution groups: 12.1. Substitution Groups (XML Schema)
derivation by restriction: 12.1.4.2. Restricting substitution groups (XML Schema)
extending, through group redefinitions: 12.1.4.1. Extension of xs:choice through group redefinitions (XML Schema)
using instead of xs:all (outside a group): 7.4.1.5.3. Using xs:choice instead of xs:all (XML Schema)
xs:choice (within a group): 15.1. Datatypes (XML Schema)
xs:complexContent element: 15.1. Datatypes (XML Schema)
16.5.1. complexContent (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:complexType element: 16.2.5. Attribute Declarations (XML in a Nutshell)
16.4. Complex Types (XML in a Nutshell)
16.5.1. complexContent (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:complexType (global definition): 3.2.1. Depth Versus Modularity? (XML Schema)
15.1. Datatypes (XML Schema)
creating
complex content models: 7.4.1. Creation of Complex Content (XML Schema)
mixed content models: 7.5.1. Creating Mixed Content Models (XML Schema)
vs. xs:simpleType (global definition): 5. Creating Simple Datatypes (XML Schema)
xs:complexType (local definition): 15.1. Datatypes (XML Schema)
XSD (XML Schema Datatypes): 2.6.4. ContentHandler and Prefix Mappings (SAX2)
xs:date type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:dateTime type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:decimal type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.6. Decimals (XML Schema)
xs:documentation element: 15.1. Datatypes (XML Schema)
16.2.2.1. The xs:documentation element (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
source attribute and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
xs:annotation element and: 14.2. The W3C XML Schema Annotation Element (XML Schema)
xs:double type: 4.4.2. Float Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.3. Float datatypes (XML Schema)
xs:duration type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
defining datatypes with complete sort order: 6.5.3.1. Time zones (XML Schema)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:element element: 16.2.3. Element Declarations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:element (global definition): 2.2. Our First Schema (XML Schema)
15.1. Datatypes (XML Schema)
compositors and particles: 7.4.1.1. Compositors and particles (XML Schema)
creating complex contents: 7.4.1. Creation of Complex Content (XML Schema)
xs:element (reference or local definition): 15.1. Datatypes (XML Schema)
xs:element (within xs:all): 15.1. Datatypes (XML Schema)
xs:ENTITIES type: 4.6. List Types (XML Schema)
5.2.1. List Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.8. List datatypes (XML Schema)
xs:ENTITY type: 4.3.3.1. Tokenss (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xs:enumeration facet: 15.1. Datatypes (XML Schema)
date and time datatypes: 5.1.1.4.1. (XML Schema)
float datatypes: 5.1.1.3.1. (XML Schema)
list datatypes and: 5.2.1. List Datatypes (XML Schema)
patterns and: 6.2. The Simplest Possible Patterns (XML Schema)
union datatype and: 5.3. Derivation By Union (XML Schema)
whitespace-collapsed strings: 5.1.1.1.1. (XML Schema)
xs:enumeration facet element: 16.6.2.3. Enumerations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:extension (complex content): 15.1. Datatypes (XML Schema)
xs:extension element: 16.2.5. Attribute Declarations (XML in a Nutshell)
16.8.2.1. Deriving by extension (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:extension (simple content): 15.1. Datatypes (XML Schema)
xs:field element: 15.1. Datatypes (XML Schema)
16.9.4.1. Forcing uniqueness (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
permitted XPath expressions: 9.2.5. Permitted XPath Expressions (XML Schema)
xs:float type: 4.4.2. Float Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.3. Float datatypes (XML Schema)
xs:fractionDigits facet: 5.1.1.6.1. (XML Schema)
15.1. Datatypes (XML Schema)
fixing maximum number of digits: 6.5.2.2. Fixed format (XML Schema)
xs:fractionDigits facet element: 16.6.2.4.2. Length and precision (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:gDay type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:gMonth type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:gMonthDay type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:group (definition): 3.2.1. Depth Versus Modularity? (XML Schema)
15.1. Datatypes (XML Schema)
xs:group element: 16.7.3. Using Groups (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:group (reference): 15.1. Datatypes (XML Schema)
xs:gYear type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:gYearMonth type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:hexBinary type: 4.3.3.5. Binary string-encoded datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
xs:ID type: 4.3.3.1. Tokenss (XML Schema)
9.1. and (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
vs. xs:key/xs:keyref elements: 9.3. ID/IDREF Versus / (XML Schema)
XPath-based identity checks for: 9.2. XPath-Based Identity Checks (XML Schema)
xs:IDREF type: 4.3.3.1. Tokenss (XML Schema)
9.1. and (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
vs. xs:key/xs:keyref elements: 9.3. ID/IDREF Versus / (XML Schema)
XPath-based identity checks for: 9.2. XPath-Based Identity Checks (XML Schema)
xs:IDREFS type: 4.6. List Types (XML Schema)
5.2.1. List Datatypes (XML Schema)
9.1. and (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.8. List datatypes (XML Schema)
xs:import element: 10.6. Referencing Other Namespaces (XML Schema)
15.1. Datatypes (XML Schema)
16.8.1.3. Importing schemas for other namespaces (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
namespace behavior of imported components: 10.8. Namespace Behavior of Imported Components (XML Schema)
splitting schema components: 13.1.3. Splitting Schema Components (XML Schema)
xs:include element: 15.1. Datatypes (XML Schema)
16.8.1.1. Including external declarations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
alternatives to: 8.3. Other Alternatives (XML Schema)
schema inclusions and: 8.1. Schema Inclusion (XML Schema)
splitting schema components: 13.1.3. Splitting Schema Components (XML Schema)
target namespaces and: 10.10. Chameleon Design (XML Schema)
xsi:nil attribute: 11.3. Defining Nil (Null) Values (XML Schema)
21.4. Instance Document Attributes (XML in a Nutshell)
xsi:noNamespaceSchemaLocation attribute: 11.1. Associating Schemas with Instance Documents (XML Schema)
16.1. Overview (XML in a Nutshell)
21.4. Instance Document Attributes (XML in a Nutshell)
xs:int type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:integer type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xsi:schemaLocation attribute: 11.1. Associating Schemas with Instance Documents (XML Schema)
16.1. Overview (XML in a Nutshell)
16.3.1. Target Namespaces (XML in a Nutshell)
21.4. Instance Document Attributes (XML in a Nutshell)
xsi:type attribute: 11.2. Defining Element Types (XML Schema)
16.8.2.3. Using derived types (XML in a Nutshell)
21.4. Instance Document Attributes (XML in a Nutshell)
block attribute and: 12.2.2.1. Block attribute (XML Schema)
creating open schemas: 13.2.1. xsi:type (XML Schema)
xs:key element: 16.9.4.2. Keys and references (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:keyref element: 16.9.4.2. Keys and references (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:key/xs:keyref elements: 9.2.3. Keys (XML Schema)
15.1. Datatypes (XML Schema)
as co-occurrence constraints: 9.4. Using and As Co-occurrence Constraints (XML Schema)
defining prefixes for namespaces: 10.5. Namespaces and XPath Expressions (XML Schema)
vs. xs:ID/xs:IDREF types: 9.3. ID/IDREF Versus / (XML Schema)
XSL (Extensible Stylesheet Language): 2.3.1. XSL (Java & XML)
1.4. The Evolution of XML (XML in a Nutshell)
formatting objects: 2.3.1.2. Formatting objects (Java & XML)
Internet Explorer, stylesheet: 10.3.3. Browser-Dependent Styling (Java & XML)
looping: 2.3.3.2. Looping (Java & XML)
online resources, formatting objects definition: 10.3.2. Viewing PDFs from XML (Java & XML)
stylesheets
for viewing XML as HTML: 10.3.1. Viewing XML Converted to HTML (Java & XML)
in business-to-business communication: 14.2.1. Filtering XML Data (Java & XML)
PDF transformation: 10.3.2. Viewing PDFs from XML (Java & XML)
template matching: 2.3.3.1. Template matching (Java & XML)
templates in business-to-business communication: 14.2.1. Filtering XML Data (Java & XML)
tree structures: 2.3.1.1. XSL and trees (Java & XML)
web development, and: 10. Web Publishing Frameworks (Java & XML)
WML pages, developer tools: 10.3.4. WAP and XML (Java & XML)
XSL (Extensible Stylesheet Language), specifications for: 1.2.4.2. The Extensible Stylesheet Language (XSL) (XSLT)
XSL-FO stream: 9.5.6. Generating the PDF Files (XSLT)
XSL-FO (XSL Formatting Objects): 1.1. The Benefits of XML (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
12. Cascading Style Sheets (CSS) (XML in a Nutshell)
13. XSL Formatting Objects (XSL-FO) (XML in a Nutshell)
1.2.4.2. The Extensible Stylesheet Language (XSL) (XSLT)
1.2.4.2. The Extensible Stylesheet Language (XSL) (XSLT)
documents
boxes in: 13.1. XSL Formatting Objects (XML in a Nutshell)
generating: 13.3.2. Generating the Finished Document (XML in a Nutshell)
programs for working with: 13.3.2. Generating the Finished Document (XML in a Nutshell)
structure of: 13.2. The Structure of an XSL-FO Document (XML in a Nutshell)
laying out master pages: 13.3. Laying Out the Master Pages (XML in a Nutshell)
properties: 13.4. XSL-FO Properties (XML in a Nutshell)
vs. CSS: 13.5. Choosing Between CSS and XSL-FO (XML in a Nutshell)
XSL Formatting Objects stream: 9.5.6. Generating the PDF Files (XSLT)
XSL-LIST mailing list: 2.6. Summary (XSLT)
<xsl:output> element: 2.4.2. The <xsl:output> Element (XSLT)
<xsl:variable> element
preceding-sibling axis and: 6.2.3. Grouping with <xsl:variable> (XSLT)
<xsl:apply-imports> element: A. XSLT Reference (XSLT)
<xsl:apply-templates> element: A. XSLT Reference (XSLT)
<xsl:attribute-set> element: A. XSLT Reference (XSLT)
<xsl:attribute> element: A. XSLT Reference (XSLT)
<xsl:call-template> element: 4.3. Invoking Templates by Name (XSLT)
A. XSLT Reference (XSLT)
<xsl:choose> element: 4.2.2. The <xsl:choose> Element (XSLT)
A. XSLT Reference (XSLT)
using to create if-else statement: D.5. How Do I Implement an if-else Statement? (XSLT)
<xsl:comment> element: A. XSLT Reference (XSLT)
<xsl:copy-of> element: A. XSLT Reference (XSLT)
<xsl:copy> element: A. XSLT Reference (XSLT)
<xsl:decimal-format> element: A. XSLT Reference (XSLT)
<xsl:element> element: A. XSLT Reference (XSLT)
<xsl:fallback> element: A. XSLT Reference (XSLT)
<xsl:for-each> element: 4.2.3. The <xsl:for-each> Element (XSLT)
A. XSLT Reference (XSLT)
grouping with <xsl:variable> element and: 6.2.3. Grouping with <xsl:variable> (XSLT)
sorting and: 6.1.1. Our First Example (XSLT)
<xsl:if> element: 4.2.1. The <xsl:if> Element (XSLT)
A. XSLT Reference (XSLT)
<xsl:import> element: 2.4.6. Top-Level Elements (XSLT)
A. XSLT Reference (XSLT)
<xsl:include> element: 2.4.6. Top-Level Elements (XSLT)
A. XSLT Reference (XSLT)
<xsl:key> element: 2.4.6. Top-Level Elements (XSLT)
A. XSLT Reference (XSLT)
grouping with: 6.2.4. The <xsl:key> Approach (XSLT)
using to define key( ) function: 5.2.1. Defining a key( ) (XSLT)
<xsl:message> element: A. XSLT Reference (XSLT)
<xsl:namespace-alias> element: 4.8.3. Complications (XSLT)
A. XSLT Reference (XSLT)
<xsl:number> element: A. XSLT Reference (XSLT)
<xsl:otherwise> element: A. XSLT Reference (XSLT)
<xsl:choose> element and: 4.2.2. The <xsl:choose> Element (XSLT)
<xsl:output> element: A. XSLT Reference (XSLT)
<xsl:param> element: 2.4.6. Top-Level Elements (XSLT)
4.4. Parameters (XSLT)
A. XSLT Reference (XSLT)
<xsl:preserve-space> element: 2.4.6. Top-Level Elements (XSLT)
A. XSLT Reference (XSLT)
<xsl:processing-instruction> element: A. XSLT Reference (XSLT)
<xsl:sort> element: 6.1.2. The Details on the <xsl:sort> Element (XSLT)
A. XSLT Reference (XSLT)
document( ) function and: 7.4.1. The document( ) Function and Sorting (XSLT)
grouping elements/nodes using: 6.2. Grouping Nodes (XSLT)
sorting elements using: 6.1. Sorting Data with <xsl:sort> (XSLT)
<xsl:strip-space> element: 2.4.6. Top-Level Elements (XSLT)
A. XSLT Reference (XSLT)
<xsl:stylesheet> element: 2.4.1. The <xsl:stylesheet> Element (XSLT)
A. XSLT Reference (XSLT)
<xsl:template> elements: 2.3.1. Parsing the Stylesheet (XSLT)
A. XSLT Reference (XSLT)
recursive: 7.2.1. An Aside: Doing Math with Recursion (XSLT)
<xsl:text> element: A. XSLT Reference (XSLT)
D.12. How Do I Generate Some Text? (XSLT)
disable-output-escaping attribute of: D.13. How Do I Control Angle Brackets and Quote Marks in My Output? (XSLT)
<xsl:transform> element: A. XSLT Reference (XSLT)
<xsl:value-of> element: A. XSLT Reference (XSLT)
D.12. How Do I Generate Some Text? (XSLT)
<xsl:variable> element: 2.4.6. Top-Level Elements (XSLT)
4.5. Variables (XSLT)
A. XSLT Reference (XSLT)
grouping with: 6.2.3. Grouping with <xsl:variable> (XSLT)
<xsl:when> element: A. XSLT Reference (XSLT)
<xsl:choose> element and: 4.2.2. The <xsl:choose> Element (XSLT)
<xsl:with-param> element: 4.4. Parameters (XSLT)
A. XSLT Reference (XSLT)
xsl namespace prefixes: 1.2.2.11. Namespaces (XSLT)
xs:language type: 4.3.3.1. Tokenss (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xsl:apply-imports element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:apply-templates construct: 5.1.3.2. SAX siblings (Java & XML)
xsl:apply-templates element: 8.6. Applying Templates with xsl:apply-templates (XML in a Nutshell)
23.2. XSLT Elements (XML in a Nutshell)
xsl:attribute element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:attribute-set element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:call-template element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:choose element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:comment element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:copy element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:copy-of element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:decimal-format element: 9.7.2. String Functions (XML in a Nutshell)
23.2. XSLT Elements (XML in a Nutshell)
23.3. XSLT Functions (XML in a Nutshell)
xsl:element element: 23.2. XSLT Elements (XML in a Nutshell)
xs:length facet: 5.1.1.1.2. (XML Schema)
15.1. Datatypes (XML Schema)
adding constraints on datatypes: 5.5. Back to Our Library (XML Schema)
list datatypes and: 5.2.1. List Datatypes (XML Schema)
redefining not allowed: 5.1.2.2. Facet that cannot be changed (XML Schema)
xs:length facet element: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xsl:fallback element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:for-each element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:if element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:import element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:include element: 23.2. XSLT Elements (XML in a Nutshell)
xs:list element: 5.2.1. List Datatypes (XML Schema)
15.1. Datatypes (XML Schema)
16.6.2.6. Lists (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xsl:key element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:message element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:namespace-alias element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:number element: 23.2. XSLT Elements (XML in a Nutshell)
xs:long type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xsl:otherwise element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:output element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:param element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:preserve-space element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:processing-instruction element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:sort element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:strip-space element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:stylesheet element: 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
23.2. XSLT Elements (XML in a Nutshell)
XSLT: 4.5.3. The javax.xml.transform.sax Package (SAX2)
0.. What This Book Covers (XML in a Nutshell)
1.4. The Evolution of XML (XML in a Nutshell)
8. XSL Transformations (XSLT) (XML in a Nutshell)
browser support for: 1.5. Web Browser Support for XSLT (Java and XSLT)
code generation using: 8.5. XSLT as a Code Generator (Java and XSLT)
conflict-resolution rules: 8.6.1. XSLT Stylesheet Design (Java and XSLT)
design of: 1. Getting Started (XSLT)
EJB, and: 4.3. XSLT and EJB (Java and XSLT)
extensions for: 8. Extending XSLT (XSLT)
8. Extending XSLT (XSLT)
functions: 23.3. XSLT Functions (XML in a Nutshell)
reference guide listing: C. XSLT and XPath Function Reference (XSLT)
internationalization: 8.6. Internationalization with XSLT (Java and XSLT)
Internet Explorer and: 7.2.2. Internet Explorer (XML in a Nutshell)
Java, and: 5.1. A Simple Example (Java and XSLT)
key applications of: 1.1. The Design of XSLT (XSLT)
Mozilla and: 7.2.3. Netscape and Mozilla (XML in a Nutshell)
MVC architecture, and: 4.2.3. XSLT Implementation (Java and XSLT)
Netscape and: 7.2.3. Netscape and Mozilla (XML in a Nutshell)
online resources: 2.1.1. An XSLT Example (Java and XSLT)
page layout templates, used for: 8.1. XSLT Page Layout Templates (Java and XSLT)
processors: 8. XSL Transformations (XSLT) (XML in a Nutshell)
8.3. Stylesheet Processors (XML in a Nutshell)
problems with incorrect namespace URIs: 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
push mode with XSLT: 4.5.3.1. SAX in Push-Mode with XSLT (SAX2)
session tracking: 8.2.2. Session Tracking with Java and XSLT (Java and XSLT)
skeleton stylesheet: 2.2.4. A Skeleton Stylesheet (Java and XSLT)
specifications for: 1.2.4. XML Standards (XSLT)
reference guide to: A. XSLT Reference (XSLT)
stylesheet parameters, declaration: 5.2.3. The Transformer Class (Java and XSLT)
stylesheets: 1.1. Java, XSLT, and the Web (Java and XSLT)
templates: 2.1.1. An XSLT Example (Java and XSLT)
testing: 9.2. Testing and Debugging (Java and XSLT)
transforming XML documents: 6.6. Transformation and Presentation (XML in a Nutshell)
17.1.4. Transformations (XML in a Nutshell)
WML implementation: 10.3.3.5. XSLT stylesheets (Java and XSLT)
WML prototyping: 10.3.3.3. WML prototypes and screen shots (Java and XSLT)
XML, advantages of use with: 2.1.1. An XSLT Example (Java and XSLT)
XPath, uses of: 2.4.1. How XSLT Uses XPath (Java and XSLT)
XSLT processor (Microsoft): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
XSLT processors: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
1.1. Java, XSLT, and the Web (Java and XSLT)
1.4.1. Java XSLT Processor Choices (Java and XSLT)
2.1. XSLT Introduction (Java and XSLT)
built in template rules: 2.2.3. Built-in Template Rules (Java and XSLT)
choosing: 1.4.1. Java XSLT Processor Choices (Java and XSLT)
defining extension elements for: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
extensions and: 8. Extending XSLT (XSLT)
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element (XSLT)
input and output: 5.3. Input and Output (Java and XSLT)
invocation from Java: 5. XSLT Processing with Java (Java and XSLT)
Java, advantages of: 1.4.1. Java XSLT Processor Choices (Java and XSLT)
JAXP: 1.4.1.4. JAXP (Java and XSLT)
JDOM support: 1.2.4.4. JDOM and DOM interoperability (Java and XSLT)
LotusXSL: 1.4.1.2. LotusXSL (Java and XSLT)
SAXON: 1.4.1.3. SAXON (Java and XSLT)
XT: 1.4.1.1. XT (Java and XSLT)
XSLT specification
embedding code snippets in schema annotations: 14.2. The W3C XML Schema Annotation Element (XML Schema)
querying support and: 1.1.3. Querying Support (XML Schema)
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery (XML Schema)
XSLT stylesheets (see stylesheets)
2. The Obligatory Hello World Example (XSLT)
adding templates to: 8.4. Templates and Template Rules (XML in a Nutshell)
as XML documents: 2.1.1. An XSLT Example (Java and XSLT)
attribute value templates: 8.9. Attribute Value Templates (XML in a Nutshell)
built-in template rules and: 8.7. The Built-in Template Rules (XML in a Nutshell)
caching: 6.4.1. Integration with the Stylesheet Cache (Java and XSLT)
8.1. XSLT Page Layout Templates (Java and XSLT)
combining multiple sheets: 3.3. Combining Multiple Stylesheets (Java and XSLT)
compiling: 5.4. Stylesheet Compilation (Java and XSLT)
caching: 5.4.2. A Stylesheet Cache (Java and XSLT)
CSS, contrasted with: 2.1. XSLT Introduction (Java and XSLT)
design considerations: 6.3.2. XML and Stylesheets (Java and XSLT)
discussion forum application: 7.2.3. XSLT Stylesheets (Java and XSLT)
discussion forum application, for: 7.2.3. XSLT Stylesheets (Java and XSLT)
editPersonalData.xslt: 6.3.2. XML and Stylesheets (Java and XSLT)
6.3.2. XML and Stylesheets (Java and XSLT)
elements, categories of: 23.2. XSLT Elements (XML in a Nutshell)
filename extensions: 2.3. Another XSLT Example, Using XHTML (Java and XSLT)
English: 8.6.1. XSLT Stylesheet Design (Java and XSLT)
flowing content into pages: 13.3.1. Flowing Content into the Pages (XML in a Nutshell)
for generating XHTML output from XML documents: 2.3. Another XSLT Example, Using XHTML (Java and XSLT)
importation: 8.6.1. XSLT Stylesheet Design (Java and XSLT)
internationalization: 8.6.3.2. XSLT stylesheets (Java and XSLT)
location via initialization parameters: 6.3.6. Locating Stylesheets with Initialization Parameters (Java and XSLT)
MIME media type for: 7.2.1.1. The required href and type pseudoattributes (XML in a Nutshell)
modes and: 8.8. Modes (XML in a Nutshell)
namespaces and: 8.10. XSLT and Namespaces (XML in a Nutshell)
23.1. The XSLT Namespace (XML in a Nutshell)
navigation areas, defining with: 8.1. XSLT Page Layout Templates (Java and XSLT)
pathnames, encoding of: 6.3.3. Source Code (Java and XSLT)
rddl:resource elements and: 14.2. RDDL Syntax (XML in a Nutshell)
session-tracking: 8.2.2. Session Tracking with Java and XSLT (Java and XSLT)
Spanish: 8.6.1. XSLT Stylesheet Design (Java and XSLT)
transforming documents into XSL-FO documents: 13.3.2. Generating the Finished Document (XML in a Nutshell)
13.4. XSL-FO Properties (XML in a Nutshell)
XML structure: 6.3.2. XML and Stylesheets (Java and XSLT)
XSL-FO and: 13. XSL Formatting Objects (XSL-FO) (XML in a Nutshell)
xsl:stylesheet element and: 8.2. xsl:stylesheet and xsl:transform (XML in a Nutshell)
23.2. XSLT Elements (XML in a Nutshell)
XSLT tools (Microsoft): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
global parameters and: 4.4.3.3. Microsoft's XSLT tools (XSLT)
xsl:template element: 8.4. Templates and Template Rules (XML in a Nutshell)
23.2. XSLT Elements (XML in a Nutshell)
xsl:text element: 23.2. XSLT Elements (XML in a Nutshell)
XSLTProcessor class: 14.2.2. XSLT from a Servlet (Java & XML)
XSLTProcessorFactory class: 14.2.2. XSLT from a Servlet (Java & XML)
xsl:transform element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:value-of element: 8.5. Calculating the Value of an Element with xsl:value-of (XML in a Nutshell)
23.2. XSLT Elements (XML in a Nutshell)
string( ) and: 9.7.2. String Functions (XML in a Nutshell)
xsl:variable element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:vendor property: 23.3. XSLT Functions (XML in a Nutshell)
xsl:vendor-url property: 23.3. XSLT Functions (XML in a Nutshell)
xsl:version property: 23.3. XSLT Functions (XML in a Nutshell)
xsl:when element: 23.2. XSLT Elements (XML in a Nutshell)
xsl:with-param element: 23.2. XSLT Elements (XML in a Nutshell)
xs:maxExclusive facet: 15.1. Datatypes (XML Schema)
date and time datatypes: 5.1.1.4.2. (XML Schema)
float datatypes: 5.1.1.3.2. (XML Schema)
xs:maxExclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:maxInclusive facet: 15.1. Datatypes (XML Schema)
date and time datatypes: 5.1.1.4.3. (XML Schema)
float datatypes: 5.1.1.3.3. (XML Schema)
xs:maxInclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:maxLength facet: 5.1.1.1.3. (XML Schema)
15.1. Datatypes (XML Schema)
changing behavior of facets: 5.4.2. Using or Abusing Lists to Change the Behavior of Length Constraining Facets (XML Schema)
list datatypes and: 5.2.1. List Datatypes (XML Schema)
xs:maxLength facet element: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:minExclusive facet: 15.1. Datatypes (XML Schema)
date and time datatypes: 5.1.1.4.4. (XML Schema)
float datatypes: 5.1.1.3.4. (XML Schema)
xs:minExclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:minInclusive facet: 15.1. Datatypes (XML Schema)
date and time datatypes: 5.1.1.4.5. (XML Schema)
float datatypes: 5.1.1.3.5. (XML Schema)
xs:minInclusive facet element: 16.6.2.4.1. Minimum and maximum values (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:minLength facet: 5.1.1.1.4. (XML Schema)
15.1. Datatypes (XML Schema)
changing behavior of facets: 5.4.2. Using or Abusing Lists to Change the Behavior of Length Constraining Facets (XML Schema)
list datatypes and: 5.2.1. List Datatypes (XML Schema)
xs:minLength facet element: 16.6.2.2. Restricting length (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:Name type: 4.3.3.1. Tokenss (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xs:NCName type: 4.3.3.1. Tokenss (XML Schema)
9.1. and (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
xs:negativeInteger type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:NMTOKEN type: 4.3.3.1. Tokenss (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xs:NMTOKENS type: 4.6. List Types (XML Schema)
5.2.1. List Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.8. List datatypes (XML Schema)
xs:nonNegativeInteger type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:nonPositiveInteger type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:normalizedString type: 4.2. Whitespace Processing (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.2. Other strings (XML Schema)
xs:notation element: 15.1. Datatypes (XML Schema)
21.2. Schema Elements (XML in a Nutshell)
xs:NOTATION type: 4.3.3.4. Notations (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xs:enumeration facet and: 5.1.1.1.1. (XML Schema)
XSP (Extensible Server Pages): 10.4. XSP (Java & XML)
creating pages: 10.4.1. Creating an XSP Page (Java & XML)
JSP, advantages compared to: 10.4. XSP (Java & XML)
XML, and: 10.4. XSP (Java & XML)
XSL stylesheets for: 10.4. XSP (Java & XML)
xs:pattern facet: 6.2. The Simplest Possible Patterns (XML Schema)
15.1. Datatypes (XML Schema)
booleans and: 5.1.1.7.1. (XML Schema)
character classes and: 6.4.3. Character Classes (XML Schema)
date and time datatypes: 5.1.1.4.6. (XML Schema)
float datatypes: 5.1.1.3.6. (XML Schema)
performing intersection of lexical spaces: 5.1.2.3. Facet that performs the intersection of the lexical spaces (XML Schema)
quantifiers and: 6.3. Quantifying (XML Schema)
union datatype and: 5.3. Derivation By Union (XML Schema)
whitespace-collapsed strings: 5.1.1.1.5. (XML Schema)
wildcard character and: 6.4.2. Wildcard (XML Schema)
xs:pattern facet element: 16.6.2.5. Enforcing format (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:positiveInteger type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:QName type: 4.3.3.2. Qualified names (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
xs:redefine element: 15.1. Datatypes (XML Schema)
16.8.1.2. Modifying external declarations (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
alternatives to: 8.3. Other Alternatives (XML Schema)
attribute/element groups
by extension: 8.2.2.1. Extension (XML Schema)
by restriction: 8.2.2.2. Restriction (XML Schema)
extending xs:choice (outside a group): 12.1.4.1. Extension of xs:choice through group redefinitions (XML Schema)
simple/complex types: 8.2.1. Redefining of Simple and Complex Types (XML Schema)
splitting schema components: 13.1.3. Splitting Schema Components (XML Schema)
target namespaces and: 10.10. Chameleon Design (XML Schema)
xs:restriction (complex content): 15.1. Datatypes (XML Schema)
xs:restriction element: 16.6.2. Facets (XML in a Nutshell)
16.8.2.2. Deriving by restriction (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:restriction (simple content): 15.1. Datatypes (XML Schema)
xs:restriction (simple type): 5.1. Derivation By Restriction (XML Schema)
15.1. Datatypes (XML Schema)
xs:schema element: 15.1. Datatypes (XML Schema)
16.2.1. Document Organization (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
blockDefault attribute of: 12.2.2.1. Block attribute (XML Schema)
blocking complex types: 12.2.3.1. Blocking complex types (XML Schema)
controlling namespace qualification: 16.3.2. Controlling Qualification (XML in a Nutshell)
global components: 2.3.3. Flat Design, Global Components (XML Schema)
namespace declarations and: 10.2. Namespace Declarations (XML Schema)
target namespaces and: 16.3.1. Target Namespaces (XML in a Nutshell)
xs:selector element: 15.1. Datatypes (XML Schema)
16.9.4.1. Forcing uniqueness (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
defining, using ID/IDREF types: 9.3. ID/IDREF Versus / (XML Schema)
permitted XPath expressions: 9.2.5. Permitted XPath Expressions (XML Schema)
xs:sequence element: 16.4. Complex Types (XML in a Nutshell)
16.7.2. Controlling Element Placement (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:sequence (outside a group): 15.1. Datatypes (XML Schema)
compositors and particles: 7.4.1.1. Compositors and particles (XML Schema)
creating complex content: 7.4.1. Creation of Complex Content (XML Schema)
xs:sequence (within a group): 15.1. Datatypes (XML Schema)
xs:short type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:simpleContent element: 15.1. Datatypes (XML Schema)
16.2.5. Attribute Declarations (XML in a Nutshell)
16.6. Simple Content (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:simpleType element: 16.4. Complex Types (XML in a Nutshell)
16.6. Simple Content (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:simpleType (global definition): 5. Creating Simple Datatypes (XML Schema)
12.2.4. Simple Types (XML Schema)
15.1. Datatypes (XML Schema)
defining list datatypes: 5.2.1. List Datatypes (XML Schema)
xs:simpleType (local definition): 15.1. Datatypes (XML Schema)
xs:string type: 4.2. Whitespace Processing (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
16.6. Simple Content (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.2. Other strings (XML Schema)
xs:time type: 4.5.2. Datatypes (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.4. Date and time datatypes (XML Schema)
xs:token type: 4.3.3.1. Tokenss (XML Schema)
16.1. Datatypes (XML Schema)
16.2.4. Simple Types (XML in a Nutshell)
21.3. Primitive Types (XML in a Nutshell)
xs:totalDigits facet: 5.1.1.5.1. (XML Schema)
15.1. Datatypes (XML Schema)
adding constraints on datatypes: 6.6. Back to Our Library (XML Schema)
fixing maximum number of digits: 6.5.2.2. Fixed format (XML Schema)
xs:totalDigits facet element: 16.6.2.4.2. Length and precision (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:union element: 5.3. Derivation By Union (XML Schema)
15.1. Datatypes (XML Schema)
16.6.2.7. Unions (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
xs:unique element: 15.1. Datatypes (XML Schema)
16.9.4.1. Forcing uniqueness (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
as co-occurrence constraint: 9.4. Using and As Co-occurrence Constraints (XML Schema)
defining prefixes for namespaces: 10.5. Namespaces and XPath Expressions (XML Schema)
key references and: 9.2.4. Key References (XML Schema)
xs:unsignedByte type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:unsignedInt type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:unsignedLong type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:unsignedShort type: 4.4.1. Decimal Types (XML Schema)
16.1. Datatypes (XML Schema)
21.3. Primitive Types (XML in a Nutshell)
facets for: 5.1.1.5. Integer and derived datatypes (XML Schema)
xs:whiteSpace facet: 5.1.1.2.1. (XML Schema)
15.1. Datatypes (XML Schema)
list datatypes and: 5.2.1. List Datatypes (XML Schema)
multiple facet definitions and: 5.1.2.4. Facet that does its job before the lexical space (XML Schema)
xs:whiteSpace facet element: 16.6.2.1. Handling whitespace (XML in a Nutshell)
21.2. Schema Elements (XML in a Nutshell)
XT processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use (XSLT)
extension elements, defining for: 8.1.1.2. Example: Using extension functions from multiple processors (XSLT)
global parameters and: 4.4.3.2. XT (XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Y

Yiddish language, Unicode block for: 26.3.9. Hebrew (XML in a Nutshell)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Z

Zapf Dingbats font, Unicode block for: 26.3.26. Dingbats (XML in a Nutshell)
zeros, removing: 6.5.2.1. Leading zeros (XML Schema)
Zeus: 15.3. Zeus (Java & XML)
classes, generating: 15.3.2. Class Generation (Java & XML)
installation: 15.3.1. Installation (Java & XML)
marshalling and unmarshalling: 15.3.3. Marshalling and Unmarshalling (Java & XML)
Zip Codes: 6.1.1. Our First Example (XSLT)
(see also grouping elements/nodes)
grouping by: 6.1.3. Another Example (XSLT)
zip files
generating: 9.5.8. Generating the Zip File (XSLT)
tutorials and: 9.1. About the Toot-O-Matic (XSLT)
9.3.6. Zip File (XSLT)
Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Java & XML, 2nd Edition

Book Home

by Brett McLaughlin
ISBN 0-596-00197-5
Second Edition, published August 2001.
(See the catalog page for this book.)

Table of Contents

Copyright Page
Preface
Chapter 1: Introduction
Chapter 2: Nuts and Bolts
Chapter 3: SAX
Chapter 4: Advanced SAX
Chapter 5: DOM
Chapter 6: Advanced DOM
Chapter 7: JDOM
Chapter 8: Advanced JDOM
Chapter 9: JAXP
Chapter 10: Web Publishing Frameworks
Chapter 11: XML-RPC
Chapter 12: SOAP
Chapter 13: Web Services
Chapter 14: Content Syndication
Chapter 15: Data Binding
Chapter 16: Looking Forward
Appendix A: API Reference
Appendix B: SAX 2.0 Features and Properties
Index
Colophon

Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Copyright (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book
Previous Next

Copyright © 2001, 2000 O'Reilly & Associates, Inc. All rights reserved.

Printed in the United States of America.

Published by O'Reilly & Associates, Inc., 101 Morris Street, Sebastopol, CA 95472.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safari.oreilly.com). For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are registered trademarks of O'Reilly & Associates, Inc. The association between the image of lions and Java and XML is a trademark of O'Reilly & Associates, Inc. Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc., in the United States and other countries. O'Reilly & Associates, Inc. is independent of Sun Microsystems.

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps.

While every precaution has been taken in the preparation of this book, the publisher assumes no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Previous | Home | Next
Table of Contents | Book Index | 0. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Preface (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Preface

When I wrote the preface to the first edition of Java & XML just over a year ago, I had no idea what I was getting into. I made jokes about XML appearing on hats and t-shirts; yet as I sit writing this, I'm wearing a t-shirt with "XML" emblazoned across it, and yes, I have a hat with XML on it also (in fact, I have two!). So, the promise of XML has been recognized, without any doubt. And that's good.

However, it has meant that more development is occurring every day, and the XML landscape is growing at a pace I never anticipated, even in my wildest dreams. While that's great for XML, it has made looking back at the first edition of this book somewhat depressing; why is everything so out of date? I talked about SAX 2.0, and DOM Level 2 as twinklings in eyes. They are now industry standard. I introduced JDOM, and now it's in JSR (Sun's Java Figure 0 Specification Request process). I hadn't even looked at SOAP, UDDI, WSDL, and XML data binding. They take up three chapters in this edition! Things have changed, to say the least.

If you're even remotely suspicious that you may have to work with XML in the next few months, this book can help. And if you've got the first edition lying somewhere on your desk at work right now, I invite you to browse the new one; I think you'll see that this book is still important to you. I've thrown out all the excessive descriptions of basic concepts, condensed the basic XML material into a single chapter, and rewritten nearly every example; I've also added many new examples and chapters. In other words, I tried to make this an in-depth technical book with lots of grit. It will take you beginners a little longer, as I do less handholding, but you'll find the knowledge to be gained much greater.

0.1. Organization

This book is structured in a very particular way: the first half of the book, Chapter 1, "Introduction" through Chapter 9, "JAXP", focuses on grounding you in XML and the core Java APIs for handling XML. For each of the three XML manipulation APIs (SAX, DOM, and JDOM), I'll give you a chapter on the basics, and then a chapter on more advanced concepts. Chapter 10, "Web Publishing Frameworks" is a transition chapter, starting to move up the XML "stack" a bit. It covers JAXP, which is an abstraction layer over SAX and DOM. The remainder of the book, Chapter 11, "XML-RPC" through Chapter 15, "Data Binding", focuses on specific XML topics that continually are brought up at conferences and tutorials I am involved with, and seek to get you neck-deep in using XML in your applications. These topics include new chapters on SOAP, data binding, and an updated look at business-to-business. Finally, there are two appendixes to wrap up the book. The summary of this content is as follows:

Chapter 1, "Introduction"
We will look at what all the hype is about, examine the XML alphabet soup, and spend time discussing why XML is so important to the present and future of enterprise development.

Chapter 2, "Nuts and Bolts"
This is a crash course in XML basics, from XML 1.0 to DTDs and XML Schema to XSLT to Namespaces. For readers of the first edition, this is the sum total (and then some) of all the various chapters on working with XML.

Chapter 3, "SAX"
The Simple API for XML (SAX), our first Java API for handling XML, is introduced and covered in this chapter. The parsing lifecycle is detailed, and the events that can be caught by SAX and used by developers are demonstrated.

Chapter 4, "Advanced SAX "
We'll push further with SAX in this chapter, covering less-used but still powerful items in the API. You'll find out how to use XML filters to chain callback behavior, use XML writers to output XML with SAX, and look at some of the less commonly used SAX handlers like LexicalHandler and DeclHandler.

Chapter 5, "DOM"
This chapter moves on through the XML landscape to the next Java and XML API, the DOM (Document Object Model). You'll learn DOM basics, find out what is in the current specification (DOM Level 2), and how to read and write DOM trees.

Chapter 6, "Advanced DOM"
Moving on through DOM, you'll learn about the various DOM modules like Traversal, Range, Events, CSS, and HTML. We'll also look at what the new version, DOM Level 3, offers and how to use these new features.

Chapter 7, "JDOM"
This chapter introduces JDOM, and describes how it is similar to and different from DOM and SAX. It covers reading and writing XML using this API.

Chapter 8, "Advanced JDOM"
In a closer examination of JDOM, we'll look at practical applications of the API, how JDOM can use factories with your own JDOM subclasses, and JAXP integration. You'll also see XPath in action in tandem with JDOM.

Chapter 9, "JAXP"
Now a full-fledged API with support for parsing and transformations, JAXP merits its own chapter. Here, we'll look at both the 1.0 and 1.1 versions, and you'll learn how to use this API to its fullest.

Chapter 10, "Web Publishing Frameworks"
This chapter looks at what a web publishing framework is, why it matters to you, and how to choose a good one. We then cover the Apache Cocoon framework, taking an in-depth look at its feature set and how it can be used to serve highly dynamic content over the Web.

Chapter 11, "XML-RPC"
In this chapter, we'll cover Remote Procedure Calls (RPC), its relevance in distributed computing as compared to RMI, and how XML makes RPC a viable solution for some problems. We'll then look at using XML-RPC Java libraries and building XML-RPC clients and servers.

Chapter 12, "SOAP"
In this chapter, we'll look at using configuration data in an XML format, and see why that format is so important to cross-platform applications, particularly as it relates to distributed systems and web services.

Chapter 13, "Web Services"
Continuing the discussions of SOAP and web services, this chapter details two important technologies, UDDI and WSDL.

Chapter 14, "Content Syndication"
Continuing in the vein of business-to-business applications, this chapter introduces another way for businesses to interoperate, using content syndication. You'll learn about Rich Site Summary, building information channels, and even a little Perl.

Chapter 15, "Data Binding"
Moving up the XML "stack," this chapter covers one of the higher-level Java and XML APIs, XML data binding. You'll learn what data binding is, how it can make working with XML a piece of cake, and the current offerings. I'll look at three frameworks: Castor, Zeus, and Sun's early access release of JAXB, the Java Architecture for XML Data Binding.

Chapter 16, "Looking Forward"
This chapter points out some of the interesting things coming up over the horizon, and lets you in on some extra knowledge on each. Some of these guesses may be completely off; others may be the next big thing.

Appendix A, "API Reference"
This appendix details all the classes, interfaces, and methods available for use in the SAX, DOM, JAXP, and JDOM APIs.

Appendix B, "SAX 2.0 Features and Properties"
This appendix details the features and properties available to SAX 2.0 parser implementations.

Previous | Home | Next
Copyright Page | Book Index | 0.2. Who Should Read This Book?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Who Should Read This Book? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

0.2. Who Should Read This Book?

This book is based on the premise that XML is quickly becoming (and to some extent has already become) an essential part of Java programming. The chapters instruct you in the use of XML and Java, and other than in Chapter 1, "Introduction", they do not focus on if you should use XML. If you are a Java developer, you should use XML, without question. For this reason, if you are a Java programmer, want to be a Java programmer, manage Java programmers, or are associated with a Java project, this book is for you. If you want to advance, become a better developer, write cleaner code, or have projects succeed on time and under budget; if you need to access legacy data, need to distribute system components, or just want to know what the XML hype is about, this book is for you.

I tried to make as few assumptions about you as possible; I don't believe in setting the entry point for XML so high that it is impossible to get started. However, I also believe that if you spent your money on this book, you want more than the basics. For this reason, I only assumed that you know the Java language and understand some server-side programming concepts (such as Java servlets and Enterprise JavaBeans). If you have never coded Java before or are just getting started with the language, you may want to read Learning Java by Pat Niemeyer and Jonathan Knudsen (O'Reilly) before starting this book. I do not assume that you know anything about XML, and start with the basics. However, I do assume that you are willing to work hard and learn quickly; for this reason we move rapidly through the basics so that the bulk of the book can deal with advanced concepts. Material is not repeated unless appropriate, so you may need to reread previous sections or flip back and forth as we use previously covered concepts in later chapters. If you know some Java, want to learn XML, and are prepared to enter some example code into your favorite editor, you should be able to get through this book without any real problem.

Previous | Home | Next
0. Preface | Book Index | 0.3. Software and Versions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Software and Versions (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

0.3. Software and Versions

This book covers XML 1.0 and the various XML vocabularies in their latest form as of July of 2001. Because various XML specifications covered are not final, there may be minor inconsistencies between printed publications of this book and the current version of the specification in question.

All the Java code used is based on the Java 1.2 platform. If you're not using Java 1.2 by now, start to work to get there; the collections classes alone are worth it. The Apache Xerces parser, Apache Xalan processor, Apache SOAP library, and Apache FOP libraries were the latest stable versions available as of June of 2000, and the Apache Cocoon web publishing framework used is Version 1.8.2. The XML-RPC Java libraries used are Version 1.0 beta 4. All software used is freely available and can be obtained online from http://java.sun.com, http://xml.apache.org, and http://www.xml-rpc.com.

The source for the examples in this book is contained completely within the book itself. Both source and binary forms of all examples (including extensive Javadoc not necessarily included in the text) are available online from http://www.oreilly.com/catalog/javaxml2/ and http://www.newInstance.com. All of the examples that could run as servlets, or be converted to run as servlets, can be viewed and used online at http://www.newInstance.com.

Previous | Home | Next
0.2. Who Should Read This Book? | Book Index | 0.4. Conventions Used in This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Conventions Used in This Book (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

0.4. Conventions Used in This Book

The following font conventions are used in this book.

Italic is used for:

  • Unix pathnames, filenames, and program names

  • Internet addresses, such as domain names and URLs

  • New terms where they are defined

Boldface is used for:

  • Names of GUI items: window names, buttons, menu choices, etc.

Constant Width is used for:

  • Command lines and options that should be typed verbatim

  • Names and keywords in Java programs, including method names, variable names, and class names

  • XML element names and tags, attribute names, and other XML constructs that appear as they would within an XML document

Previous | Home | Next
0.3. Software and Versions | Book Index | 0.5. Comments and Questions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Comments and Questions (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

0.5. Comments and Questions

Please address comments and questions concerning this book to the publisher:

O'Reilly & Associates, Inc.
101 Morris Street
Sebastopol, CA 95472
(800) 998-9938 (in the U.S. or Canada)
(707) 829-0515 (international or local)
(707) 829-0104 (fax)

You can also send us messages electronically. To be put on the mailing list or request a catalog, send email to:

info@oreilly.com

To ask technical questions or comment on the book, send email to:

bookquestions@oreilly.com

We have a web site for the book, where we'll list examples, errata, and any plans for future editions. You can access this page at:

http://www.oreilly.com/catalog/javaxml2/

For more information about this book and others, see the O'Reilly web site:

http://www.oreilly.com

Previous | Home | Next
0.4. Conventions Used in This Book | Book Index | 0.6. Acknowledgments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Acknowledgments (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

0.6. Acknowledgments

Well, here I am writing acknowledgments again. It's no easier to remember everybody this time than it was the first. My editor, Mike Loukides, keeps me up at night stressing out about getting things done, which is exactly what a good editor does! Kyle Hart, marketing superwoman, keeps things going and reminds me that there's light at the end of the tunnel. Tim O'Reilly and Frank Willison are patient, yet pushy, just what good bosses should be. And Bob Eckstein and Marc Loy were there for me for pesky Swing GUI problems. (Besides, Bob's just funny. Face it.) O'Reilly is as good as it gets, all around. I'm honored to be associated with them.

I also want to think the incredible team of reviewers for this book. Many times, these folks turned a chapter around in less than 24 hours, yet still managed to give honest technical feedback. These guys are a large part of why this book stayed technical. Robert Sese, Philip Nelson, and Victor Brilon, you guys are amazing. Of course, I've always got to thank my partner in crime, Jason Hunter, for being annoyingly dedicated to JDOM and other technical issues (take a night off, man!). Finally, my company, Lutris Technologies, is about as good a place as you could hope to work for. They let me work long hours on this book, with never a complaint. In particular, Yancy Lind, Paul Morgan, David Young, and Keith Bigelow are simply the best at what they do. Thanks, guys!

To my parents, Larry and Judy McLaughlin, thanks again. I love you both for putting up with your rather ambitious and driven son (you realize, of course, those characteristics also make for a terribly obnoxious child!). Sarah Jane, my aunt, and my grandparents, Dean and Gladys McLaughlin, don't ever think that because I don't see you often I don't think about you all the time. Granddad, I'm more thankful than you'll ever know that you're getting to see a second edition. I love you all.

To my second set of parents (my wife's folks), Gary and Shirley Greathouse, you're just the best. One day I'll learn to take these writing skills and explain what you both mean to me, but it might take a whole book on its own. I love you both, for your humor and your wisdom. To Quinn and Joni for providing such levity at Sunday lunches. To Lonnie and Laura, can't wait to see Baby J. To Bill and Terri for being friends, and very wise ones at that, and to Bill for being a pastor like no other.

The laughter in my life comes from several hilarious characters, and I just can't pass up mentioning them here: Kendra, Brittany, Lisette, Janay, Rocky, Dustin, Tony, Stephanie, Robbie, Erin, Angela, Mike, Matt, Carlos, and John. I'll see you all Sunday, and can we please stop going to Mazzio's? And to the nonhuman part of my life, my dogs: Seth, Charlie, Jake, Moses, Molly, and Daisy. You haven't lived until the cold tongue of a basset hound wakes you up in the morning.

Finally, to the two people that mean more to me than anyone; my grandfather, Robert Earl Burden, who one day I'll see again. I think about you every day, and my children will hear about you soon. Most of all, to my wife, Leigh. Words just don't cut it. One day all the songs and tears that have come to me because of what you mean to me will come out, and you'll finally understand how much you mean to me.

And to the Lord who got me this far. Even so, come Lord Jesus.

Previous | Home | Next
0.5. Comments and Questions | Book Index | 1. Introduction
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Introduction (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 1. Introduction

Contents:

XML Matters
What's Important?
The Essentials
What's Next?

Introductory chapters are typically pretty easy to write. In most books, you give an overview of the technology covered, explain a few basics, and try and get the reader interested. However, for this second edition of Java and XML, things aren't so easy. In the first edition, there were still a lot of people coming to XML, or skeptics wanting to see if this new type of markup was really as good as the hype. Over a year later, everyone is using XML in hundreds of ways. In a sense, you probably don't need an introduction. But I'll give you an idea of what's going to be covered, why it matters, and what you'll need to get up and running.

1.1. XML Matters

First, let me simply say that XML matters. I know that sounds like the beginning of a self-help seminar, but it's worth starting with. There are still many developers, managers, and executives who are afraid of XML. They are afraid of the perception that XML is "cutting-edge," and of XML's high rate of change. (This is a second edition, a year later, right? Has that much changed?) They are afraid of the cost of hiring folks like you and me to work in XML. Most of all, they are afraid of adding yet another piece to their application puzzles.

To try and assuage these fears, let me quickly run down the major reasons that you should start working with XML, today. First, XML is portable. Second, it allows an unprecedented degree of interoperability. And finally, XML matters. . . because it doesn't matter! If that's completely confusing, read on and all will soon make sense.

1.1.1. Portability

XML is portable. If you've been around Java long, or have ever wandered through Moscone Center at JavaOne, you've heard the mantra of Java: "portable code." Compile Java code, drop those .class or .jar files onto any operating system, and the code runs. All you need is a Java Runtime Environment (JRE) or Java Virtual Machine (JVM), and you're set. This has continually been one of Java's biggest draws, because developers can work on Linux or Windows workstations, develop and test code, and then deploy on Sparcs, E4000s, HP-UX, or anything else you could imagine.

As a result, XML is worth more than a passing look. Because XML is simply text, it can obviously be moved between various platforms. Even more importantly, XML must conform to a specification defined by the World Wide Web Consortium (W3C) at http://www.w3.org. This means that XML is a standard. When you send XML, it conforms to this standard; when some other application receives it, the XML still conforms to that standard. The receiving application can count on that. This is essentially what Java provides: any JVM knows what to expect, and as long as code conforms to those expectations, it will run. By using XML, you get portable data. In fact, recently you may have heard the phrase "portable code, portable data" in reference to the combination of Java and XML. It's a good saying, because it turns out (as not all marketing-type slogans do) to be true.

1.1.2. Interoperability

Second, XML allows interoperability above and beyond what we've ever seen in enterprise applications. Some of you probably think this is just another form of portability, but it's more than that. Remember that XML stands for the Extensible Markup Language. And it is extensibility that is so important in business interoperating. Consider HTML, the hypertext markup language, for example. HTML is a standard. It's all text. So, in those respects, it's just as portable as XML. In fact, clients using different browsers on different operating systems can all view HTML more or less identically. However, HTML is aimed specifically at presentation. You couldn't use HTML to represent a furniture manifest, or a billing invoice. That's because the standard tightly defines the allowed tags, the format, and everything else in HTML. This allows it to remain focused on presentation, which is both an advantage and a disadvantage.

However, XML says very little about the elements and content of a document. Instead, it focuses on the structure of the document; elements must begin and end, each attribute must have a single value, and so on. The content of the document and the elements and attributes used remain up to you. You can develop your own document formatting, content, and custom specifications for representing your data. And this allows interoperability. The various furniture chains can agree upon a certain set of constraints for XML, and then exchange data in those formats; they get all the advantages of XML (like portability), as well as the ability to apply their business knowledge to the data being exchanged to make it meaningful. A billing system can include a customized format appropriate for invoices, broadcast this format, and export and import invoices from other billing systems. XML's extensibility makes it perfect for cross-application operation.

Even more intriguing is the large number of vertical standards[1] being developed. Browse the ebXML project at http://www.ebxml.org and see what's going on. Here, businesses are working together to develop standards built upon XML that allow global electronic commerce. The telecommunications industry has undertaken similar efforts. Soon, vertical markets across the world will have agreed upon standards for exchanging data, all built on XML.

[1]A vertical standard, or vertical market, refers to a standard or market targeting a specific business. Instead of moving horizontally (where common functionality is preferred), the focus is on moving vertically, providing functionality for a specific audience, like shoe manufacturers or guitar makers.

1.1.3. It Doesn't Matter

When all is said and done, XML matters because it doesn't matter. I said this earlier, and I want to say it again, because it's at the root of why XML is so important. Proprietary solutions for data, formats that are binary and must be decoded in certain ways, and other data solutions all matter in the final analysis. They involve communication with other companies, extensive documentation, coding efforts, and reinvention of tools for transmission. XML is so attractive because you don't need any special expertise and can spend your time doing other things. In Chapter 2, "Nuts and Bolts", I describe in 25 or so pages most of what you'll ever need to author XML. It doesn't require documentation, because that documentation is already written. You don't need special encoders or decoders; there are APIs and parsers already written that handle all of this for you. And you don't have to incur risk; XML is now a proven technology, with millions of developers working, fixing, and extending it every day.

XML is important because it becomes such a reliable, unimportant part of your application. Write your constraints, encode your data in XML, and forget about it. Then go on to the important things; the complex business logic and presentation that involves weeks and months of thought and hard work. Meanwhile, XML will happily chug along representing your data with nary a whimper or whine (OK, I'm getting a bit dramatic, but you get the idea).

So if you've been afraid of XML, or even skeptical, jump on board now. It might be the most important decision, with the fewest side effects, that you'll ever make. The rest of this book will get you up and running with APIs, transport protocols, and more odds and ends than you can shake a stick at.

Previous | Home | Next
0.6. Acknowledgments | Book Index | 1.2. What's Important?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Important? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

1.2. What's Important?

Once you've accepted that XML can help you out, the next question is what part of it you need. As I mentioned earlier, there are literally hundreds of applications of XML, and trying to find the right one is not an easy task. I've got to pick out twelve or thirteen key topics from these hundreds, and manage to make them all applicable to you; not an easy task! Fortunately, I've had a year to gather feedback from the first edition of this book, and have been working with XML in production applications for well over two years now. That means that I've at least got an idea of what's interesting and useful. When you boil all the various XML machinery down, you end up with just a few categories.

1.2.1. Low-Level APIs

An API is an application programming interface, and a low-level API is one that lets you deal directly with an XML document's content. In other words, there is little to no preprocessing, and you get raw XML content to work with. It is the most efficient way to deal with XML, and also the most powerful. At the same time, it requires the most knowledge about XML, and generally involves the most work to turn document content into something useful.

The two most common low-level APIs today are SAX, the Simple API for XML, and DOM, the Document Object Model. Additionally, JDOM (which is not an acronym, nor is it an extension of DOM) has gained a lot of momentum lately. All three of these are in some form of standardization (SAX as a de facto, DOM by the W3C, and JDOM by Sun), and are good bets to be long-lasting technologies. All three offer you access to an XML document, in differing forms, and let you do pretty much anything you want with the document. I'll spend quite a bit of time on these APIs, as they are the basis for everything else you'll do in XML. I've also devoted a chapter to JAXP, Sun's Java API for XML Processing, which provides a thin abstraction layer over SAX and DOM.

1.2.2. High-Level APIs

High-level APIs are the next step up the ladder. Instead of offering direct access to a document, they rely on low-level APIs to do that work for them. Additionally, these APIs present the document in a different form, either more user-friendly, or modeled in a certain way, or in some form other than a basic XML document structure. While these APIs are often easier to use and quicker to develop with, you may pay an additional processing cost while your data is converted to a different format. Also, you'll need to spend some time learning the API, most likely in addition to some lower-level APIs.

In this book, the main example of a high-level API is XML data binding. Data binding allows for taking an XML document and providing that document as a Java object. Not a tree-based object, mind you, but a custom Java object. If you had elements named "person" and "firstName", you would get an object with methods like getPerson( ) and setFirstName( ). Obviously, this is a simple way to quickly get going with XML; hardly any in-depth knowledge is required! However, you can't easily change the structure of the document (like making that "person" element become an "employee" element), so data binding is suited for only certain applications. You can find out all about data binding in Chapter 14, "Content Syndication".

1.2.3. XML-Based Applications

In addition to APIs built specifically for working with a document or its content, there are a number of applications built on XML. These applications use XML directly or indirectly, but are focused on a specific business process, like displaying stylized web content or communicating between applications. These are all examples of XML-based applications that use XML as a part of their core behavior. Some require extensive XML knowledge, some require none; but all belong in discussions about Java and XML. I've picked out the most popular and useful to discuss here.

First, I'll cover web publishing frameworks, which are used to take XML and format them as HTML, WML (Wireless Markup Language), or as binary formats like Adobe's PDF (Portable Document Format). These frameworks are typically used to serve clients complex, highly customized web applications. Next, I'll look at XML-RPC, which provides an XML variant on remote procedure calls. This is the beginning of a complete suite of tools for application communication. Building on XML-RPC, I'll describe SOAP, the Simple Object Access Protocol, and how it expands upon what XML-RPC provides. Then you'll get to see the emerging players in the web services field by examining UDDI (Universal Discovery, Description, and Integration) and WSDL (Web Services Descriptor Language) in a business-to-business chapter. Putting all these tools in your toolbox will make you formidable not only in XML, but in any enterprise application environment.

And finally, in the last chapter I'll gaze into my crystal ball and point out what appears to be gathering strength in the coming months and years, and try and give you a heads-up on what is worth monitoring. This should keep you ahead of the curve, which is where any good developer should be.

Previous | Home | Next
1. Introduction | Book Index | 1.3. The Essentials
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Essentials (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

1.3. The Essentials

Now you're ready to learn how to use Java and XML to their best. What do you need? I will address that subject, give you some basics, and then let you get after it.

1.3.1. An Operating System and Java

I say this almost tongue in cheek; if you expect to get through this book with no OS (operating system) and no Java installation, you just might be in a bit over your head. Still, it's worth letting you know what I expect. I wrote the first half of this book and the examples for those chapters on a Windows 2000 machine, running both JDK 1.2 and JDK 1.3 (as well as 1.3.1). I did most of my compiling under Cygwin (from Cygnus), so I usually operate in a Unix-esque environment. The last half of the book was written on my (at the time) brand new Macintosh G4 running OS X. That system comes with JDK 1.3, and is a beauty, for those of you who are curious.

In any case, all the examples should work unchanged with Java 1.2 or above; I used no features of JDK 1.3. However, I did not write this code to compile under Java 1.1, as I felt using the Java 2 Collections classes was important. Additionally, if you're working with XML, you need to take a long hard look at updating your JDK if you're still on 1.1 (I know some of you have no choice). If you are stuck on a 1.1 JVM, you should be able to get the collections from Sun (http://java.sun.com), make some small modifications, and be up and running.

1.3.2. A Parser

You will need an XML parser. One of the most important layers to any XML-aware application is the XML parser. This component handles the important task of taking a raw XML document as input and making sense of the document; it will ensure that the document is well-formed, and if a DTD or schema is referenced, it may be able to ensure that the document is valid. What results from an XML document being parsed is typically a data structure that can be manipulated and handled by other XML tools or Java APIs. I'm going to leave the detailed discussions of these APIs for later chapters. For now, just be aware that the parser is one of the core building blocks to using XML data.

Selecting an XML parser is not an easy task. There are no hard and fast rules, but two main criteria are typically used. The first is the speed of the parser. As XML documents are used more often and their complexity grows, the speed of an XML parser becomes extremely important to the overall performance of an application. The second factor is conformity to the XML specification. Because performance is often more of a priority than some of the obscure features in XML, some parsers may not conform to finer points of the XML specification in order to squeeze out additional speed. You must decide on the proper balance between these factors based on your application's needs. In addition, most XML parsers are validating, which means they offer the option to validate your XML with a DTD or XML Schema, but some are not. Make sure you use a validating parser if that capability is needed in your applications.

Here's a list of the most commonly used XML parsers. The list does not show whether a parser validates or not, as there are current efforts to add validation to several of the parsers that do not yet offer it. No overall ranking is suggested here, but there is a wealth of information on the web pages for each parser:

  • Apache Xerces: http://xml.apache.org

  • IBM XML4J: http://alphaworks.ibm.com/tech/xml4j

  • James Clark's XP: http://www.jclark.com/xml/xp

  • Oracle XML Parser: http://technet.oracle.com/tech/xml

  • Sun Microsystems Crimson: http://xml.apache.org/crimson

  • Tim Bray's Lark and Larval: http://www.textuality.com/Lark

  • The Mind Electric's Electric XML: http://www.themindelectric.com/products/xml/xml.html

  • Microsoft's MXSML Parser: http://msdn.microsoft.com/xml/default.asp

WARNING: I've included Microsoft's MSXML parser in this list in deference to their efforts to address numerous compliance issues in their latest versions. However, their parser still tends to be "doing its own thing" and is not guaranteed to work with the examples in this book because of that. Use it if you need to, but be willing to do a little extra work if you make this decision.

Throughout this book, I tend to use Apache Xerces because it is open source. This is a huge plus to me, so I'd recommend you try out Xerces if you don't already have a parser selected.

1.3.3. APIs

Once you've gotten the parser part of the equation taken care of, you'll need the various APIs I'll be talking about (low-level and high-level). Some of these will be included with your parser download, while others need to be downloaded manually. I'll expect you to either have these on hand, or be able to get them from an Internet web site, so ensure you've got web access before getting too far into any of the chapters.

First, the low-level APIs: SAX, DOM, JDOM, and JAXP. SAX and DOM should be included with any parser you download, as those APIs are interface-based and will be implemented within the parser. You'll also get JAXP with most of these, although you may end up with an older version; hopefully by the time this book is out, most parsers will have full JAXP 1.1 (the latest production version) support. JDOM is currently bundled as a separate download, and you can get it from the web site at http://www.jdom.org.

As for the high-level APIs, I cover a couple of alternatives in the data binding chapter. I'll look briefly at Castor and Quick, available online at http://castor.exolab.org and http://sourceforge.net/projects/jxquick, respectively. I'll also take some time to look at Zeus, available at http://zeus.enhydra.org. All of these packages contain any needed dependencies within the downloaded bundles.

1.3.4. Application Software

Last in this list is the myriad of specific technologies I'll talk about in the chapters. These technologies include things like SOAP toolkits, WSDL validators, the Cocoon web publishing framework, and so on. Rather than try and cover each of these here, I'll address the more specific applications in appropriate chapters, including where to get the packages, what versions are needed, installation issues, and anything else you'll need to get up and running. I can spare you all the ugly details here, and only bore those of you who choose to be bored (just kidding! I'll try to stay entertaining). In any case, you can follow along and learn everything you need to know.

In some cases, I do build on examples in previous chapters. For example, if you start reading Chapter 6, "Advanced DOM" before going through Chapter 5, "DOM", you'll probably get a bit lost. If this occurs, just back up a chapter and you'll see where the confusing code originated. As I already mentioned, you can skim Chapter 2, "Nuts and Bolts" on XML basics, but I'd recommend you go through the rest of the book in order, as I try to logically build up concepts and knowledge.

Previous | Home | Next
1.2. What's Important? | Book Index | 1.4. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

1.4. What's Next?

Now you're probably ready to get on with it. In the next chapter, I'm going to give you a crash course in XML. If you're new to XML, or are shaky on the basics, this chapter will fill in the gaps. If you're an old hand to XML, I'd recommend you skim the chapter, and move on to the code in Chapter 3, "SAX". In either case, get ready to dive into Java and XML; things get exciting from here on in.

Previous | Home | Next
1.3. The Essentials | Book Index | 2. Nuts and Bolts
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Nuts and Bolts (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 2. Nuts and Bolts

Contents:

The Basics
Constraints
Transformations
And More...
What's Next?

With the introductions behind us, let's get to it. Before heading straight into Java, though, some basic structures must be laid down. These address a fundamental understanding of the concepts in XML and how the extensible markup language works. In other words, you need an XML primer. If you are already an XML expert, skim through this chapter to make sure you're comfortable with the topics addressed. If you're completely new to XML, on the other hand, this chapter can get you ready for the rest of the book without hours, days, or weeks of study.

Where Did All the Chapters Go?

Readers of the first edition of Java & XML may be a little confused. In that edition, there were (count 'em!) three full chapters just on XML itself. When I worked on the first edition over a year ago, I was faced with writing a book that was part XML, part Java, and couldn't completely address either. There was no other reliable resource to direct you to for additional help. Today, books like Learning XML by Erik Ray (O'Reilly) and XML in a Nutshell by Elliotte Rusty Harold and W. Scott Means (O'Reilly) have rectified that problem. It's now enough to give you a whirlwind tour of XML in this chapter, and let you refer to one of those excellent books for more detail on "pure" XML. As a result, I was able to condense several chapters into this one, paving the way for new chapters on Java, which I'm sure is what you want! Be prepared for some radical departures from the first edition; now at least you know why.

You can use this chapter as a glossary while you read the rest of the book. I won't spend time in future chapters explaining XML concepts, in order to deal strictly with Java and get to some more advanced concepts. So if you hit something that completely befuddles you, check this chapter for information. And if you are still a little lost, I highly recommended that this book be read with a copy of Elliotte Harold and Scott Means' excellent book XML in a Nutshell (O'Reilly) open. That will give you all the information you need on XML concepts, and then I can focus on Java ones.

Finally, I'm big on examples. I'm going to load the rest of the chapters as full of them as possible. I'd rather give you too much information than barely engage you. To get started along those lines, I'll introduce several XML and related documents in this chapter to illustrate the concepts in this primer. You might want to take the time to either type these into your editor or download them from the book's web site (http://www.newInstance.com), as they will be used in this chapter and throughout the rest of the book. It will save you time later on.

2.1. The Basics

It all begins with the XML 1.0 Recommendation, which you can read in its entirety at http://www.w3.org/TR/REC-xml. Example 2-1 shows a simple XML document that conforms to this specification. It's a portion of the XML table of contents for this book (I've only included part of it because it's long!). The complete file is included with the samples for the book, available online at http://www.oreilly.com/catalog/javaxml2 and http://www.newInstance.com. I'll use it to illustrate several important concepts.

Example 2-1. The contents.xml document

<?xml version="1.0"?>
<!DOCTYPE book SYSTEM "DTD/JavaXML.dtd">

<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>
  <title ora:series="Java">Java and XML</title>

  <!-- Chapter List -->
  <contents>
    <chapter title="Introduction" number="1">
      <topic name="XML Matters" />
      <topic name="What's Important" />
      <topic name="The Essentials" />
      <topic name="What&apos;s Next?" />
    </chapter>
    <chapter title="Nuts and Bolts" number="2">
      <topic name="The Basics" />
      <topic name="Constraints" />
      <topic name="Transformations" />
      <topic name="And More..." />
      <topic name="What&apos;s Next?" />
    </chapter>
    <chapter title="SAX" number="3">
      <topic name="Getting Prepared" />
      <topic name="SAX Readers" />
      <topic name="Content Handlers" />
      <topic name="Gotcha!" />
      <topic name="What&apos;s Next?" />
    </chapter>
    <chapter title="Advanced SAX" number="4">
      <topic name="Properties and Features" />
      <topic name="More Handlers" />
      <topic name="Filters and Writers" />
      <topic name="Even More Handlers" />
      <topic name="Gotcha!" />
      <topic name="What&apos;s Next?" />
    </chapter>
    <chapter title="DOM" number="5">
      <topic name="The Document Object Model" />
      <topic name="Serialization" />
      <topic name="Mutability" />
      <topic name="Gotcha!" />
      <topic name="What&apos;s Next?" />
    </chapter>

    <!-- And so on... -->

  </contents>

  <ora:copyright>&OReillyCopyright;</ora:copyright>
</book>

2.1.1. XML 1.0

A lot of this specification describes what is mostly intuitive. If you've done any HTML authoring, or SGML, you're already familiar with the concept of elements (such as contents and chapter in the example) and attributes (such as title and name). In XML, there's little more than definition of how to use these items, and how a document must be structured. XML spends more time defining tricky issues like whitespace than introducing any concepts that you're not at least somewhat familiar with.

An XML document can be broken into two basic pieces: the header, which gives an XML parser and XML applications information about how to handle the document; and the content, which is the XML data itself. Although this is a fairly loose division, it helps us differentiate the instructions to applications within an XML document from the XML content itself, and is an important distinction to understand. The header is simply the XML declaration, in this format:

<?xml version="1.0"?>

The header can also include an encoding, and whether the document is a standalone document or requires other documents to be referenced for a complete understanding of its meaning:

<?xml version="1.0" encoding="UTF8" standalone="no"?>

The rest of the header is made up of items like the DOCTYPE declaration:

<!DOCTYPE Book SYSTEM "DTD/JavaXML.dtd">

In this case, I've referred to a file on my local system, in the directory DTD/ called JavaXML.dtd. Any time you use a relative or absolute file path or a URL, you want to use the SYSTEM keyword. The other option is using the PUBLIC keyword, and following it with a public identifier. This means that the W3C or another consortium has defined a standard DTD that is associated with that public identifier. As an example, take the DTD statement for XHTML 1.0:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Here, a public identifier is supplied (the funny little string starting with "-//"), followed by a system identifier (the URL). If the public identifier cannot be resolved, the system identifier is used instead.

You may also see processing instructions at the top of a file, and they are generally considered part of a document's header, rather than its content. They look like this:

<?xml-stylesheet href="XSL\JavaXML.html.xsl" type="text/xsl"?>
<?xml-stylesheet href="XSL\JavaXML.wml.xsl" type="text/xsl"
                 media="wap"?>
<?cocoon-process type="xslt"?>

Each is considered to have a target (the first word, like xml-stylesheet or cocoon-process), and data (the rest). More often than not, the data is in the form of name-value pairs, which can really help readability. This is only a good practice, though, and not required, so don't depend on it.

Other than that, the bulk of your XML document should be content; in other words, elements, attributes, and data that you have put into it.

2.1.1.1. The root element

The root element is the highest-level element in the XML document, and must be the first opening tag and the last closing tag within the document. It provides a reference point that enables an XML parser or XML-aware application to recognize a beginning and end to an XML document. In our example, the root element is book:

<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>
    <!-- Document content -->
</book>

This tag and its matching closing tag surround all other data content within the XML document. XML specifies that there may be only one root element in a document. In other words, the root element must enclose all other elements within the document. Aside from this requirement, a root element does not differ from any other XML element. It's important to understand this, because XML documents can reference and include other XML documents. In these cases, the root element of the referenced document becomes an enclosed element in the referring document, and must be handled normally by an XML parser. Defining root elements as standard XML elements without special properties or behavior allows document inclusion to work seamlessly.

2.1.1.2. Elements

So far I have glossed over defining an actual element. Let's take an in-depth look at elements, which are represented by arbitrary names and must be enclosed in angle brackets. There are several different variations of elements in the sample document, as shown here:

 <!-- Standard element opening tag -->
 <contents>

  <!-- Standard element with attribute -->
  <chapter title="Nuts and Bolts" number="2">

  <!-- Element with textual data -->
  <title ora:series="Java">Java and XML</title>

  <!-- Empty element -->
  <sectionBreak />

 <!-- Standard element closing tag -->
 </contents>

The first rule in creating elements is that their names must start with a letter or underscore, and then may contain any amount of letters, numbers, underscores, hyphens, or periods. They may not contain embedded spaces:

<!-- Embedded spaces are not allowed -->
<my element name>

XML element names are also case-sensitive. Generally, using the same rules that govern Java variable naming will result in sound XML element naming. Using an element named tcbo to represent Telecommunications Business Object is not a good idea because it is cryptic, while an overly verbose tag name like beginningOfNewChapter just clutters up a document. Keep in mind that your XML documents will probably be seen by other developers and content authors, so clear documentation through good naming is essential.

Every opened element must in turn be closed. There are no exceptions to this rule as there are in many other markup languages, like HTML. An ending element tag consists of the forward slash and then the element name: </content>. Between an opening and closing tag, there can be any number of additional elements or textual data. However, you cannot mix the order of nested tags: the first opened element must always be the last closed element. If any of the rules for XML syntax are not followed in an XML document, the document is not well-formed. A well-formed document is one in which all XML syntax rules are followed, and all elements and attributes are correctly positioned. However, a well-formed document is not necessarily valid, which means that it follows the constraints set upon a document by its DTD or schema. There is a significant difference between a well-formed document and a valid one; the rules I discuss in this section ensure that your document is well-formed, while the rules discussed in the constraints section allow your document to be valid.

As an example of a document that is not well-formed, consider this XML fragment:

<tag1>
 <tag2>
</tag1>
 </tag2>

The order of nesting of tags is incorrect, as the opened <tag2> is not followed by a closing </tag2> within the surrounding tag1 element. However, if these syntax errors are corrected, there is still no guarantee that the document will be valid.

While this example of a document that is not well-formed may seem trivial, remember that this would be acceptable HTML, and commonly occurs in large tables within an HTML document. In other words, HTML and many other markup languages do not require well-formed XML documents. XML's strict adherence to ordering and nesting rules allows data to be parsed and handled much more quickly than when using markup languages without these constraints.

The last rule I'll look at is the case of empty elements. I already said that XML tags must always be paired; an opening tag and a closing tag constitute a complete XML element. There are cases where an element is used purely by itself, like a flag stating a chapter is incomplete, or where an element has attributes but no textual data, like an image declaration in HTML. These would have to be represented as:

<chapterIncomplete></chapterIncomplete>
<img src="/images/xml.gif"></img>

This is obviously a bit silly, and adds clutter to what can often be very large XML documents. The XML specification provides a means to signify both an opening and closing element tag within one element:

<chapterIncomplete />
<img src="/images/xml.gif" />

What's with the Space Before Your End-Slash, Brett?

Well, let me tell you. I've had the unfortunate pleasure of working with Java and XML since late 1998, when things were rough, at best. And some web browsers at that time (and some today, to be honest) would only accept XHTML (HTML that is well-formed) in very specific formats. Most notably, tags like <br> that are never closed in HTML must be closed in XHTML, resulting in <br/>. Some of these browsers would completely ignore a tag like this; however, oddly enough, they would happily process <br /> (note the space before the end-slash). I got used to making my XML not only well-formed, but consumable by these browsers. I've never had a good reason to change these habits, so you get to see them in action here.

This nicely solves the problem of unnecessary clutter, and still follows the rule that every XML element must have a matching end tag; it simply consolidates both start and end tag into a single tag.

2.1.1.3. Attributes

In addition to text contained within an element's tags, an element can also have attributes. Attributes are included with their respective values within the element's opening declaration (which can also be its closing declaration!). For example, in the chapter tag, the title of the chapter was part of what was noted in an attribute:

<chapter title="Advanced SAX" number="4">
  <topic name="Properties and Features" />
  <topic name="More Handlers" />
  <topic name="Filters and Writers" />
  <topic name="Even More Handlers" />
  <topic name="Gotcha!" />
  <topic name="What&apos;s Next?" />
</chapter>

In this example, title is the attribute name; the value is the title of the chapter, "Advanced SAX." Attribute names must follow the same rules as XML element names, and attribute values must be within quotation marks. Although both single and double quotes are allowed, double quotes are a widely used standard and result in XML documents that model Java programming practices. Additionally, single and double quotation marks may be used in attribute values; surrounding the value in double quotes allows single quotes to be used as part of the value, and surrounding the value in single quotes allows double quotes to be used as part of the value. This is not good practice, though, as XML parsers and processors often uniformly convert the quotes around an attribute's value to all double (or all single) quotes, possibly introducing unexpected results.

In addition to learning how to use attributes, there is an issue of when to use attributes. Because XML allows such a variety of data formatting, it is rare that an attribute cannot be represented by an element, or that an element could not easily be converted to an attribute. Although there's no specification or widely accepted standard for determining when to use an attribute and when to use an element, there is a good rule of thumb: use elements for multiple-valued data and attributes for single-valued data. If data can have multiple values, or is very lengthy, the data most likely belongs in an element. It can then be treated primarily as textual data, and is easily searchable and usable. Examples are the description of a book's chapters, or URLs detailing related links from a site. However, if the data is primarily represented as a single value, it is best represented by an attribute. A good candidate for an attribute is the section of a chapter; while the section item itself might be an element and have its own title, the grouping of chapters within a section could be easily represented by a section attribute within the chapter element. This attribute would allow easy grouping and indexing of chapters, but would never be directly displayed to the user. Another good example of a piece of data that could be represented in XML as an attribute is if a particular table or chair is on layaway. This instruction could let an XML application used to generate a brochure or flier know not to include items on layaway in current stock; obviously this is a true or false value, and has only a singular value at any time. Again, the application client would never directly see this information, but the data would be used in processing and handling the XML document. If after all of this analysis you are still unsure, you can always play it safe and use an element.

You may have already come up with alternate ways to represent these various examples, using different approaches. For example, rather than using a title attribute, it might make sense to nest title elements within a chapter element. Perhaps an empty tag, <layaway />, might be more useful to mark furniture on layaway. In XML, there is rarely only one way to perform data representation, and often several good ways to accomplish the same task. Most often the application and use of the data dictates what makes the most sense. Rather than tell you how to write XML, which would be difficult, I show you how to use XML so you gain insight into how different data formats can be handled and used. This gives you the knowledge to make your own decisions about formatting XML documents.

2.1.1.4. Entity references and constants

One item I have not discussed is escaping characters, or referring to other constant type data values. For example, a common way to represent a path to an installation directory is <path-to-Cocoon>. Here, the user would replace the text with the appropriate choice of installation directory. In this example, the chapter that discusses web applications must give some details on installing and using Apache Cocoon, and might need to represent this data within an element:

<topic>
 <heading>Installing Cocoon</heading>
 <content>
  Locate the Cocoon.properties file in the <path-to-Cocoon>/bin
  directory.
 </content>
</topic>

The problem is that XML parsers attempt to handle this data as an XML tag, and then generate an error because there is no closing tag. This is a common problem, as any use of angle brackets results in this behavior. Entity references provide a way to overcome this problem. An entity reference is a special data type in XML used to refer to another piece of data. The entity reference consists of a unique name, preceded by an ampersand and followed by a semicolon: &[entity name];. When an XML parser sees an entity reference, the specified substitution value is inserted and no processing of that value occurs. XML defines five entities to address the problem discussed in the example: &lt; for the less-than bracket, &gt; for the greater-than bracket, &amp; for the ampersand sign itself, &quot; for a double quotation mark, and &apos; for a single quotation mark or apostrophe. Using these special references, you can accurately represent the installation directory reference as:

<topic>
 <heading>Installing Cocoon</heading>
 <content>
  Locate the Cocoon.properties file in the
  &lt;path-to-Cocoon&gt;/bin directory.
 </content>
</topic>

Once this document is parsed, the data is interpreted as "<path-to-Cocoon>" and the document is still considered well-formed.

Also be aware that entity references are user-definable. This allows a sort of shortcut markup; in the XML example I have been walking through, I reference an external shared copyright text. Because the copyright is used for multiple O'Reilly books, I don't want to include the text within this XML document; however, if the copyright is changed, the XML document should reflect the changes. You may notice that the syntax used in the XML document looks like the predefined XML entity references:

<ora:copyright>&OReillyCopyright;</ora:copyright>

Although you won't see how the XML parser is told what to reference when it sees &OReillyCopyright; until the section on DTDs, you should see that there are more uses for entity references than just representing difficult or unusual characters within data.

2.1.1.5. Unparsed data

The last XML construct to look at is the CDATA section marker. A CDATA section is used when a significant amount of data should be passed on to the calling application without any XML parsing. It is used when an unusually large number of characters would have to be escaped using entity references, or when spacing must be preserved. In an XML document, a CDATA section looks like this:

<unparsed-data>
  <![CDATA[Diagram:
       <Step 1>Install Cocoon to "/usr/lib/cocoon"
       <Step 2>Locate the correct properties file.
       <Step 3>Download Ant from "http://jakarta.apache.org"
                                  -----> Use CVS for this <----
  ]]>
</unparsed-data>

In this example, the information within the CDATA section does not have to use entity references or other mechanisms to alert the parser that reserved characters are being used; instead, the XML parser passes them unchanged to the wrapping program or application.

At this point, you have seen the major components of XML documents. Although each has only been briefly described, this should give you enough information to recognize XML tags when you see them and know their general purpose. With existing resources like O'Reilly's XML in a Nutshell by your side, you are ready to look at some of the more advanced XML specifications.

2.1.2. Namespaces

Although I will not delve too deeply into XML namespaces here, note the use of a namespace in the root element of Example 2-1. An XML namespace is a means of associating one or more elements in an XML document with a particular URI. This effectively means that the element is identified by both its name and its namespace URI. In this XML example, it may be necessary later to include portions of other O'Reilly books. Because each of these books may also have Chapter, Heading, or Topic elements, the document must be designed and constructed in a way to avoid namespace collision problems with other documents. The XML namespaces specification nicely solves this problem. Because the XML document represents a specific book, and no other XML document should represent the same book, using a namespace associated with a URI like http://www.oreilly.com/javaxml2 can create a unique namespace. The namespace specification requires that a unique URI be associated with a prefix to distinguish the elements in the namespace from elements in other namespaces. A URL is recommended, and supplied here:

<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>

In fact, I've defined two namespaces. The first is considered the default namespace, because no prefix is supplied. Any element without a prefix is associated with this namespace. As a result, all of the elements in the XML document except the copyright element, prefixed with ora, are in this default namespace. The second defines a prefix, which allows the tag <ora:copyright> to be associated with this second namespace.

A final interesting (and somewhat confusing) point: XML Schema, which I will talk about more in a later section, requires the schema of an XML document to be specified in a manner that looks very similar to a set of namespace declarations; see Example 2-2.

Example 2-2. Referencing an XML Schema

<?xml version="1.0"?>
<addressBook xmlns:xsi="http://www.w3.org/1999/XMLSchema/instance"
             xmlns="http://www.oreilly.com/catalog/javaxml"
             xsi:schemaLocation="http://www.oreilly.com/catalog/javaxml
                                 mySchema.xsd"
>
  <person>
    <name>
      <firstName>Brett</firstName>
      <lastName>McLaughlin</lastName>
    </name>
    <email>brettmclaughlin@earthlink.net</email>
  </person>
  <person>
    <name>
      <firstName>Eddie</firstName>
      <lastName>Balucci</lastName>
    </name>
    <email>eddieb@freeworld.net</email>
  </person>
</addressBook>

Several things happen here, and it is important to understand them all. First, the XML Schema instance namespace is defined and associated with a URL. This namespace, abbreviated xsi, is used for specifying information in XML documents about a schema, exactly as is being done here. Thus, the first line makes the elements in the XML Schema instance available to the document for use. The next line defines the namespace for the XML document itself. Because the document does not use an explicit namespace, like the one associated with http://www.oreilly.com/javaxml2 in earlier examples, the default namespace is declared. This means that all elements without an explicit namespace and associated prefix (all of them, in this example) will be associated with this default namespace.

With both the document and XML Schema instance namespaces defined like this, we can then actually do what we want, which is to associate a schema with this document. The schemaLocation attribute, which belongs to the XML Schema instance namespace, is used to accomplish this. I've prefaced this attribute with its namespace (xsi), which was just defined. The argument to this attribute is actually two URIs: the first specifies the namespace associated with a schema, and the second the URI of the schema to refer to. In the example, this results in the first URI being the default namespace just declared, and the second a file on the local filesystem called mySchema.xsd. Like any other XML attribute, the entire pair is enclosed in a single set of quotation marks. And as simple as that, you have referenced a schema in your XML document!

Seriously, it's not simple, and is to date one of the most misunderstood portions of using namespaces and XML Schema. I look more at the mechanics used here as we continue. For now, keep in mind how namespaces allow elements from various groupings to be used, yet remain identified as a part of them specific grouping.

Previous | Home | Next
1.4. What's Next? | Book Index | 2.2. Constraints
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Constraints (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

2.2. Constraints

Next up to bat is dealing with constraining XML. If there's nothing you get out of this chapter other than the rationale behind constraining XML, then I'm a happy author. Because XML is extensible and can represent data in hundreds and even thousands of ways, constraints on a document provide meaning to those various formats. Without document constraints, it is impossible (in most cases) to tell what the data in a document means. In this section, I'm going to cover the two current standard means of constraining XML: DTDs (included in the XML 1.0 specification) and XML Schema (recently a standard put out by the W3C). Choose the one best suited for you.

2.2.1. DTDs

An XML document is not very usable without an accompanying DTD (or schema). Just as XML can effectively describe data, the DTD makes this data usable for many different programs in a variety of ways by defining the structure of the data. In this section, I show you the most common constructs used within a DTD. I use the XML representation of a portion of the table of contents for this book as an example again, and go through the process of constructing a DTD for the XML table of contents document.

The DTD defines how data is formatted. It must define each allowed element in an XML document, the allowed attributes and possibly the acceptable attribute values for each element, the nesting and occurrences of each element, and any external entities. DTDs can specify many other things about an XML document, but these basics are what we will focus on. You will learn the constructs that a DTD offers by applying them to and constraining the XML file from Example 2-1. The complete DTD is shown in Example 2-3, which I'll refer to in this section.

Example 2-3. DTD for Example 2-1

<!ELEMENT book (title, contents, ora:copyright)>
<!ATTLIST book
          xmlns       CDATA  #REQUIRED
          xmlns:ora   CDATA  #REQUIRED
>
<!ELEMENT title (#PCDATA)>
<!ATTLIST title
          ora:series  (C | Java | Linux | Oracle |
                      Perl | Web | Windows)
                      #REQUIRED
>
<!ELEMENT contents (chapter+)>
<!ELEMENT chapter (topic+)>
<!ATTLIST chapter
          title       CDATA  #REQUIRED
          number      CDATA  #REQUIRED
>
<!ELEMENT topic EMPTY>
<!ATTLIST topic
          name        CDATA  #REQUIRED
>

<!-- Copyright Information -->
<!ELEMENT ora:copyright (copyright)>
<!ELEMENT copyright (year, content)>
<!ATTLIST copyright
          xmlns  CDATA  #REQUIRED
>
<!ELEMENT year EMPTY>
<!ATTLIST year
          value  CDATA  #REQUIRED
>
<!ELEMENT content (#PCDATA)>
<!ENTITY OReillyCopyright SYSTEM
   "http://www.newInstance.com/javaxml2/copyright.xml"
>

2.2.1.1. Elements

The bulk of the DTD is composed of ELEMENT definitions (covered in this section) and ATTRIBUTE definitions (covered in the next). An element definition begins with the ELEMENT keyword, following the standard <! opening of a DTD tag, and then the name of the element. Following that name is the content model of the element. The content model is generally within parentheses, and specifies what content can be included within the element. Take the book element as an example:

<!ELEMENT book (title, contents, ora:copyright)>

This says that for any book element, there may be a title element, a contents element, and an ora:copyright element within it. The definitions for these elements are defined later with their content models, and so on. You should be aware that in this standard case, the order specified in the content model is the order that the elements must appear within the document. Additionally, each element must appear, once and only once, when no modifiers are used (which I'll cover momentarily). In this case, each book element must have a title element, a contents element, and then an ora:copyright element, without exception. If these rules are broken, the document is not considered valid (although it still could be well-formed).

Of course, in many cases you need to specify multiple occurrences of an element, or optional occurrences. You can do this using the recurrence modifiers listed in Table 2-1.

Table 2-1. DTD recurrence modifiers

Operator | Description
[Default] | Must appear once and only once (1)
? | May appear once or not at all (0..1)
+ | Must appear at least once, up to an infinite number of times (1..N)
* | May appear any number of times, including not at all (0..N)

As an example, take a look at the contents element definition:

<!ELEMENT contents (chapter+)>

Here, the contents element must have at least one chapter element within it, but there can be an unlimited number of those chapters.

If an element has character data within it, the #PCDATA keyword is used as its content model:

<!ELEMENT title (#PCDATA)>

If an element should always be an empty element, the EMPTY keyword is used:

<!ELEMENT topic EMPTY>

2.2.1.2. Attributes

Once you've handled the element definition, you'll want to define attributes. These are defined through the ATTLIST keyword. The first value is the name of the element, and then you have various attributes defined. Those definitions involve giving the name of the attribute, the type of attribute, and then whether the attribute is required or implied (which means it is not required, essentially). Most attributes with textual values will simply be of the type CDATA, as shown here:

<!ATTLIST chapter
          title       CDATA  #REQUIRED
          number      CDATA  #REQUIRED
>

You can also specify a set of values that an attribute must take on for the document to be considered valid:

<!ATTLIST title
          ora:series  (C | Java | Linux | Oracle |
                      Perl | Web | Windows)
                      #REQUIRED
>

2.2.1.3. Entities

You can specify entity reference resolution in a DTD using the ENTITY keyword. This works a lot like the DOCTYPE reference I talked about earlier, where a public ID and/or system ID may be specified. In the example DTD, I've specified a system ID, a URL, for the OReillyCopyright entity reference to resolve to:

<!ENTITY OReillyCopyright SYSTEM
   "http://www.newInstance.com/javaxml2/copyright.xml"
>

This results in the copyright.xml file at the specified URL being loaded as the value of the O'Reilly copyright entity reference in the sample document. You'll see this in action in the next few chapters.

Now this is hardly an extensive reference on DTDs, but it should give you enough basic knowledge to get going. As I've suggested, have some additional resources specifically on XML available (like XML in a Nutshell) as you go through this book in case you run across something you're unsure about. By assuming that you have that or the online specifications from http://www.w3.org around, I can delve into Java topics more quickly.

2.2.2. XML Schema

XML Schema is a newly finalized candidate recommendation from the W3C. It seeks to improve upon DTDs by adding more typing and quite a few more constructs than DTDs, as well as following an XML format. I'm going to spend relatively little time here talking about schemas, because they are a "behind-the-scenes" detail for Java and XML. In the chapters where you'll be working with schemas (Chapter 14, "Content Syndication", for instance), I'll address specific points you need to be aware of. However, the specification for XML Schema is so enormous that it would take up an entire book of explanation on its own (see the book XML Schema on this CD-ROM). Example 2-4 shows the XML Schema constraining Example 2-1.

Example 2-4. XML Schema constraining Example 2-1

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="http://www.oreilly.com/javaxml2"
           xmlns:ora="http://www.oreilly.com"
           targetNamespace="http://www.oreilly.com/javaxml2"
           elementFormDefault="qualified"
>
  <xs:import namespace="http://www.oreilly.com"
             schemaLocation="contents-ora.xsd" />

  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="title" />
        <xs:element ref="contents" />
        <xs:element ref="ora:copyright" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:restriction base="xs:string">
          <xs:attribute ref="ora:series" use="required" />
        </xs:restriction>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>

  <xs:element name="contents">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="chapter" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="topic" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:attribute name="name"
                                type="xs:string"
                                use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="title" type="xs:string" use="required"/>
            <xs:attribute name="number" type="xs:byte" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

In addition, you'll need the schema in Example 2-5, for reasons you will soon understand.

Example 2-5. Additional XML Schema for Example 2-1

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns="http://www.oreilly.com"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.oreilly.com"
           attributeFormDefault="qualified"
           elementFormDefault="qualified"
>
  <xs:attribute name="series" type="xs:string"/>
  <xs:element name="copyright" type="xs:string" />
</xs:schema>

Before diving into the specifics of these schemas, notice that various namespace declarations are made. First, the XML Schema namespace itself is attached to the xs prefix, allowing separation of XML Schema constructs from the elements and attributes being constrained. Next, the default namespace is attached to the namespace of the elements being defined; in Example 2-4 this is the Java and XML namespace, and in Example 2-5 it's the O'Reilly namespace. I've also assigned the targetNamespace attribute this same value. This attribute specifies to the schema the namespace of the elements and attributes being constrained. This is easy to forget, and can wreak a lot of havoc, so be careful to include it. At this point, namespaces are defined for the elements being constrained (the default namespace) and the constructs being used (the XML Schema namespace).

Last, I've specified the value of attributeFormDefault and elementFormDefault as "qualified." This indicates that I'll use fully qualified names for the elements and attributes, rather than just local names. I won't go into detail about this, but I highly recommend you use qualified names at all times. Trying to deal with multiple namespaces and unqualified names at the same time is a mess I wouldn't want to wander into.

2.2.2.1. Elements and attributes

Elements are defined with the element construct. You'll generally need to define your own data types by nesting a complexType tag within the element element, which defines the name of the element (through the name attribute). Take a look at this fragment of Example 2-4:

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="title" />
      <xs:element ref="contents" />
      <xs:element ref="ora:copyright" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

Here, I've specified that the book element has complex content. Within it there should be three elements: title, contents, and ora:copyright. By using the sequence construct, I've ensured that they appear in the specified order; and with no modifiers, an element must appear once and only once. For each of these other elements, I've used the ref keyword to reference another element definition. This points to the definitions for each of these elements in another part of the schema, and keeps things organized and easy to follow.

Later in the file, the title element is defined:

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:restriction base="xs:string">
        <xs:attribute ref="ora:series" use="required" />
      </xs:restriction>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

This element is really just a simple XML Schema string type; however, I've added an attribute to it, so I must define a complexType. Since I'm extending an existing type, I use the simpleContent and restriction keywords (as nested elements) to define this type. simpleContent informs the schema that this is a basic type, and restriction, with the base of "xs:string", lets the schema know I want to allow just what the XML Schema string type allows, plus the additional attribute defined here (with the attribute keyword). For the attribute itself, I reference the type defined elsewhere, and specify that it must appear for this element (through use="required"). I realize that this paragraph is a mouthful, and not completely obvious; however, take your time and you'll get it all.

One other thing you'll notice is the use of minOccurs and maxOccurs attributes on the element element; these attributes allow an element to appear a specified number of times other than the default, which is once and only once. For example, specifying minOccurs="0" and maxOccurs="1" allows an element to appear once, or not at all. To allow an element to appear an unlimited number of times, you can use the value of "unbounded" for the maxOccurs attribute, as in Example 2-4.

2.2.2.2. Multiple namespaces

You'll notice that I defined two schemas, though, which may have you puzzled. For each namespace in a document, one schema must be defined. Additionally, you can't use the same external schema for both namespaces, and simply point both at that external schema. As a result, using the ora prefix and namespace requires an additional schema, which I called contents-ora.xsd. You'll also need to use the schemaLocation attribute I talked about earlier to reference this schema; however, don't add another attribute. Instead, you can append another namespace and schema-location pair to the end of the value of the attribute, as shown here:

<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.oreilly.com/javaxml2 XSD/contents.xsd
                          http://www.oreilly.com XSD/contents-ora.xsd"
>

This essentially says for the namespace http://www.oreilly.com/javaxml2, look up definitions in the schema called contents.xsd in the XSD/ directory. For the http://www.oreilly.com namespace, use the contents-ora.xsd schema in the same directory. You'll then need to define the two schemas I showed you in Example 2-5 and Example 2-5. Finally, import the O'Reilly schema into the Java and XML one, since elements in the Java and XML schema refer to attributes in the O'Reilly one:

<xs:import namespace="http://www.oreilly.com"
           schemaLocation="contents-ora.xsd" />

This import is fairly self-explanatory, so I won't dwell on it. You should realize that dealing with multiple namespaces is about the most complex thing you can do in schemas, and can easily trip you up. (It tripped me up, until Eric van der Vlist saved the day.) I also recommend a good XML Schema-capable editor. While I'm generally slow to recommend commercial products, in this case XMLSpy 4.0 (http://www.xmlspy.com) turned out to be wonderfully helpful.

I've barely scratched the surface of either DTDs or XML Schema, and there are even other constraint models not covered at all! For example, Relax (and Relax NG, which includes what used to be TREX) is gaining a lot of steam, as it's considered a lot easier and more lightweight than XML Schema. You can check out the activity online at http://www.oasis-open.org/committees/relax-ng/. No matter what technology you choose, though, you should be able to find something that helps you constrain your XML documents. With these constraints in place, validation and interoperability become a snap. Consider yourself educated on XML constraints, and get ready to move on to the next topic in this whirlwind tour: XML transformations.

Previous | Home | Next
2. Nuts and Bolts | Book Index | 2.3. Transformations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Transformations (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

2.3. Transformations

As useful as XML transformations can be, they are not simple to implement. In fact, rather than trying to specify the transformation of XML in the original XML 1.0 specification, three separate recommendations have come out to define how transformations should occur. Although one of these (XPath) is also used in several other XML specifications, by far the most common use of the components I outline here is to transform XML from one format into another.

Because these three specifications are tied together tightly and almost always used in concert, there is rarely a clear distinction between them. This can often make for a discussion that is easy to understand, but not necessarily technically correct. In other words, the term XSLT, which refers specifically to extensible stylesheet transformations, is often applied to both extensible stylesheets (XSL) and XPath. In the same fashion, XSL is often used as a grouping term for all three technologies. In this section, I distinguish among the three recommendations, and remain true to the letter of the specifications outlining these technologies. However, in the interest of clarity, I use XSL and XSLT interchangeably to refer to the complete transformation process throughout the rest of the book. Although this may not follow the letter of these specifications, it certainly follows their spirit, as well as avoiding lengthy definitions of simple concepts when you already understand what I mean.

2.3.1. XSL

XSL is the Extensible Stylesheet Language. It is defined as a language for expressing stylesheets. This broad definition is broken down into two parts:

  • XSL is a language for transforming XML documents.

  • XSL is an XML vocabulary for specifying the formatting of XML documents.

The definitions are similar, but one deals with moving from one XML document form to another, while the other focuses on the actual presentation of content within each document. Perhaps a clearer definition would be to say that XSL handles the specification of how to transform a document from format A to format B. The components of the language handle the processing and identification of the constructs used to do this.

2.3.1.1. XSL and trees

The most important concept to understand in XSL is that all data within XSL processing stages is in tree structures (see Figure 2-1). In fact, the rules you define using XSL are themselves held in a tree structure. This allows simple processing of the hierarchical structure of XML documents. Templates are used to match the root element of the XML document being processed. Then "leaf" rules are applied to "leaf" elements, filtering down to the most nested elements. At any point in this progression, elements can be processed, styled, ignored, copied, or have a variety of other things done to them.

Figure 2-1. Tree operations within XSL

A nice advantage of this tree structure is that it allows the grouping of XML documents to be maintained. If element A contains elements B and C, and element A is moved or copied, the elements contained within it receive the same treatment.

This makes the handling of large data sections that need to receive the same treatment fast and easy to notate concisely in the XSL stylesheet. You will see more about how this tree is constructed when I talk specifically about XSLT in the next section.

2.3.1.2. Formatting objects

The XSL specification is almost entirely concerned with defining formatting objects. A formatting object is based on a large model, not surprisingly called the formatting model. This model is all about a set of objects that are fed as input into a formatter. The formatter applies the objects to the document, either in whole or in part, and what results is a new document that consists of all or part of the data from the original XML document in a format specific to the objects the formatter used. Because this is such a vague, shadowy concept, the XSL specification attempts to define a concrete model these objects should conform to. In other words, a large set of properties and vocabulary make up the set of features that formatting objects can use. These include the types of areas that may be visualized by the objects, the properties of lines, fonts, graphics, and other visual objects, inline and block formatting objects, and a wealth of other syntactical constructs.

Formatting objects are used heavily when converting textual XML data into binary formats such as PDF files, images, or document formats such as Microsoft Word. For transforming XML data to another textual format, these objects are seldom used explicitly. Although an underlying part of the stylesheet logic, formatting objects are rarely invoked directly, since the resulting textual data often conforms to another predefined markup language such as HTML. Because most enterprise applications today are based at least in part on web architecture and use a browser as a client, I spend the most time looking at transformations to HTML and XHTML. While formatting objects are covered only lightly, the topic is broad enough to merit its own coverage in a separate book. For further information, consult the XSL specification at http://www.w3.org/TR/WD-xsl.

2.3.2. XSLT

The second component of XML transformations is XSL Transformations. XSLT is the language that specifies the conversion of a document from one format to another (where XSL defined the means of that specification). The syntax used within XSLT is generally concerned with textual transformations that do not result in binary data output. For example, XSLT is instrumental is generating HTML or WML (Wireless Markup Language) from an XML document. In fact, the XSLT specification outlines the syntax of an XSL stylesheet more explicitly than the XSL specification itself!

Just as in the case of XSL, XSLT is always well-formed, valid XML. A DTD is defined for XSL and XSLT that delineates the allowed constructs. For this reason, you should only have to learn new syntax to use XSLT as opposed to the entirely new structures that had to be digested to use DTDs themselves. Just as in XSL, XSLT is based on a hierarchical tree structure of data, where nested elements are leaves, or children, of their parents. XSLT provides a mechanism for matching patterns within the original XML document (using an XPath expression, which I'll discuss next), and applying formatting to that data. This results in simply outputting the data without the unwanted XML element names, or inserting the data into a complex HTML table and displaying it to the user with highlighting and coloring. XSLT also provides syntax for many common operators, such as conditionals, copying of document tree fragments, advanced pattern matching, and the ability to access elements within the input XML data in an absolute and relative path structure. All these constructs are designed to ease the process of transforming an XML document into a new format. For a thorough treatment of the XSLT language, see Java and XSLT by Eric Burke (O'Reilly), which has an excellent discussion of how to put XSLT to work with Java.

2.3.3. XPath

The final piece of the XML transformations puzzle, XPath provides a mechanism for referring to the wide variety of element and attribute names and values in an XML document. As I mentioned earlier, many XML specifications are now using XPath, but this discussion is concerned only with its use in XSLT. With the complex structure that an XML document can have, locating one specific element or set of elements can be difficult. It is made more difficult because access to a DTD or other set of constraints that outlines the document's structure cannot be assumed; documents that are not validated must be able to be transformed just as valid documents can. To accomplish this addressing of elements, XPath defines syntax in line with the tree structure of XML, and the XSLT processes and constructs that use it.

Referencing any element or attribute within an XML document is most easily accomplished by specifying the path to the element relative to the current element being processed. In other words, if element B is the current element and element C and element D are nested within it, a relative path most easily locates them. This is similar to the relative paths used in operating system directory structures. At the same time, XPath also defines addressing for elements relative to the root of a document. This covers the common case of needing to reference an element not within the current element's scope; in other words, an element that is not nested within the element being processed. Finally, XPath defines syntax for actual pattern matching: find an element whose parent is element E and which has a sibling element F. This fills in the gaps left between the absolute and relative paths. In all these expressions, attributes can be used as well, with similar matching abilities. Several examples are shown in Example 2-6.

Example 2-6. XPath expressions

<!-- Match the element named Book relative to the current element -->
<xsl:value-of select="Book" />

<!-- Match the element named Contents nested within the Book element -->
<xsl:value-of select="Book/Contents" />

<!-- Match the Contents element using an absolute path -->
<xsl:value-of select="/Book/Contents" />

<!-- Match the name attribute of the current element -->
<xsl:value-of select="@name" />

<!-- Match the title attribute of the Chapter element -->
<xsl:value-of select="Chapter/@title" />

Because the input document is often not fixed, an XPath expression can result in the evaluation of no input data, one input element or attribute, or multiple input elements and attributes. This ability makes XPath very useful and handy; it also causes the introduction of some additional terms. The result of evaluating an XPath expression is generally referred to as a node set. This name shouldn't be surprising, as it is in line with the idea of a hierarchical or tree structure, often dealt with in terms of its leaves or nodes. The resultant node set can then be transformed, copied, or ignored, or have any other legal operation performed on it. In addition to expressions to select node sets, XPath also defines several node set functions, such as not( ) and count( ). These functions take in a node set as input (typically in the form of an XPath expression) and then further pare the results. All of these expressions and functions are collectively part of the XPath specification and XPath implementations; however, XPath is also often used to signify any expression that conforms to the specification itself. As with XSL and XSLT, this makes it easier to talk about XSL and XPath, though it is not always technically correct.

With all that in mind, you're at least somewhat prepared to take a look at a simple XSL stylesheet, shown in Example 2-7. Although you may not understand all of this now, let's briefly look at some key aspects of the stylesheet.

Example 2-7. XSL stylesheet for Example 2-1

<?xml version="1.0"?>

<xsl:stylesheet xmlns:javaxml2="http://www.oreilly.com/javaxml2"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ora="http://www.oreilly.com"
                version="1.0"
>

  <xsl:template match="javaxml2:book">
    <html>
      <head>
        <title><xsl:value-of select="javaxml2:title" /></title>
      </head>
      <body>
        <xsl:apply-templates select="*[not(self::javaxml2:title)]" />
      </body>
    </html>
  </xsl:template>

  <xsl:template match="javaxml2:contents">
    <center>
     <h2>Table of Contents</h2>
    </center>
    <hr />
    <ul>
     <xsl:for-each select="javaxml2:chapter">
      <b>
       Chapter <xsl:value-of select="@number" />.
       <xsl:text> </xsl:text>
       <xsl:value-of select="@title" />
      </b>
      <xsl:for-each select="javaxml2:topic">
       <ul>
        <li><xsl:value-of select="@name" /></li>
       </ul>
      </xsl:for-each>
     </xsl:for-each>
    </ul>
  </xsl:template>

  <xsl:template match="ora:copyright">
    <p align="center"><font size="-1">
     <xsl:copy-of select="*" />
    </font></p>
  </xsl:template>

</xsl:stylesheet>

2.3.3.1. Template matching

The basis of all XSL work is template matching. For any element you want some sort of output to occur on, you generally provide a template that matches the element. You signify a template with the template keyword, and provide the name of the element to match in its match attribute:

<xsl:template match="javaxml2:book">
  <html>
    <head>
      <title><xsl:value-of select="javaxml2:title" /></title>
    </head>
    <body>
      <xsl:apply-templates select="*[not(self::javaxml2:title)]" />
    </body>
  </html>
</xsl:template>

Here, the book element (in the javaxml2-associated namespace) is being matched. When an XSL processor encounters the book element, the instructions within this template are carried out. In the example, several HTML formatting tags are output (the html, head, title, and body tags). Be sure to distinguish your XSL elements from other elements (such as HTML elements) with proper use of namespaces.

Instead of applying a template, you can use the value-of construct to obtain the value of an element, and provide the element name to match through the select attribute. In the example, the character data within the title element is extracted and used as the title of the HTML form to output.

On the other hand, when you want to cause the templates associated with an element's children to be applied, use apply-templates. Be sure to do this, or nested elements can be ignored! You can specify the elements to apply templates to using the select attribute; by specifying a value of "*" to that attribute, all templates left will be applied to all nested elements. In the example, though, I want to exclude the title element (since I already used it in the document heading). To accomplish this, I've used the not keyword, and specified the title element on the self axis, which basically means "everything (*), except (not) the title element in this document (self::javaxml2:title). That's a quick overview, but I'm just trying to give you enough information to move on to the Java code.

2.3.3.2. Looping

You'll also often find a need for looping in XSL. Look at this fragment from Example 2-7:

<xsl:template match="javaxml2:contents">
  <center>
   <h2>Table of Contents</h2>
  </center>
  <hr />
  <ul>
   <xsl:for-each select="javaxml2:chapter">
    <b>
     Chapter <xsl:value-of select="@number" />.
     <xsl:text> </xsl:text>
     <xsl:value-of select="@title" />
    </b>
    <xsl:for-each select="javaxml2:topic">
     <ul>
      <li><xsl:value-of select="@name" /></li>
     </ul>
    </xsl:for-each>
   </xsl:for-each>
  </ul>
</xsl:template>

Here, I'm looping through each element named chapter using the for-each construct. In Java, this would be:

for (Iterator i = chapters.iterator(); i.hasNext( ); ) {
    // take action on each chapter
}

Within the loop, the "current" element becomes the next chapter element encountered. For each, I output the chapter number; this is accomplished by getting the value (through value-of) of the number attribute. To indicate that I want an attribute (not the default, an element), I prefix the attribute name with the "@" sign. I do the same thing to get the title attribute's value, and then in a subloop I move through the topics for each chapter.

Notice the rather odd code fragment <xsl:text> <xsl:text>. The text construct provides a way to directly output characters to the result tree. This construct generates a space between the word "Chapter" and the chapter number (there is a single space between the opening and closing text tags).

2.3.3.3. Copying

You will also find times when all the template matching in the world isn't as useful as simply passing on the content, unchanged, to the output tree. This is the case with the copyright element:

<xsl:template match="ora:copyright">
  <p align="center"><font size="-1">
   <xsl:copy-of select="*" />
  </font></p>
</xsl:template>

In addition to a little bit of HTML formatting, this template instructs all the content of the copyright element to be copied to the output tree, using the copy-of construct. Simple enough.

You'll learn how to use a publishing framework like Cocoon to render the result of this transformation to HTML, a PDF, or more in Chapter 10, "Web Publishing Frameworks". Rather than keeping you waiting, though, Figure 2-2 shows the transformed output from Example 2-1 and the stylesheet in Example 2-6.

Figure 2-2. Result of XSL transformation

I realize that I've virtually flown through this material, but again, I'm just trying to get you past the basics and to the good stuff, the Java and XML. Have a reference handy, and don't sweat it too much.

Previous | Home | Next
2.2. Constraints | Book Index | 2.4. And More...
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


And More... (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

2.4. And More...

Lest I mislead you into thinking that's all that there is to XML, I want to make sure that you realize there are a multitude of other XML-related technologies. I can't possibly get into them all here. You should take a quick glance at things like CSS (Cascading Style Sheets) and XHTML if you are working on web design. Document authors will want to find out more about XLink and XPointer (both of which I cover in Chapter 16, "Looking Forward"). XQL (XML Query Language) will be of interest to database programmers. In other words, there's something XML for pretty much every technology space right now. Take a look at the W3C XML activity page at http://www.w3.org/XML and see what looks interesting.

Previous | Home | Next
2.3. Transformations | Book Index | 2.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

2.5. What's Next?

With some baseline knowledge of XML, you're ready to dive into the Java side of things. In the next chapter, I'll introduce you to SAX, the Simple API for XML. This is ground zero of the Java and XML APIs, and will get you started on seeing how you can use XML in your own Java applications. You'll learn how to read documents, set various options for DTD and schema validation, use namespace processing, and more, and understand when SAX is the right tool for a particular job. Fire up your editor and turn the page.

Previous | Home | Next
2.4. And More... | Book Index | 3. SAX
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 3. SAX

Contents:

Getting Prepared
SAX Readers
Content Handlers
Error Handlers
Gotcha!
What's Next?

When dealing with XML programmatically, one of the first things you have to do is take an XML document and parse it. As the document is parsed, the data in the document becomes available to the application using the parser, and suddenly you are within an XML-aware application! If this sounds a little too simple to be true, it almost is. This chapter describes how an XML document is parsed, focusing on the events that occur within this process. These events are important, as they are all points where application-specific code can be inserted and data manipulation can occur.

As a vehicle for this chapter, I'm going to introduce the Simple API for XML (SAX). SAX is what makes insertion of this application-specific code into events possible. The interfaces provided in the SAX package will become an important part of any programmer's toolkit for handling XML. Even though the SAX classes are small and few in number, they provide a critical framework for Java and XML to operate within. Solid understanding of how they help in accessing XML data is critical to effectively leveraging XML in your Java programs. In later chapters, we'll add to this toolkit other Java and XML APIs like DOM, JDOM, JAXP, and data binding. But, enough fluff; it's time to talk SAX.

3.1. Getting Prepared

There are a few items that you must have before beginning to code. They are:

  • An XML parser

  • The SAX classes

  • An XML document

First, you must obtain an XML parser. Writing a parser for XML is a serious task, and there are several efforts going on to provide excellent XML parsers, especially in the open source arena. I am not going to detail the process of actually writing an XML parser here; rather, I will discuss the applications that wrap this parsing behavior, focusing on using existing tools to manipulate XML data. This results in better and faster programs, as neither you nor I spend time trying to reinvent what is already available. After selecting a parser, you must ensure that a copy of the SAX classes is on hand. These are easy to locate, and are key to Java code's ability to process XML. Finally, you need an XML document to parse. Then, on to the code!

3.1.1. Obtaining a Parser

The first step to coding Java that uses XML is locating and obtaining the parser you want to use. I briefly talked about this process in Chapter 1, "Introduction", and listed various XML parsers that could be used. To ensure that your parser works with all the examples in the book, you should verify your parser's compliance with the XML specification. Because of the variety of parsers available and the rapid pace of change within the XML community, all of the details about which parsers have what compliance levels are beyond the scope of this book. Consult the parser's vendor and visit the web sites previously given for this information.

In the spirit of the open source community, all of the examples in this book use the Apache Xerces parser. Freely available in binary and source form at http://xml.apache.org, this C- and Java-based parser is already one of the most widely contributed-to parsers available (not that hardcore Java developers like us care about C, though, right?). In addition, using an open source parser such as Xerces allows you to send questions or bug reports to the parser's authors, resulting in a better product, as well as helping you use the software quickly and correctly. To subscribe to the general list and request help on the Xerces parser, send a blank email to xerces-j-dev-subscribe@xml.apache.org. The members of this list can help if you have questions or problems with a parser not specifically covered in this book. Of course, the examples in this book all run normally on any parser that uses the SAX implementation covered here.

Once you have selected and downloaded an XML parser, make sure that your Java environment, whether it be an IDE (Integrated Development Environment) or a command line, has the XML parser classes in its classpath. This will be a basic requirement for all further examples.

NOTE: If you don't know how to deal with CLASSPATH issues, you may be in a bit over your head. However, assuming you are comfortable with your system CLASSPATH, set it to include your parser's jar file, as shown here:

c: set CLASSPATH=.;c:\javaxml2\lib\xerces.jar;%CLASSPATH%

c: echo %CLASSPATH%
.;c:\javaxml2\lib\xerces.jar;c:\java\jdk1.3\lib\tools.jar

Of course, your path will be different from mine, but you get the idea.

3.1.2. Getting the SAX Classes and Interfaces

Once you have your parser, you need to locate the SAX classes. These classes are almost always included with a parser when downloaded, and Xerces is no exception. If this is the case with your parser, you should be sure not to download the SAX classes explicitly, as your parser is probably packaged with the latest version of SAX that is supported by the parser. At this time, SAX 2.0 has long been final, so expect the examples detailed here (which are all using SAX 2) to work as shown, with no modifications.

If you are not sure whether you have the SAX classes, look at the jar file or class structure used by your parser. The SAX classes are packaged in the org.xml.sax structure. Ensure, at a minimum, that you see the class org.xml.sax.XMLReader. This will indicate that you are (almost certainly) using a parser with SAX 2 support, as the XMLReader class is core to SAX 2.

Finally, many parsers include documentation with a download, and this documentation may have the SAX API documentation packaged with it (Xerces being an example of this case).

3.1.3. Have an XML Document on Hand

You should also make sure that you have an XML document to parse. The output shown in the examples is based on parsing the XML document discussed in Chapter 2, "Nuts and Bolts". Save this file as contents.xml somewhere on your local hard drive. I highly recommend that you follow what I'm demonstrating by using this document; it contains various XML constructs for demonstration purposes. You can simply type the file in from the book, or you may download the XML file from the book's web site, http://www.newInstance.com.

Previous | Home | Next
2.5. What's Next? | Book Index | 3.2. SAX Readers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX Readers (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

3.2. SAX Readers

Without spending any further time on the preliminaries, it's time to code. As a sample to familiarize you with SAX, this chapter details the SAXTreeViewer class. This class uses SAX to parse an XML document supplied on the command line, and displays the document visually as a Swing JTree. If you don't know anything about Swing, don't worry; I don't focus on that, but just use it for visual purposes. The focus will remain on SAX, and how events within parsing can be used to perform customized action. All that really happens is that a JTree is used, which provides a nice simple tree model, to display the XML input document. The key to this tree is the DefaultMutableTreeNode class, which you'll get quite used to in using this example, as well as the DefaultTreeModel that takes care of the layout.

The first thing you need to do in any SAX-based application is get an instance of a class that conforms to the SAX org.xml.sax.XMLReader interface. This interface defines parsing behavior and allows us to set features and properties (which I'll cover later in this chapter). For those of you familiar with SAX 1.0, this interface replaces the org.xml.sax.Parser interface.

WARNING: This is a good time to point out that SAX 1.0 is not covered in this book. While there is a very small section at the end of this chapter explaining how to convert SAX 1.0 code to SAX 2.0, you really are not in a good situation if you are using SAX 1.0. While the first edition of this book came out on the heels of SAX 2.0, it's now been well over a year since the API was released in a 2.0 final form. I strongly urge you to move on to Version 2 if you haven't already.

3.2.1. Instantiating a Reader

SAX provides an interface all SAX-compliant XML parsers should implement. This allows SAX to know exactly what methods are available for callback and use within an application. For example, the Xerces main SAX parser class, org.apache.xerces.parsers.SAXParser, implements the org.xml.sax.XMLReader interface. If you have access to the source of your parser, you should see the same interface implemented in your parser's main SAX parser class. Each XML parser must have one class (and sometimes has more than one) that implements this interface, and that is the class you need to instantiate to allow for parsing XML:

// Instantiate a Reader
XMLReader reader =
  new org.xml.sax.SAXParser( );

// Do something with the parser
reader.parse(uri);

With that in mind, it's worth looking at a more realistic example. Example 3-1 is the skeleton for the SAXTreeViewer class I was just referring to, which allows viewing of an XML document as a graphical tree. This also gives you a chance to look at each of the SAX events and associated callback methods that can be used to perform action within the parsing of an XML document.

Example 3-1. The SAXTreeViewer skeleton

package javaxml2;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

// This is an XML book - no need for explicit Swing imports
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;

public class SAXTreeViewer extends JFrame {

    /** Default parser to use */
    private String vendorParserClass =
        "org.apache.xerces.parsers.SAXParser";

    /** The base tree to render */
    private JTree jTree;

    /** Tree model to use */
    DefaultTreeModel defaultTreeModel;

    public SAXTreeViewer( ) {
        // Handle Swing setup
        super("SAX Tree Viewer");
        setSize(600, 450);
    }

    public void init(String xmlURI) throws IOException, SAXException {
        DefaultMutableTreeNode base =
            new DefaultMutableTreeNode("XML Document: " +
                xmlURI);

        // Build the tree model
        defaultTreeModel = new DefaultTreeModel(base);
        jTree = new JTree(defaultTreeModel);

        // Construct the tree hierarchy
        buildTree(defaultTreeModel, base, xmlURI);

        // Display the results
        getContentPane( ).add(new JScrollPane(jTree),
            BorderLayout.CENTER);
    }

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);

        // Register content handler

        // Register error handler

        // Parse
    }

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.out.println(
                    "Usage: java javaxml2.SAXTreeViewer " +
                    "[XML Document URI]");
                System.exit(0);
            }
            SAXTreeViewer viewer = new SAXTreeViewer( );
            viewer.init(args[0]);
            viewer.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

This should all be fairly straightforward.[2] Other than setting up the visual properties for Swing, this code takes in the URI of an XML document (our contents.xml from the last chapter). In the init( ) method, a JTree is created for displaying the contents of the URI. These objects (the tree and URI) are then passed to the method that is worth focusing on, the buildTree( ) method. This is where parsing will take place, and the visual representation of the XML document supplied will be created. Additionally, the skeleton takes care of creating a base node for the graphical tree, with the path to the supplied XML document as that node's text.

[2]Don't be concerned if you are not familiar with the Swing concepts involved here; to be honest, I had to look most of them up myself! For a good reference on Swing, pick up a copy of Java Swing by Robert Eckstein, Marc Loy, and Dave Wood (O'Reilly).

U-R-What?

I've just breezed by what URIs are both here and in the last chapter. In short, a URI is a uniform resource indicator. As the name suggests, it provides a standard means of identifying (and thereby locating, in most cases) a specific resource; this resource is almost always some sort of XML document, for the purposes of this book. URIs are related to URLs, uniform resource locators. In fact, a URL is always a URI (although the reverse is not true). So in the examples in this and other chapters, you could specify a filename or a URL, like http://www.newInstance.com/javaxml2/copyright.xml, and either would be accepted.

You should be able to load and compile this program if you made the preparations talked about earlier to ensure that an XML parser and the SAX classes are in your class path. If you have a parser other than Apache Xerces, you can replace the value of the vendorParserClass variable to match your parser's XMLReader implementation class, and leave the rest of the code as is. This simple program doesn't do much yet; in fact, if you run it and supply a legitimate filename as an argument, it should happily grind away and show you an empty tree, with the document's filename as the base node. That's because you have only instantiated a reader, not requested that the XML document be parsed.

WARNING: If you have trouble compiling this source file, you most likely have problems with your IDE or system's class path. First, make sure you obtained the Apache Xerces parser (or your vendor's parser). For Xerces, this involves downloading azipped or gzipped file. This archive can then be extracted, and will contain a xerces.jar file; it is this jar file that contains the compiled class files for the program. Add this archive to your class path. You should then be able to compile the source file listing.

3.2.2. Parsing the Document

Once a reader is loaded and ready for use, you can instruct it to parse an XML document. This is conveniently handled by the parse( ) method of org.xml.sax.XMLReader class, and this method can accept either an org.xml.sax.InputSource or a simple string URI. It's a much better idea to use the SAX InputSource class, as that can provide more information than a simple location. I'll talk more about that later, but suffice it to say that an InputSource can be constructed from an I/O InputStream, Reader, or a string URI.

You can now add construction of an InputSource from the provided URI, as well as the invocation of the parse( ) method to the example. Because the document must be loaded, either locally or remotely, a java.io.IOException may result, and must be caught. In addition, the org.xml.sax.SAXException will be thrown if problems occur while parsing the document. Notice that the buildTree method can throw both of these exceptions:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, File file)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);

        // Register content handler

        // Register error handler

        // Parse
        InputSource inputSource =
            new InputSource(xmlURI);
        reader.parse(inputSource);
    }

Compile these changes and you are ready to execute the parsing example. You should specify the path to your file as the first argument to the program:

c:\javaxml2\build>java javaxml2.SAXTreeViewer ..\Ch03\xml\contents.xml
WARNING: Supplying an XML URI can be a rather strange task. In versions of Xerces before 1.1, a normal filename could be supplied (for example, on Windows, ..\xml\contents.xml). However, this behavior changed in Xerces 1.1 and 1.2, and the URI had to be in this form: file:///c:/javaxml2/xml/contents.xml. However, in the latest versions of Xerces (from 1.3 up, as well as 2.0), this behavior has moved back to accepting normal filenames. Be aware of these issues if you are using Xerces 1.1 through 1.2.

The rather boring output shown in Figure 3-1 may make you doubt that anything has happened. However, if you lean nice and close, you may hear your hard drive spin briefly (or you can just have faith in the bytecode). In fact, the XML document is parsed. However, no callbacks have been implemented to tell SAX to take action during the parsing; without these callbacks, a document is parsed quietly and without application intervention. Of course, we want to intervene in that process, so it's now time to look at creating some parser callback methods. A callback method is a method that is not directly invoked by you or your application code. Instead, as the parser begins to work, it calls these methods at certain events, without any intervention. In other words, instead of your code calling into the parser, the parser calls back to yours. That allows you to programmatically insert behavior into the parsing process. This intervention is the most important part of using SAX. Parser callbacks let you insert action into the program flow, and turn the rather boring, quiet parsing of an XML document into an application that can react to the data, elements, attributes, and structure of the document being parsed, as well as interact with other programs and clients along the way.

Figure 3-1. An uninteresting JTree

3.2.3. Using an InputSource

I mentioned earlier that I would touch on using a SAX InputSource again, albeit briefly. The advantage to using an InputSource instead of directly supplying a URI is simple: it can provide more information to the parser. An InputSource encapsulates information about a single object, the document to parse. In situations where a system identifier, public identifier, or stream may all be tied to one URI, using an InputSource for encapsulation can become very handy. The class has accessor and mutator methods for its system ID and public ID, a character encoding, a byte stream (java.io.InputStream), and a character stream (java.io.Reader). Passed as an argument to the parse( ) method, SAX also guarantees that the parser will never modify the InputSource. The original input to a parser is still available unchanged after its use by a parser or XML-aware application. In our example, it's important because the XML document uses a relative path to the DTD in it:

<!DOCTYPE Book SYSTEM "DTD/JavaXML.dtd">

By using an InputSource and wrapping the supplied XML URI, you have set the system ID of the document. This effectively sets up the path to the document for the parser and allows it to resolve all relative paths within that document, like the JavaXML.dtd file. If instead of setting this ID, you parsed an I/O stream, the DTD wouldn't be located (as it has no frame of reference); you could simulate this by changing the code in the buildTree( ) method as shown here:

        // Parse
        InputSource inputSource =
            new InputSource(new java.io.FileInputStream(
                new java.io.File(xmlURI)));
        reader.parse(inputSource);

As a result, you would get the following exception when running the viewer:

C:\javaxml2\build>java javaxml2.SAXTreeViewer ..\ch03\xml\contents.xml
org.xml.sax.SAXParseException: File
  "file:///C:/javaxml2/build/DTD/JavaXML.dtd" not found.

While this seems a little silly (wrapping a URI in a file and I/O stream), it's actually quite common to see people using I/O streams as input to parsers. Just be sure that you don't reference any other files in the XML and that you set a system ID for the XML stream (using the setSystemID( ) method on InputSource). So the above code sample could be "fixed" by changing it to the following:

        // Parse
        InputSource inputSource =
            new InputSource(new java.io.FileInputStream(
                new java.io.File(xmlURI)));
        inputSource.setSystemID(xmlURI);
        reader.parse(inputSource);

Always set a system ID. Sorry for the excessive detail; now you can bore coworkers with your knowledge about SAX InputSources.

Previous | Home | Next
3. SAX | Book Index | 3.3. Content Handlers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Content Handlers (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

3.3. Content Handlers

In order to let an application do something useful with XML data as it is being parsed, you must register handlers with the SAX parser. A handler is nothing more than a set of callbacks that SAX defines to let programmers insert application code at important events within a document's parsing. These events take place as the document is parsed, not after the parsing has occurred. This is one of the reasons that SAX is such a powerful interface: it allows a document to be handled sequentially, without having to first read the entire document into memory. Later, we will look at the Document Object Model (DOM), which has this limitation.[3]

[3]Of course, this limitation is also an advantage; having the entire document in memory allows for random access. In other words, it's a double-edged sword, which I'll look at more in Chapter 5, "DOM".

There are four core handler interfaces defined by SAX 2.0: org.xml.sax.ContentHandler , org.xml.sax.ErrorHandler, org.xml.sax.DTDHandler, and org.xml.sax.EntityResolver. In this chapter, I will discuss ContentHandler and ErrorHandler. I'll leave discussion of DTDHandler and EntityResolver for the next chapter; it is enough for now to understand that EntityResolver works just like the other handlers, and is built specifically for resolving external entities specified within an XML document. Custom application classes that perform specific actions within the parsing process can implement each of these interfaces. These implementation classes can be registered with the reader using the methods setContentHandler( ) , setErrorHandler( ), setDTDHandler( ), and setEntityResolver( ). Then the reader invokes the callback methods on the appropriate handlers during parsing.

For the SAXTreeViewer example, a good start is to implement the ContentHandler interface. This interface defines several important methods within the parsing lifecycle that our application can react to. Since all the necessary import statements are in place (I cheated and put them in already), all that is needed is to code an implementation of the ContentHandler interface. For simplicity, I'll do this as a nonpublic class, still within the SAXTreeViewer.java source file. Add in the JTreeContentHandler class, as shown here:

class JTreeContentHandler implements ContentHandler {

    /** Tree Model to add nodes to */
    private DefaultTreeModel treeModel;

    /** Current node to add sub-nodes to */
    private DefaultMutableTreeNode current;

    public JTreeContentHandler(DefaultTreeModel treeModel,
                               DefaultMutableTreeNode base) {
        this.treeModel = treeModel;
        this.current = base;
    }

    // ContentHandler method implementations
}

Don't bother trying to compile the source file at this point; you'll get a ton of errors about methods defined in ContentHandler not being implemented. The rest of this section walks through each of these methods, adding as we go. In this basic class, it's enough to pass in the TreeModel implementation, which is used to add new nodes to the JTree, and the base node (created in the buildTree( ) method, earlier). The base node is set to a member variable called current; this variable always points to the node being worked with, and the code needs to move that node down the tree hierarchy (when nested elements are found), as well as back up the tree (when elements end and the parent becomes current again). With that in place, it's time to look at the various ContentHandler callbacks and implement each. First take a quick glance at the ContentHandler interface, which shows the callbacks that need to be implemented:

public interface ContentHandler {
    public void setDocumentLocator(Locator locator);
    public void startDocument( ) throws SAXException;
    public void endDocument( ) throws SAXException;
    public void startPrefixMapping(String prefix, String uri)
        throws SAXException;
    public void endPrefixMapping(String prefix)
        throws SAXException;
    public void startElement(String namespaceURI, String localName,
                            String qName, Attributes atts)
        throws SAXException;
    public void endElement(String namespaceURI, String localName,
                      String qName)
        throws SAXException;
    public void characters(char ch[], int start, int length)
        throws SAXException;
    public void ignorableWhitespace(char ch[], int start, int length)
        throws SAXException;
    public void processingInstruction(String target, String data)
        throws SAXException;
    public void skippedEntity(String name)
        throws SAXException;
}

3.3.1. The Document Locator

The first method you need to define is one that sets an org.xml.sax.Locator for use within any other SAX events. When a callback event occurs, the class implementing a handler often needs access to the location of the SAX parser within an XML file. This is used to help the application make decisions about the event and its location within the XML document, such as determining the line on which an error occurred. The Locator class has several useful methods such as getLineNumber( ) and getColumnNumber( ) that return the current location of the parsing process within an XML file when invoked. Because this location is only valid for the current parsing lifecycle, the Locator should be used only within the scope of the ContentHandler implementation. Since this might be handy to use later, the code shown here saves the provided Locator instance to a member variable:

class JTreeContentHandler implements ContentHandler {

    /** Hold onto the locator for location information */
    private Locator locator;

    // Constructor

    public void setDocumentLocator(Locator locator) {
        // Save this for later use
        this.locator = locator;
    }
}

3.3.2. The Beginning and the End of a Document

In any lifecycle process, there must always be a beginning and an end. These important events should each occur once, the former before all other events, and the latter after all other events. This rather obvious fact is critical to applications, as it allows them to know exactly when parsing begins and ends. SAX provides callback methods for each of these events, startDocument( ) and endDocument( ).

The first method, startDocument( ), is called before any other callbacks, including the callback methods within other SAX handlers, such as DTDHandler. In other words, startDocument( ) is not only the first method called within ContentHandler, but also within the entire parsing process, aside from the setDocument-Locator( ) method just discussed. This ensures a finite beginning to parsing, and lets the application perform any tasks it needs to before parsing takes place.

The second method, endDocument( ), is always the last method called, again across all handlers. This includes situations in which errors occur that cause parsing to halt. I will discuss errors later, but there are both recoverable errors and unrecoverable errors. If an unrecoverable error occurs, the ErrorHandler's callback method is invoked, and then a final call to endDocument( ) completes the attempted parsing.

In the example code, no visual event should occur with these methods; however, as with implementing any interface, the methods must still be present:

    public void startDocument( ) throws SAXException {
        // No visual events occur here
    }

    public void endDocument( ) throws SAXException {
        // No visual events occur here
    }

Both of these callback methods can throw SAXExceptions. The only types of exceptions that SAX events ever throw, they provide another standard interface to the parsing behavior. However, these exceptions often wrap other exceptions that indicate what problems have occurred. For example, if an XML file was parsed over the network via a URL, and the connection suddenly became invalid, a java.net.SocketException might occur. However, an application using the SAX classes should not have to catch this exception, because it should not have to know where the XML resource is located (it might be a local file, as opposed to a network resource). Instead, the application can catch the single SAXException. Within the SAX reader, the original exception is caught and rethrown as a SAXException, with the originating exception stuffed inside the new one. This allows applications to have one standard exception to trap for, while allowing specific details of what errors occurred within the parsing process to be wrapped and made available to the calling program through this standard exception. The SAXException class provides a method, getException( ), which returns the underlying Exception (if one exists).

3.3.3. Processing Instructions

I talked about processing instructions (PIs) within XML as a bit of a special case. They were not considered XML elements, and were handled differently by being made available to the calling application. Because of these special characteristics, SAX defines a specific callback for handling processing instructions. This method receives the target of the processing instruction and any data sent to the PI. For this chapter's example, the PI can be converted to a new node and displayed in the tree viewer:

    public void processingInstruction(String target, String data)
        throws SAXException {

        DefaultMutableTreeNode pi =
            new DefaultMutableTreeNode("PI (target = '" + target +
                                       "', data = '" + data + "')");
        current.add(pi);
    }

In a real application using XML data, this is where an application could receive instructions and set variable values or execute methods to perform application-specific processing. For example, the Apache Cocoon publishing framework might set flags to perform transformations on the data once it is parsed, or to display the XML as a specific content type. This method, like the other SAX callbacks, throws a SAXException when errors occur.

NOTE: It's worth pointing out that this method will not receive notification of the XML declaration:
 <?xml version="1.0" standalone="yes"?>

In fact, SAX provides no means of getting at this information (and you'll find out that it's not currently part of DOM or JDOM, either!). The general underlying principle is that this information is for the XML parser or reader, not the consumer of the document's data. For that reason, it's not exposed to the developer.

3.3.4. Namespace Callbacks

From the discussion of namespaces in Chapter 2, "Nuts and Bolts", you should be starting to realize their importance and impact on parsing and handling XML. Alongside XML Schema, XML Namespaces is easily the most significant concept added to XML since the original XML 1.0 Recommendation. With SAX 2.0, support for namespaces was introduced at the element level. This allows a distinction to be made between the namespace of an element, signified by an element prefix and an associated namespace URI, and the local name of an element. In this case, the term local name refers to the unprefixed name of an element. For example, the local name of the ora:copyright element is simply copyright. The namespace prefix is ora, and the namespace URI is declared as http://www.oreilly.com.

There are two SAX callbacks specifically dealing with namespaces. These callbacks are invoked when the parser reaches the beginning and end of a prefix mapping. Although this is a new term, it is not a new concept; a prefix mapping is simply an element that uses the xmlns attribute to declare a namespace. This is often the root element (which may have multiple mappings), but can be any element within an XML document that declares an explicit namespace. For example:

<catalog>
  <books>
    <book title="XML in a Nutshell"
          xmlns:xlink="http://www.w3.org/1999/xlink">
      <cover xlink:type="simple" xlink:show="onLoad"
             xlink:href="xmlnutCover.jpg" ALT="XML in a Nutshell"
             width="125" height="350" />
    </book>
  </books>
</catalog>

In this case, an explicit namespace is declared several element nestings deep within the document. That prefix and URI mapping (in this case, xlink and http://www.w3.org/1999/xlink, respectively) are then available to elements and attributes within the declaring element.

The startPrefixMapping( ) callback is given the namespace prefix as well as the URI associated with that prefix. The mapping is considered "closed" or "ended" when the element that declared the mapping is closed, which triggers the endPrefixMapping( ) callback. The only twist to these callbacks is that they don't quite behave in the sequential manner in which SAX usually is structured; the prefix mapping callback occurs directly before the callback for the element that declares the namespace, and the ending of the mapping results in an event just after the close of the declaring element. However, it actually makes a lot of sense: for the declaring element to be able to use the declared namespace mapping, the mapping must be available before the element's callback. It works in just the opposite way for ending a mapping: the element must close (as it may use the namespace), and then the namespace mapping can be removed from the list of available mappings.

In the JTreeContentHandler, there aren't any visual events that should occur within these two callbacks. However, a common practice is to store the prefix and URI mappings in a data structure. You will see in a moment that the element callbacks report the namespace URI, but not the namespace prefix. If you don't store these prefixes (reported through startPrefixMapping( )), they won't be available in your element callback code. The easiest way to do this is to use a Map, add the reported prefix and URI to this Map in startPrefixMapping( ), and then remove them in endPrefixMapping( ). This can be accomplished with the following code additions:

class JTreeContentHandler implements ContentHandler {

    /** Hold onto the locator for location information */
    private Locator locator;

    /** Store URI to prefix mappings */
    private Map namespaceMappings;

    /** Tree Model to add nodes to */
    private DefaultTreeModel treeModel;

    /** Current node to add sub-nodes to */
    private DefaultMutableTreeNode current;

    public JTreeContentHandler(DefaultTreeModel treeModel,
                               DefaultMutableTreeNode base) {
        this.treeModel = treeModel;
        this.current = base;
        this.namespaceMappings = new HashMap( );
    }

    // Existing methods

    public void startPrefixMapping(String prefix, String uri) {
        // No visual events occur here.
        namespaceMappings.put(uri, prefix);
    }

    public void endPrefixMapping(String prefix) {
        // No visual events occur here.
        for (Iterator i = namespaceMappings.keySet().iterator( );
             i.hasNext( ); ) {

            String uri = (String)i.next( );
            String thisPrefix = (String)namespaceMappings.get(uri);
            if (prefix.equals(thisPrefix)) {
                namespaceMappings.remove(uri);
                break;
            }
        }
    }
}

One thing of note: I used the URI as a key to the mappings, rather than the prefix. As I mentioned a moment ago, the startElement( ) callback reports the namespace URI for the element, not the prefix. So keying on URIs makes those lookups faster. However, as you see in endPrefixMapping( ), it does add a little bit of work to removing the mapping when it is no longer available. In any case, storing namespace mappings in this fashion is a fairly typical SAX trick, so store it away in your toolkit for XML programming.

WARNING: The solution shown here is far from a complete one in terms of dealing with more complex namespace issues. It's perfectly legal to reassign prefixes to new URIs for an element's scope, or to assign multiple prefixes to the same URI. In the example, this would result in widely scoped namespace mappings being overwritten by narrowly scoped ones in the case where identical URIs were mapped to different prefixes. In a more robust application, you would want to store prefixes and URIs separately, and have a method of relating the two without causing overwriting. However, you get the idea in the example of how to handle namespaces in the general sense.

3.3.5. Element Callbacks

By now you are probably ready to get to the data in the XML document. It is true that over half of the SAX callbacks have nothing to do with XML elements, attributes, and data. This is because the process of parsing XML is intended to do more than simply provide your application with the XML data; it should give the application instructions from XML PIs so your application knows what actions to take, let the application know when parsing begins and when it ends, and even tell it when there is whitespace that can be ignored! If some of these callbacks don't make much sense yet, keep reading.

Of course, there certainly are SAX callbacks intended to give you access to the XML data within your documents. The three primary events involved in getting that data are the start and end of elements and the characters( ) callback. These tell you when an element is parsed, the data within that element, and when the closing tag for that element is reached. The first of these, startElement( ), gives an application information about an XML element and any attributes it may have. The parameters to this callback are the name of the element (in various forms) and an org.xml.sax.Attributes instance. This helper class holds references to all of the attributes within an element. It allows easy iteration through the element's attributes in a form similar to a Vector. In addition to being able to reference an attribute by its index (used when iterating through all attributes), it is possible to reference an attribute by its name. Of course, by now you should be a bit cautious when you see the word "name" referring to an XML element or attribute, as it can mean various things. In this case, either the complete name of the attribute (with a namespace prefix, if any), called its Q name, can be used, or the combination of its local name and namespace URI if a namespace is used. There are also helper methods such as getURI(int index) and getLocal-Name(int index) that help give additional namespace information about an attribute. Used as a whole, the Attributes interface provides a comprehensive set of information about an element's attributes.

In addition to the element attributes, you get several forms of the element's name. This again is in deference to XML namespaces. The namespace URI of the element is supplied first. This places the element in its correct context across the document's complete set of namespaces. Then the local name of the element is supplied, which is the unprefixed element name. In addition (and for backwards compatibility), the Q name of the element is supplied. This is the unmodified, unchanged name of the element, which includes a namespace prefix if present; in other words, exactly what was in the XML document: ora:copyright for the copyright element. With these three types of names supplied, you should be able to describe an element with or without respect to its namespace.

In the example, several things occur that illustrate this capability. First, a new node is created and added to the tree with the local name of the element. Then, that node becomes the current node, so all nested elements and attributes are added as leaves. Next, the namespace is determined, using the supplied namespace URI and the namespaceMappings object (to get the prefix) that you just added to the code from the last section. This is added as a node, as well. Finally, the code iterates through the Attributes interface, adding each (with local name and namespace information) as a child node. The code to accomplish all this is shown here:

    public void startElement(String namespaceURI, String localName,
                             String qName, Attributes atts)
        throws SAXException {

        DefaultMutableTreeNode element =
            new DefaultMutableTreeNode("Element: " + localName);
        current.add(element);
        current = element;

        // Determine namespace
        if (namespaceURI.length( ) > 0) {
            String prefix =
                (String)namespaceMappings.get(namespaceURI);
            if (prefix.equals("")) {
                prefix = "[None]";
            }
            DefaultMutableTreeNode namespace =
                new DefaultMutableTreeNode("Namespace: prefix = '" +
                    prefix + "', URI = '" + namespaceURI + "'");
            current.add(namespace);
        }

        // Process attributes
        for (int i=0; i<atts.getLength( ); i++) {
            DefaultMutableTreeNode attribute =
                new DefaultMutableTreeNode("Attribute (name = '" +
                                           atts.getLocalName(i) +
                                           "', value = '" +
                                           atts.getValue(i) + "')");
            String attURI = atts.getURI(i);
            if (attURI.length( ) > 0) {
                String attPrefix =
                    (String)namespaceMappings.get(namespaceURI);
                if (attPrefix.equals("")) {
                    attPrefix = "[None]";
                }
                DefaultMutableTreeNode attNamespace =
                    new DefaultMutableTreeNode("Namespace: prefix = '" +
                        attPrefix + "', URI = '" + attURI + "'");
                attribute.add(attNamespace);
            }
            current.add(attribute);
        }
    }

The end of an element is much easier to code. Since there is no need to give any visual information, all that must be done is to walk back up the tree one node, leaving the element's parent as the new current node:

    public void endElement(String namespaceURI, String localName,
                           String qName)
        throws SAXException {

        // Walk back up the tree
        current = (DefaultMutableTreeNode)current.getParent( );
    }

One final note before moving on to element data: you may have noticed that with a namespace URI and an element's Q name, it would be possible to figure out the prefix as well as the URI from the information supplied to the startElement( ) callback, without having to use a map of namespace associations. That's absolutely true, and would serve the example code well. However, most applications have hundreds and even thousands of lines of code in these callbacks (or, better yet, in methods invoked from code within these callbacks). In those cases, relying on parsing of the element's Q name is not nearly as robust a solution as storing the data in a custom structure. In other words, splitting the Q name on a colon is great for simple applications, but isn't so wonderful for complex (and therefore more realistic) ones.

3.3.6. Element Data

Once the beginning and end of an element block are identified and the element's attributes are enumerated for an application, the next piece of important information is the actual data contained within the element itself. This generally consists of additional elements, textual data, or a combination of the two. When other elements appear, the callbacks for those elements are initiated, and a type of pseudo-recursion happens: elements nested within elements result in callbacks "nested" within callbacks. At some point, though, textual data will be encountered. Typically the most important information to an XML client, this data is usually either what is shown to the client or what is processed to generate a client response.

In XML, textual data within elements is sent to a wrapping application via the characters( ) callback. This method provides the wrapping application with an array of characters as well as a starting index and the length of the characters to read. Generating a String from this array and applying the data is a piece of cake:

    public void characters(char[] ch, int start, int length)
        throws SAXException {

        String s = new String(ch, start, length);
        DefaultMutableTreeNode data =
            new DefaultMutableTreeNode("Character Data: '" + s + "'");
        current.add(data);
    }

Seemingly a simple callback, this method often results in a significant amount of confusion because the SAX interface and standards do not strictly define how this callback must be used for lengthy pieces of character data. In other words, a parser may choose to return all contiguous character data in one invocation, or split this data up into multiple method invocations. For any given element, this method will be called not at all (if no character data is present within the element) or one or more times. Parsers implement this behavior differently, often using algorithms designed to increase parsing speed. Never count on having all the textual data for an element within one callback method; conversely, never assume that multiple callbacks would result from one element's contiguous character data.

As you write SAX event handlers, be sure to keep your mind in a hierarchical mode. In other words, you should not get in the habit of thinking that an element owns its data and child elements, but only that it serves as a parent. Also keep in mind that the parser is moving along, handling elements, attributes, and data as it comes across them. This can make for some surprising results. Consider the following XML document fragment:

<parent>This element has <child>embedded text</child> within it.</parent>

Forgetting that SAX parses sequentially, making callbacks as it sees elements and data, and forgetting that the XML is viewed as hierarchical, you might make the assumption that the output here would be something like Figure 3-2.

Figure 3-2. Expected, and incorrect, graphical tree

This seems logical, as the parent element completely "owns" the child element. But what actually occurs is that a callback is made at each SAX event-point, resulting in the tree shown in Figure 3-3.

Figure 3-3. Actual generated tree

SAX does not read ahead, so the result is exactly what you would expect if you viewed the XML document as sequential data, without all the human assumptions that we tend to make. This is an important point to remember.

WARNING: Currently, neither Apache Xerces nor just about any other parser available performs validation by default. In the example program, since nothing has been done to turn it on, no validation occurs. However, that does not mean that a DTD or schema is not processed, again in almost all cases. Note that even without validation, an exception resulted when no system ID could be found, and the DTD reference could not be resolved (in the section on InputSources). So be sure to realize the difference between validation occurring, and DTD or schema processing occurring. Triggering of ignorableWhitespace( ) only requires that DTD or schema processing occurs, not that validation occurs.

Finally, whitespace is often reported by the characters( ) method. This introduces additional confusion, as another SAX callback, ignorableWhitespace( ), also reports whitespace. Unfortunately, a lot of books (including, I'm embarrassed to admit, my first edition of Java and XML) got the details of whitespace either partially or completely wrong. So, let me take this opportunity to set the record straight. First, if no DTD or XML Schema is referenced, the ignorable-Whitespace( ) method should never be invoked. Period.

The reason is that a DTD (or schema) details the content model for an element. In other words, in the JavaXML.dtd file, the contents element can only have chapter elements within it. Any whitespace between the start of the contents element and the start of a chapter element is (by logic) ignorable. It doesn't mean anything, because the DTD says not to expect any character data (whitespace or otherwise). The same thing applies for whitespace between the end of a chapter element and the start of another chapter element, or between it and the end of the contents element. Because the constraints (in DTD or schema form) specify that no character data is allowed, this whitespace cannot be meaningful. However, without a constraint specifying that information to a parser, that whitespace cannot be interpreted as meaningless. So by removing the reference to a DTD, these various whitespaces would trigger the characters( ) callback, where previously they triggered the ignorableWhitespace( ) callback. Thus whitespace is never simply ignorable, or nonignorable; it all depends on what (if any) constraints are referenced. Change the constraints, and you might change the meaning of the whitespace.

Let's dive even deeper. In the case where an element can only have other elements within it, things are reasonably clear. Whitespace in between elements is ignorable. However, consider a mixed content model:

<!ELEMENT p (b* | i* | a* | #PCDATA)>

If this looks like gibberish, think of HTML; it represents (in part) the constraints for the p element, or paragraph tag. Of course, text within this tag can exist, and also bold (b), italics (i), and links (a) elements as well. In this model, there is no whitespace between the starting and ending p tags that will ever be reported as ignorable (with or without a DTD or schema reference). That's because it's impossible to distinguish between whitespace used for readability and whitespace that is supposed to be in the document. For example:

<p>
  <i>Java and XML</i>, 2nd edition, is now available at bookstores, as
    well as through O'Reilly at
    <a href="http://www.oreilly.com">http://www.oreilly.com</a>.
</p>

In this XHTML fragment, the whitespace between the opening p element and the opening i element is not ignorable, and therefore reported through the characters( ) callback. If you aren't completely confused (and I don't think you are), be prepared to closely monitor both of the character-related callbacks. That will make explaining the last SAX callback related to this issue a snap.

3.3.7. Ignorable Whitespace

With all that whitespace discussion done, adding an implementation for the ignorableWhitespace( ) method is a piece of cake. Since the whitespace reported is ignorable, the code does just that -- ignore it:

    public void ignorableWhitespace(char[] ch, int start, int length)
        throws SAXException {

        // This is ignorable, so don't display it
    }

Whitespace is reported in the same manner as character data; it can be reported with one callback, or a SAX parser may break up the whitespace and report it over several method invocations. In either case, adhere closely to the precautions about not making assumptions or counting on whitespace as textual data in order to avoid troublesome bugs in your applications.

3.3.8. Entities

As you recall, there is only one entity reference in the contents.xml document, OReillyCopyright. When parsed and resolved, this results in another file being loaded, either from the local filesystem or some other URI. However, validation is not turned on in the reader implementation being used.[4] An often overlooked facet of nonvalidating parsers is that they are not required to resolve entity references, and instead may skip them. This has caused some headaches before, as parser results may simply not include entity references that were expected to be included. SAX 2.0 nicely accounts for this with a callback that is issued when an entity is skipped by a nonvalidating parser. The callback gives the name of the entity, which can be included in the viewer's output:

[4]I'm assuming that even if you aren't using Apache Xerces, your parser does not leave validation on by default. If you get different results than shown in this chapter, consult your documentation and see if validation is on. If it is, sneak a peek at Chapter 4, "Advanced SAX " and see how to turn it off.

    public void skippedEntity(String name) throws SAXException {
        DefaultMutableTreeNode skipped =
            new DefaultMutableTreeNode("Skipped Entity: '" + name + "'");
        current.add(skipped);
    }

Before you go looking for the OReillyCopyright node, though, you should be aware that most established parsers will not skip entities, even if they are not validating. Apache Xerces, for example, never invokes this callback; instead, the entity reference is expanded and the result included in the data available after parsing. In other words, it's there for parsers to use, but you will be hard-pressed to find a case where it crops up! If you do have a parser that exhibits this behavior, note that the parameter passed to the callback does not include the leading ampersand and trailing semicolon in the entity reference. For &OReillyCopyright;, only the name of the entity reference, OReillyCopyright, is passed to skippedEntity( ).

3.3.9. The Results

Finally, you need to register the content handler implementation with the XMLReader you've instantiated. This is done with setContentHandler( ). Add the following lines to the buildTree( ) method:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        ContentHandler jTreeContentHandler =
            new JTreeContentHandler(treeModel, base);

        // Register content handler
        reader.setContentHandler(jTreeContentHandler);

        // Register error handler

        // Parse
        InputSource inputSource =
            new InputSource(xmlURI);
        reader.parse(inputSource);
    }

If you have entered all of these document callbacks, you should be able to compile the SAXTreeViewer source file. Once done, you may run the SAX viewer demonstration on the XML sample file created earlier. Also, make sure that you have added your working directory to the classpath. The complete Java command should read:

C:\javaxml2\build>java javaxml2.SAXTreeViewer ..\ch03\xml\contents.xml

This should result in a Swing window firing up, loaded with the XML document's content. If you experience a slight pause in startup, you are probably waiting on your machine to connect to the Internet and resolve the OReillyCopyright entity reference. If you aren't online, refer to Chapter 2, "Nuts and Bolts" for instructions on replacing the reference in the DTD with a local copyright file. In any case, your output should look similar to Figure 3-4, depending on what nodes you have expanded.

Figure 3-4. SAXTreeViewer in action

A couple of things to notice: first, the surrounding whitespace of elements is not present, since the presence of a DTD and strict content model forces that whitespace to be ignored (as it is reported to the ignorableWhitespace( ) callback). Second, the entity reference is resolved, and you see the contents of the copyright.xml file nested within the larger tree structure. Also, because this file has no DTD, whitespace that might be considered ignorable is reported as character data through the characters( ) callback. That results in the odd little control characters in the tree's text value (these are most often carriage returns in the underlying document). Finally, notice how the text "O'Reilly & Associates" within copyright.xml is actually reported through three invocations of the characters( ) callback. This is a perfect illustration of textual data not being reported as one block of text. In this case, the parser split the text on the entity reference (&amp;), which is a common behavior. In any case, you should try running the viewer on different XML documents and see how the output changes.

You have now seen how a SAX-compliant parser handles a well-formed XML document. You should also be getting an understanding of the document callbacks that occur within the parsing process and of how an application can use these callbacks to get information about an XML document as it is parsed. In the next chapter, I will look at validating an XML document by using additional SAX classes designed for handling DTDs. Before moving on, though, I want to address the issue of what happens when your XML document is not valid, and the errors that can result from this condition.

Previous | Home | Next
3.2. SAX Readers | Book Index | 3.4. Error Handlers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Error Handlers (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

3.4. Error Handlers

In addition to providing the ContentHandler interface for handling parsing events, SAX provides an ErrorHandler interface that can be implemented to treat various error conditions that may arise during parsing. This class works in the same manner as the document handler already constructed, but defines only three callback methods. Through these three methods, all possible error conditions are handled and reported by SAX parsers. Here's a look at the ErrorHandler interface:

public interface ErrorHandler {
    public abstract void warning (SAXParseException exception)
        throws SAXException;
    public abstract void error (SAXParseException exception)
        throws SAXException;
    public abstract void fatalError (SAXParseException exception)
        throws SAXException;
}

Each method receives information about the error or warning that has occurred through a SAXParseException. This object holds the line number where the trouble was encountered, the URI of the document being treated (which could be the parsed document or an external reference within that document), and normal exception details such as a message and a printable stack trace. In addition, each method can throw a SAXException. This may seem a bit odd at first; an exception handler that throws an exception? Keep in mind that each handler receives a parsing exception. This can be a warning that should not cause the parsing process to stop or an error that needs to be resolved for parsing to continue; however, the callback may need to perform system I/O or another operation that can throw an exception, and it needs to be able to send any problems resulting from these actions up the application chain. It can do this through the SAXException the error handler callback is allowed to throw.

As an example, consider an error handler that receives error notifications and writes those errors to an error log. This callback method needs to be able to either append to or create an error log on the local filesystem. If a warning were to occur within the process of parsing an XML document, the warning would be reported to this method. The intent of the warning is to give information to the callback and then continue parsing the document. However, if the error handler could not write to the log file, it might need to notify the parser and application that all parsing should stop. This can be done by catching any I/O exceptions and rethrowing these to the calling application, thus causing any further document parsing to stop. This common scenario is why error handlers must be able to throw exceptions (see Example 3-2).

Example 3-2. Error handler that may throw a SAXException

public void warning(SAXParseException exception)
    throws SAXException {

    try {
        FileWriter fw = new FileWriter("error.log");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write("Warning: " + exception.getMessage( ) + "\n");
        bw.flush( );
        bw.close( );
        fw.close( );
    } catch (IOException e) {
        throw new SAXException("Could not write to log file", e);
    }
}

With this in mind, it's possible to define the skeleton of an ErrorHandler implementation and register it with the reader implementation in the same way that the content handler was registered. In the interests of keeping this book from becoming a treatise on Swing, these methods will just stop parsing and report warnings and errors through the command line. First, add another nonpublic class to the end of the SAXTreeViewer.java source file:

class JTreeErrorHandler implements ErrorHandler {

    // Method implementations

}

Next, in order to actually use the custom error handler, you need to register this error handler with your SAX reader. This is done with the setErrorHandler( ) method on the XMLReader instance, and needs to occur in the example's buildTree( ) method:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        ContentHandler jTreeContentHandler =
            new JTreeContentHandler(treeModel, base);
        ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

        // Register content handler
        reader.setContentHandler(jTreeContentHandler);

        // Register error handler
        reader.setErrorHandler(jTreeErrorHandler);

        // Parse
        InputSource inputSource =
            new InputSource(xmlURI);
        reader.parse(inputSource);
    }

Finally, let's take a look at coding the three methods required by the ErrorHandler interface.

3.4.1. Warnings

Any time a warning (as defined by the XML 1.0 specification) occurs, this method is invoked in the registered error handler. There are several conditions that can generate a warning; however, all of them are related to the DTD and validity of a document, and I will discuss them in the next chapter. For now, you just need to define a simple method that prints out the line number, URI, and warning message when a warning occurs. Because (for demonstration purposes) I want any warnings to stop parsing, this code throws a SAXException and lets the wrapping application exit gracefully, cleaning up any used resources:

    public void warning(SAXParseException exception)
        throws SAXException {

        System.out.println("**Parsing Warning**\n" +
                           "  Line:    " +
                              exception.getLineNumber( ) + "\n" +
                           "  URI:     " +
                              exception.getSystemId( ) + "\n" +
                           "  Message: " +
                              exception.getMessage( ));
        throw new SAXException("Warning encountered");
    }

3.4.2. Nonfatal Errors

Errors that occur within parsing that can be recovered from, but constitute a violation of some portion of the XML specification, are considered nonfatal errors. An error handler should always at least log these, as they are typically serious enough to merit informing the user or administrator of the application, if not so critical as to cause parsing to cease. Like warnings, most nonfatal errors are concerned with validation, and will be covered in the next chapter in more detail. Also like warnings, in the example this error handler just reports the information sent to the callback method and exits the parsing process:

    public void error(SAXParseException exception)
        throws SAXException {

        System.out.println("**Parsing Error**\n" +
                           "  Line:    " +
                              exception.getLineNumber( ) + "\n" +
                           "  URI:     " +
                              exception.getSystemId( ) + "\n" +
                           "  Message: " +
                              exception.getMessage( ));
        throw new SAXException("Error encountered");
    }

3.4.3. Fatal Errors

Fatal errors are those that necessitate stopping the parser. These are typically related to a document not being well-formed, and make further parsing either a complete waste of time or technically impossible. An error handler should almost always notify the user or application administrator when a fatal error occurs; without intervention, these can bring an application to a shuddering halt. For the example, I'll just emulate the behavior of the other two callback methods, stopping the parsing and writing an error message to the screen when a fatal error is encountered:

    public void fatalError(SAXParseException exception)
        throws SAXException {

        System.out.println("**Parsing Fatal Error**\n" +
                           "  Line:    " +
                              exception.getLineNumber( ) + "\n" +
                           "  URI:     " +
                              exception.getSystemId( ) + "\n" +
                           "  Message: " +
                              exception.getMessage( ));
        throw new SAXException("Fatal Error encountered");
    }

With this third error handler coded, you should be able to compile the example source file successfully and run it on the XML document again. Your output should not be any different than it was earlier, as there are no reportable errors in the XML. Next, I'll show you how to make some of these errors occur (for testing purposes, of course!).

3.4.4. Breaking the Data

Now that some error handlers are in place, it is worthwhile to generate some problems and see these handlers in action. Most warnings and nonfatal errors are associated with document validity issues, which I will address in the next chapter (when turning on validation is covered in detail). However, there is one nonfatal error that results from a nonvalidated XML document, involving the version of XML that a document reports. To view this error, make the following change to the first line of the XML table of contents example:

<?xml version="1.2"?>

Now run the Java SAX viewer program on the modified XML document. Your output should be similar to that shown here:

C:\javaxml2\build>java javaxml2.SAXTreeViewer ..\ch03\xml\contents.xml
**Parsing Error**
  Line:    1
  URI:     file:///C:/javaxml2/ch03/xml/contents.xml
  Message: XML version "1.2" is not supported.
org.xml.sax.SAXException: Error encountered

When an XML parser is operating upon a document that reports a version of XML greater than that supported by the parser, a nonfatal error is reported, in accordance with the XML 1.0 specification. This tells an application that newer features expected to be utilized by the document may not be available within the parser and the version that it supports. Because parsing continues, this is a nonfatal error. However, because it signifies a major impact on the document (such as newer syntax possibly generating subsequent errors), it is considered more important than a warning. This is why the error( ) method is invoked and triggers the error message and parsing halt in the example program.

All other meaningful warnings and nonfatal errors will be discussed in the next chapter; still, there is a variety of fatal errors that a nonvalidated XML document may have. These are related to an XML document not being well-formed. There is no logic built into XML parsers to try to resolve or estimate fixes to malformed XML, so an error in syntax results in the parsing process halting. The easiest way to demonstrate one of these errors is to introduce problems within your XML document. Reset the XML declaration to specify an XML version of 1.0, and make the following change to the XML document:

<?xml version="1.0"?>
<!DOCTYPE Book SYSTEM "DTD/JavaXML.dtd">

<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>
  <!-- Note the missing end slash on the title element -->
  <title ora:series="Java">Java and XML<title>

  <!-- Rest of content -->
</book>

This is no longer a well-formed document. To see the fatal error that parsing this document generates, run the SAXVTreeViewer program on this modified file to get the following the output:

C:\javaxml2\build>java javaxml2.SAXTreeViewer ..\ch03\xml\contents.xml
**Parsing Fatal Error**
  Line:    23
  URI:     file:///C:/javaxml2/ch03/xml/contents.xml
  Message: The element type "title" must be terminated by the matching
           end-tag "</title>".
org.xml.sax.SAXException: Fatal Error encountered

The parser reports an incorrect ending to the title element. This fatal error is exactly as expected; parsing could not continue beyond this error. With this error handler, you begin to see what can go wrong within the parsing process, as well as how to handle those events. In Chapter 4, "Advanced SAX " I will revisit the error handler and its methods and look at the problems that can be reported by a validating parser.

Previous | Home | Next
3.3. Content Handlers | Book Index | 3.5. Gotcha!
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Gotcha! (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

3.5. Gotcha!

Before leaving this introduction to parsing XML documents with SAX, there are a few pitfalls to make you aware of. These "gotchas" will help you avoid common programming mistakes when using SAX, and I will discuss more of these for other APIs in the appropriate sections.

3.5.1. My Parser Doesn't Support SAX 2.0

For those of you who are forced to use a SAX 1.0 parser, perhaps in an existing application, don't despair. First, you always have the option of changing parsers; keeping current on SAX standards is an important part of an XML parser's responsibility, and if your vendor is not doing this, you may have other concerns to address with them as well. However, there are certainly cases where you are forced to use a parser because of legacy code or applications; in these situations, you are still not left out in the cold.

SAX 2.0 includes a helper class, org.xml.sax.helpers.ParserAdapter, which can actually cause a SAX 1.0 Parser implementation to behave like a SAX 2.0 XMLReader implementation. This handy class takes in a 1.0 Parser implementation as an argument and then can be used instead of that implementation. It allows a ContentHandler to be set (which is a SAX 2.0 construct), and handles all namespace callbacks properly (also a feature of SAX 2.0). The only functionality loss you will see is that skipped entities will not be reported, as this capability was not available in a 1.0 implementation in any form, and cannot be emulated by a 2.0 adapter class. Example 3-3 shows this behavior in action.

Example 3-3. Using SAX 1.0 with SAX 2.0 code constructs

try {
    // Register a parser with SAX
    Parser parser =
        ParserFactory.makeParser(
            "org.apache.xerces.parsers.SAXParser");

    ParserAdapter myParser = new ParserAdapter(parser);

    // Register the document handler
    myParser.setContentHandler(contentHandler);

    // Register the error handler
    myParser.setErrorHandler(errHandler);

    // Parse the document
    myParser.parse(uri);

} catch (ClassNotFoundException e) {
    System.out.println(
        "The parser class could not be found.");
} catch (IllegalAccessException e) {
    System.out.println(
        "Insufficient privileges to load the parser class.");
} catch (InstantiationException e) {
    System.out.println(
        "The parser class could not be instantiated.");
} catch (ClassCastException e) {
    System.out.println(
        "The parser does not implement org.xml.sax.Parser");
} catch (IOException e) {
    System.out.println("Error reaading URI: " + e.getMessage( ));
} catch (SAXException e) {
    System.out.println("Error in parsing: " + e.getMessage( ));
}

If SAX is new to you and this example doesn't make much sense, don't worry about it; you are using the latest and greatest version of SAX (2.0) and probably won't ever have to write code like this. This code is helpful only in cases where a 1.0 parser must be used.

3.5.2. The SAX XMLReader: Reused and Reentrant

One of Java's nicest features is the easy reuse of objects, and the memory advantages of this reuse. SAX parsers are no different. Once an XMLReader has been instantiated, it is possible to continue using that reader, parsing several or even hundreds of XML documents. Different documents or InputSources may be continually passed to a reader, allowing it to be used for a variety of different tasks. However, readers are not reentrant. That means that once the parsing process has started, a reader may not be used until the parsing of the requested document or input has completed. In other words, the process cannot be reentered. For those prone to coding recursive methods, this is definitely a gotcha! The first time that you attempt to use a reader that is in the middle of processing another document, you will receive a rather nasty SAXException and all parsing will stop. What is the lesson learned? Parse one document at a time, or pay the price of instantiating multiple reader instances.

3.5.3. The Misplaced Locator

Another dangerous but seemingly innocuous feature of SAX events is the Locator instance that is made available through the setDocumentLocator( ) callback method. This gives the application the origin of a SAX event, and is useful for making decisions about the progress of parsing and reaction to events. However, this origin point is valid only for the duration of the life of the ContentHandler instance; once parsing is complete, the Locator is no longer valid, including the case when another parse begins. A "gotcha" that many XML newcomers make is to hold a reference to the Locator object within a class member variable outside of the callback method:

public void setDocumentLocator(Locator locator) {
    // Saving the Locator to a class outside the ContentHandler
    myOtherClass.setLocator(locator);
}
...

public myOtherClassMethod( ) {
    // Trying to use this outside of the ContentHandler
    System.out.println(locator.getLineNumber( ));
}

This is an extremely bad idea, as this Locator instance becomes meaningless as soon as the scope of the ContentHandler implementation is left. Often, using the member variable resulting from this operation results in not only erroneous information being supplied to an application, but exceptions being generated in the running code. In other words, use this object locally, and not globally. In the JTreeContentHandler implementation class, the supplied Locator instance is saved to a member variable. It could then correctly be used (for example) to give you the line number of each element as it was encountered:

public void startElement(String namespaceURI, String localName,
                         String rawName, Attributes atts)
    throws SAXException {

    DefaultMutableTreeNode element =
        new DefaultMutableTreeNode("Element: " + localName +
            " at line " + locator.getLineNumber());
    current.add(element);
    // Rest of existing code...
}

3.5.4. Getting Ahead of the Data

The characters( ) callback method accepts a character array, as well as start and length parameters, to signify which index to start at and how far to read into the array. This can cause some confusion; a common mistake is to include code like this example to read from the character array:

public void characters(char[] ch, int start, int length)
    throws SAXException {

    for (int i=0; i<ch.length; i++)
        System.out.print(i);
}

The mistake here is in reading from the beginning to the end of the character array. This natural "gotcha" results from years of iterating through arrays, either in Java, C, or another language. However, in the case of a SAX event, this can cause quite a bug. SAX parsers are required to pass starting and length values on the character array that any loop constructs should use to read from the array. This allows lower-level manipulation of textual data to occur in order to optimize parser performance, such as reading data ahead of the current location as well as array reuse. This is all legal behavior within SAX, as the expectation is that a wrapping application will not try to "read past" the length parameter sent to the callback.

Mistakes as in the example shown can result in gibberish data being output to the screen or used within the wrapping application, and are almost always problematic for applications. The loop construct looks very normal and compiles without a hitch, so this gotcha can be a very tricky problem to track down. Instead, you can simply convert this data to a String, use it, and never worry:

public void characters(char[] ch, int start, int length)
    throws SAXException {

    String data = new String(ch, start, length);
    // Use the string
}
Previous | Home | Next
3.4. Error Handlers | Book Index | 3.6. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

3.6. What's Next?

Now that you have a taste of SAX, you are ready for some of the more advanced features of the API. These include setting properties and features, using validation and namespace processing, and the EntityResolver and DTDHandler interfaces. Additionally, you'll take a look at many less used (but still valuable) features of the Simple API for XML, as well as the optional add-ons to SAX, such as filters and the org.xml.sax.ext package. This should get those of you who are using SAX in applications up, running, and even flying past developers around you. That's always good. Keep that editor humming, and turn the page.

Previous | Home | Next
3.5. Gotcha! | Book Index | 4. Advanced SAX
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Advanced SAX (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 4. Advanced SAX

Contents:

Properties and Features
More Handlers
Filters and Writers
Even More Handlers
Gotcha!
What's Next?

The last chapter was a good introduction to SAX. However, there are several more topics that will round out your knowledge of SAX. While I've called this chapter "Advanced SAX," don't be intimidated. It could just as easily be called "Less-Used Portions of SAX that are Still Important." In writing these two chapters, I followed the 80/20 principle. 80% of you will probably never need to use the material in this chapter, and Chapter 3, "SAX" will completely cover your needs. However, for those power users out there working in XML day in and day out, this chapter covers some of the finer points of SAX that you'll need.

I'll start with a look at setting parser properties and features, and discuss configuring your parser to do whatever you need it to. From there, I'll move on to some more handlers: the EntityResolver and DTDHandler left over from the last chapter. At that point, you should have a comprehensive understanding of the standard SAX 2.0 distribution. However, we'll push on to look at some SAX extensions, beginning with the writers that can be coupled with SAX, as well as some filtering mechanisms. Finally, I'll introduce some new handlers to you, the LexicalHandler and DeclHandler, and show you how they are used. When all is said and done (including another "Gotcha!" section), you should be ready to take on the world with just your parser and the SAX classes. So slip into your shiny spacesuit and grab the flightstick -- ahem. Well, I got carried away with the taking on the world. In any case, let's get down to it.

4.1. Properties and Features

With the wealth of XML-related specifications and technologies emerging from the World Wide Web Consortium (W3C), adding support for any new feature or property of an XML parser has become difficult. Many parser implementations have added proprietary extensions or methods at the cost of code portability. While these software packages may implement the SAX XMLReader interface, the methods for setting document and schema validation, namespace support, and other core features are not standard across parser implementations. To address this, SAX 2.0 defines a standard mechanism for setting important properties and features of a parser that allows the addition of new properties and features as they are accepted by the W3C without the use of proprietary extensions or methods.

4.1.1. Setting Properties and Features

Lucky for you and me, SAX 2.0 includes the methods needed for setting properties and features in the XMLReader interface. This means you have to change little of your existing code to request validation, set the namespace separator, and handle other feature and property requests. The methods used for these purposes are outlined in Table 4-1.

Table 4-1. Property and feature methods

Method | Returns | Parameters | Syntax
setProperty( ) | void | String propertyID, Object value | parser.setProperty("[Property URI]", propertyValue);
setFeature( ) | void | String featureID, boolean state | parser.setFeature("[Feature URI]", featureState);
getProperty( ) | Object | String propertyID | Object propertyValue = parser.getProperty("[Property URI]");
getFeature( ) | boolean | String featureID | boolean featureState = parser.getFeature("[Feature URI]");

For these methods, the ID of a specific property or feature is a URI. The core set of features and properties is listed in Appendix B, "SAX 2.0 Features and Properties". Additional documentation on features and properties supported by your vendor's XML parser should also be available. These URIs are similar to namespace URIs; they are only used as associations for particular features. Good parsers ensure that you do not need network access to resolve these features; think of them as simple constants that happen to be in URI form. These methods are simply invoked and the URI is dereferenced locally, often to constantly represent what action in the parser needs to be taken.

WARNING: Don't type these property and feature URIs into a browser to "check for their existence." Often, this results in a 404 Not Found error. I've had many browsers report this to me, insisting that the URIs are invalid. However, this is not the case; the URI is just an identifier, and as I pointed out, usually resolved locally. Trust me: just use the URI, and trust the parser to do the right thing.

In the parser configuration context, a property requires some object value to be usable. For example, for lexical handling, a DOM Node implementation would be supplied as the value for the appropriate property. In contrast, a feature is a flag used by the parser to indicate whether a certain type of processing should occur. Common features are validation, namespace support, and including external parameter entities.

The most convenient aspect of these methods is that they allow simple addition and modification of features. Although new or updated features will require a parser implementation to add supporting code, the method by which features and properties are accessed remains standard and simple; only a new URI need be defined. Regardless of the complexity (or obscurity) of new XML-related ideas, this robust set of four methods should be sufficient to allow parsers to implement the new ideas.

4.1.2. SAX Properties and Features

More often than not, the features and properties you deal with are the standard SAX-defined ones. These are features and properties that should be available with any SAX distribution, and that any SAX-compliant parser should support. Additionally, this preserves vendor-independence in your code, so I recommend that you use SAX-defined properties and features whenever possible.

4.1.2.1. Validation

The most common feature you'll use is the validation feature. The URI for this guy is http://xml.org/sax/features/validation, and not surprisingly, it turns validation on or off in the parser. For example, if you want to turn on validation in the parsing example from the last chapter (remember the Swing viewer?), make this change in the SAXTreeViewer.java source file:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        ContentHandler jTreeContentHandler =
            new JTreeContentHandler(treeModel, base);
        ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

        // Register content handler
        reader.setContentHandler(jTreeContentHandler);

        // Register error handler
        reader.setErrorHandler(jTreeErrorHandler);

        // Request validation
        reader.setFeature("http://xml.org/sax/features/validation", true);

        // Parse
        InputSource inputSource =
            new InputSource(xmlURI);
        reader.parse(inputSource);
    }

Compile these changes, and run the example program. Nothing happens, right? Not surprising; the XML we've looked at so far is all valid with respect to the DTD supplied. However, it's easy enough to fix that. Make the following change to your XML file (notice that the element in the DOCTYPE declaration no longer matches the actual root element, since XML is case-sensitive):

<?xml version="1.0"?>
<!DOCTYPE Book SYSTEM "DTD/JavaXML.dtd">

<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>

Now run your program on this modified document. Because validation is turned on, you should get an ugly stack trace reporting the error. Of course, because that's all that our error handler methods do, this is precisely what we want:

C:\javaxml2\build>java javaxml2.SAXTreeViewer
    c:\javaxml2\ch04\xml\contents.xml
**Parsing Error**
  Line:    7
  URI:     file:///c:/javaxml2/ch04/xml/contents.xml
  Message: Document root element "book", must match DOCTYPE root "Book".
org.xml.sax.SAXException: Error encountered
        at javaxml2.JTreeErrorHandler.error(SAXTreeViewer.java:445)
[Nasty Stack Trace to Follow...]

Remember, turning validation on or off does not affect DTD processing; I talked about this in the last chapter, and wanted to remind you of this subtle fact. To get a better sense of this, turn off validation (comment out the feature setting, or supply it the "false" value), and run the program on the modified XML. Even though the DTD is processed, as seen by the resolved OReillyCopyright entity reference, no errors occur. That's the difference between processing a DTD and validating an XML document against that DTD. Memorize, understand, and recite this to yourself; it will save you hours of confusion in the long run.

4.1.2.2. Namespaces

Next to validation, you'll most commonly deal with namespaces. There are two features related to namespaces: one that turns namespace processing on or off, and one that indicates whether namespace prefixes should be reported as attributes. The two are essentially tied together, and you should always "toggle" both, as shown in Table 4-2.

Table 4-2. Toggle values for namespace-related features

Value for namespace processing | Value for namespace prefix reporting
True | False
False | True

This should make sense: if namespace processing is on, the xmlns-style declarations on elements should not be exposed to your application as attributes, as they are only useful for namespace handling. However, if you do not want namespace processing to occur (or want to handle it on your own), you will want these xmlns declarations reported as attributes so you can use them just as you would use other attributes. However, if these two fall out of sync (both are true, or both are false), you can end up with quite a mess!

Consider writing a small utility method to ensure these two features stay in sync with each other. I often use the method shown here for this purpose:

private void setNamespaceProcessing(XMLReader reader, boolean state)
    throws SAXNotSupportedException, SAXNotRecognizedException {

    reader.setFeature(
        "http://xml.org/sax/features/namespaces", state);
    reader.setFeature(
        "http://xml.org/sax/features/namespace-prefixes", !state);
}

This maintains the correct setting for both features, and you can now simply call this method instead of two setFeature( ) invocations in your own code. Personally, I've used this feature less than ten times in about two years; the default values (processing namespaces as well as not reporting prefixes as attributes) almost always work for me. Unless you are writing low-level applications that either don't need namespaces or can use the speed increase obtained from not processing namespaces, or you need to handle namespaces on your own, I wouldn't worry too much about either of these features.

This code brings up a rather important aspect of features and properties, though: invoking the feature and property methods can result in SAXNotSupportedExceptions and SAXNotRecognizedExceptions. These are both in the org.xml.sax package, and need to be imported in any SAX code that uses them. The first indicates that the parser knows about the feature or property but doesn't support it. You won't run into this much in even average quality parsers, but it is commonly used when a standard property or feature is not yet coded in. So invoking setFeature( ) on the namespace processing feature on a parser in development might result in a SAXNotSupportedException. The parser recognizes the feature, but doesn't have the ability to perform the requested processing. The second exception most commonly occurs when using vendor-specific features and properties (covered in the next section), and then switching parser implementations. The new implementation won't know anything about the other vendor's features or properties, and will throw a SAXNotRecognizedException.

You should always explicitly catch these exceptions so you can deal with them. Otherwise, you end up losing valuable information about what happened in your code. For example, let me show you a modified version of the code from the last chapter that tries to set up various features, and how that changes the exception-handling architecture:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        String featureURI = "";

        try {
            // Create instances needed for parsing
            XMLReader reader =
                XMLReaderFactory.createXMLReader(vendorParserClass);
            ContentHandler jTreeContentHandler =
                new JTreeContentHandler(treeModel, base);
            ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

            // Register content handler
            reader.setContentHandler(jTreeContentHandler);

            // Register error handler
            reader.setErrorHandler(jTreeErrorHandler);

            /** Deal with features **/
            featureURI = "http://xml.org/sax/features/validation";

            // Request validation
            reader.setFeature(featureURI, true);

            // Namespace processing - on
            featureURI = "http://xml.org/sax/features/namespaces";
            setNamespaceProcessing(reader, true);

            // Turn on String interning
            featureURI = "http://xml.org/sax/features/string-interning";
            reader.setFeature(featureURI, true);

            // Turn off schema processing
            featureURI =
                "http://apache.org/xml/features/validation/schema";
            reader.setFeature(featureURI, false);

            // Parse
            InputSource inputSource =
                new InputSource(xmlURI);
            reader.parse(inputSource);
        } catch (SAXNotRecognizedException e) {
            System.out.println("The parser class " + vendorParserClass +
                " does not recognize the feature URI " + featureURI);
            System.exit(0);
        } catch (SAXNotSupportedException e) {
            System.out.println("The parser class " + vendorParserClass +
                " does not support the feature URI " + featureURI);
            System.exit(0);
        }
    }

By dealing with these exceptions as well as other special cases, you give the user better information and improve the quality of your code.

4.1.2.3. Interning and entities

The three remaining SAX-defined features are fairly obscure. The first, http://xml.org/sax/features/string-interning, turns string interning on or off. By default this is false (off) in most parsers. Setting it to true means that every element name, attribute name, namespace URI and prefix, and other strings have java.lang.String.intern() invoked on them. I'm not going to get into great detail about interning here; if you don't know what it is, check out Sun's Javadoc on the method at http://java.sun.com/j2se/1.3/docs/api/index.html. In a nutshell, every time a string is encountered, Java attempts to return an existing reference for the string in the current string pool, instead of (possibly) creating a new String object. Sounds like a good thing, right? Well, the reason it's off by default is most parsers have their own optimizations in place that can outperform string interning. My advice is to leave this setting alone; many people have spent weeks tuning things like this so you don't have to mess with them.

The other two features determine whether textual entities are expanded and resolved (http://xml.org/sax/features/external-general-entities), and whether parameter entities are included (http://xml.org/sax/features/external-parameter-entities) when parsing occurs. These are set to true for most parsers, as they deal with all the entities that XML has to offer. Again, I recommend you leave these settings as is, unless you have a specific reason for disabling entity handling.

4.1.2.4. DOM nodes and literal strings

The two standard SAX properties are a little less clear in their usage. In both cases, the properties are more useful for obtaining values, whereas with features the common use is to set values. Additionally, both properties are more helpful in error handling than in any general usage. And finally, both properties provide access to what is being parsed at a given time. The first, identified by the URI http://xml.org/sax/properties/dom-node, returns the current DOM node being processed, or the root DOM node if parsing isn't occurring. Of course, I haven't really talked about DOM yet, but this will make more sense in the next two chapters. The second property, identified by the URI http://xml.org/sax/properties/xml-string, returns the literal string of characters being processed. You'll find varying support for these properties in various parsers, showing that many parser implementers find these properties of arguable use as well. For example, Xerces does not support the xml-string property, to avoid having to buffer the input document (at least in that specific way). On the other hand, it does support the dom-node property so that you can turn a SAX parser into (essentially) a DOM tree iterator.

4.1.3. Proprietary Properties and Features

In addition to the standard, SAX-defined features and properties, most parsers define several features and properties of their own. For example, Apache Xerces has a page of features it supports at http://xml.apache.org/xerces-j/properties.html,and properties it supports at http://xml.apache.org/xerces-j/properties.html. I'm not going to cover these in great detail, and you should steer clear of them whenever possible; it locks your code into a specific vendor. However, there are times when using a vendor's specific functionality will save you some work. In those cases, exercise caution, but don't be foolish; use what your parser gives you!

As an example, take the Xerces feature that enables and disables XML schema processing: http://apache.org/xml/features/validation/schema. Because there is no standard support for XML schemas across parsers or in SAX, use this specific feature (it's set to true by default) to avoid spending parsing time to deal with any referenced XML schemas in your documents, for example. You save time in production if you don't use this processing, and it needs a vendor-specific feature. Check out your vendor documentation for options available in addition to SAX's.

Previous | Home | Next
3.6. What's Next? | Book Index | 4.2. More Handlers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


More Handlers (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

4.2. More Handlers

In the last chapter, I showed you the ContentHandler and ErrorHandler interfaces and briefly mentioned the EntityResolver and DTDHandler interfaces as well. Now that you've got a good understanding of SAX basics, you're ready to look at these two other handlers.[5] You'll find that you use EntityResolver every now and then (more if you're writing applications to be resold), and that the DTDHandler is something rarely ever pulled out of your bag of tricks.

[5]For the picky reader, I know that technically EntityResolver isn't a "handler," per se. Of course, I could easily argue that the interface might be named EntityHandler, so it's close enough for me!

4.2.1. Using an EntityResolver

The first of these new handlers is org.xml.sax.EntityResolver. This interface does exactly what it says: resolves entities (or at least declares a method that resolves entities, but you get the idea). The interface defines only a single method, and it looks like this:

public InputSource resolveEntity(String publicID, String systemID)
    throws SAXException, IOException;

You can create an implementation of this interface, and register it with your XMLReader instance (through setEntityResolver( ), not surprisingly). Once that's done, every time the reader comes across an entity reference, it passes the public ID and system ID for that entity to the resolveEntity( ) method of your implementation. Now you can change the normal process of entity resolution.

Typically, the XML reader resolves the entity through the specified public or system ID, whether it be a file, URL, or other resource. And if the return value from the resolveEntity( ) method is null, this process executes unchanged. As a result, you should always make sure that whatever code you add to your resolveEntity( ) implementation, it returns null in the default case. In other words, start with an implementation class that looks like Example 4-1.

Example 4-1. Simple implementation of EntityResolver

package javaxml2;

import java.io.IOException;

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class SimpleEntityResolver implements EntityResolver {

    public InputSource resolveEntity(String publicID, String systemID)
        throws IOException, SAXException {

        // In the default case, return null
        return null;
    }
}

You can compile this class with no problems, and register it with the reader implementation used in the SAXTreeViewer class within the buildTree( ) method:

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        ContentHandler jTreeContentHandler =
            new JTreeContentHandler(treeModel, base, reader);
        ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

        // Register content handler
        reader.setContentHandler(jTreeContentHandler);

        // Register error handler
        reader.setErrorHandler(jTreeErrorHandler);

        // Register entity resolver
        reader.setEntityResolver(new SimpleEntityResolver( ));

        // Other instructions and parsing...

Recompiling and rerunning the example class creates no change. Of course, that's exactly what was predicted, so don't be too surprised. By always returning a null value, the process of entity resolution proceeds normally. If you don't believe that anything is happening, though, you can make this small change to echo what's going on to the system output:

    public InputSource resolveEntity(String publicID, String systemID)
        throws IOException, SAXException {

        System.out.println("Found entity with public ID " + publicID +
            " and system ID " + systemID);

        // In the default case, return null
        return null;
    }

Recompile this class and run the sample tree viewer. Once the Swing GUI comes up, move it out of the way and check out the shell or command prompt output; it should look similar to Example 4-2.

Example 4-2. Output from SAXTreeViewer with verbose output

C:\javaxml2\build>java javaxml2.SAXTreeViewer
    c:\javaxml2\ch04\xml\contents.xml
Found entity with public ID null and
    system ID file:///c:/javaxml2/ch04/xml/DTD/JavaXML.dtd
Found entity with public ID null and
    system ID http://www.newInstance.com/javaxml2/copyright.xml

As always, the line breaks are purely for display purposes. In any case, you can see that both references in the XML document, for the DTD and the OReillyCopyright entity reference, are passed to the resolveEntity( ) method.

At this point, you might be scratching your head; a DTD is an entity? The term "entity" is a bit vague as it is used in EntityResolver. Perhaps a better name would have been ExternalReferenceResolver, but that wouldn't be very fun to type. In any case, keep in mind that any external reference in your XML is going to be passed on to this method. So what's the point, you may be asking yourself. Remember the reference for OReillyCopyright, and how it accesses an Internet URL (http://www.newInstance.com/javaxml2/copyright.xml)? What if you don't have Internet access? What if you have a local copy you already downloaded, and want to save time by using that copy? What if you simply want to put your own copyright in place? All of these are viable questions, and real-world problems that you may have to solve in your applications. The answer, of course, is the resolveEntity( ) method I've been talking about.

If you return a valid InputSource (instead of null) from this method, that InputSource is used as the value for the entity reference, rather than the public or system ID specified. In other words, you can specify your own data instead of letting the reader handle resolution on its own. As an example, create a copyright.xml file on your local machine, as shown in Example 4-3.

Example 4-3. Local copy of copyright.xml

<copyright xmlns="http://www.oreilly.com">
  <year value="2001" />
  <content>This is my local version of the copyright.</content>
</copyright>

Save this in a directory you can access from your Java code (I used the same directory as my contents.xml file), and make the following change to the resolveEntity( ) method:

    public InputSource resolveEntity(String publicID, String systemID)
        throws IOException, SAXException {

        // Handle references to online version of copyright.xml
        if (systemID.equals(
            "http://www.newInstance.com/javaxml2/copyright.xml")) {
            return new InputSource(
                "file:///c:/javaxml2/ch04/xml/copyright.xml");
        }

        // In the default case, return null
        return null;
    }

You can see that instead of allowing resolution to the online resource, an InputSource that provides access to the local version of copyright.xml is returned. If you recompile your source file and run the tree viewer, you can visually verify that this local copy is used. Figure 4-1 shows the ora:copyright element expanded, including the local copyright document's content.

Figure 4-1. SAXTreeViewer running with local copyrights.xml

In real-world applications, this method tends to become a lengthy laundry list of if/then/else blocks, each one handling a specific system or public ID. And this brings up an important point: try to avoid this class and method becoming a kitchen sink for IDs. If you no longer need a specific resolution to occur, remove the if clause for it. Additionally, try to use different EntityResolver implementations for different applications, rather than trying to use one generic implementation for all your applications. Doing this avoids code bloat, and more importantly, speeds up entity resolution. If you have to wait for your reader to run through fifty or a hundred String.equals( ) comparisons, you can really bog down an application. Be sure to put references accessed often at the top of the if/else stack, so they are encountered first and result in quicker entity resolution.

Finally, I want to make one more recommendation concerning your EntityResolver implementations. You'll notice that I defined my implementation in a separate class file, while the ErrorHandler, ContentHandler, and (in the next section) DTDHandler implementations were in the same source file as parsing occurred in. That wasn't an accident! You'll find that the way you deal with content, errors, and DTDs is fairly static. You write your program, and that's it. When you make changes, you're making a larger rewrite, and need to make big changes anyway. However, you'll make many changes to the way you want your application to resolve entities. Depending on the machine you're on, the type of client you're deploying to, and what and where documents are available, you'll need different versions of an EntityResolver implementation. To allow for rapid changes to this implementation without causing editing or recompilation of your core parsing code, I use a separate source file for EntityResolver implementations; I suggest you do the same. And with that, you should know all that you need to know about resolving entities in your applications using SAX.

4.2.2. Using a DTDHandler

After a rather extensive look at EntityResolver, I'm going to cruise through DTDHandler pretty quickly. In two years of extensive XML programming, I've used this interface only once, in writing JDOM, and even then it was a rather obscure case. More often than not, you won't work with it much unless you have lots of unparsed entities in your XML documents.

The DTDHandler interface allows you to receive notification when a reader encounters an unparsed entity or notation declaration. Of course, both of these events occur in DTDs, not XML documents, which is why this is called DTDHandler. Rather than go on and on, let me just show you what the interface looks like. It's right here for you to check out in Example 4-4.

Example 4-4. The DTDHandler interface

package org.xml.sax;

public interface DTDHandler {

    public void notationDecl(String name, String publicID,
                             String systemID)
        throws SAXException;

    public void unparsedEntityDecl(String name, String publicId,
                                   String systemId, String notationName)
         throws SAXException;
}

These two methods do exactly what you would expect. The first reports a notation declaration, including its name, public ID, and system ID. Remember the NOTATION structure in DTDs?

<!NOTATION jpeg SYSTEM "images/jpeg">

The second method provides information about an unparsed entity declaration, which looks as follows:

<!ENTITY stars_logo SYSTEM "http://www.nhl.com/img/team/dal38.gif"
                    NDATA jpeg>

In both cases, you can take action at these occurrences if you create an implementation of DTDHandler and register it with your reader through the XMLReader's setDTDHandler( ) method. This is generally useful when writing low-level applications that must either reproduce XML content (such as an XML editor), or when you want to build up some Java representation of a DTD's constraints (such as for data binding, covered in Chapter 15, "Data Binding"). In most other situations, it isn't something you will need very often.

4.2.3. The DefaultHandler Class

Before finishing up with handlers (for now, at least), there's one other important handler-related class you should know about. This class is org.xml.sax.helpers.DefaultHandler, and can be a very good friend to you SAX developers out there. Remember that so far, implementing the various handler interfaces required a class to implement ContentHandler, one to implement ErrorHandler, one to implement EntityResolver (this one is OK for all the reasons I discussed about keeping that implementation in a separate source file), and one to implement DTDHandler, if needed. Additionally, you get the joy of implementing the numerous methods in ContentHandler, even if you don't need them all to do anything.

And here comes DefaultHandler to the rescue. This class doesn't define any behavior of its own; however, it does implement ContentHandler, ErrorHandler, EntityResolver, and DTDHandler, and provides empty implementations of each method of each interface. So you can have a single class (call it, for example, MyHandlerClass) that extends DefaultHandler. This class only needs to override methods it needs to perform action in. You might implement startElement( ), characters( ), endElement( ), and fatalError( ), for example. In any combination of implemented methods, though, you'll save tons of lines of code for methods you don't need to provide action for, and make your code a lot clearer too. Then, the argument to setErrorHandler( ), setContentHandler( ), and setDTDHandler( ) would be the same instance of this MyHandlerClass. Theoretically, you could pass the instance to setEntityResolver( ) as well, although (for about the fourth time!) I discourage mixing the resolveEntity( ) method in with these other interface methods.

Previous | Home | Next
4. Advanced SAX | Book Index | 4.3. Filters and Writers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Filters and Writers (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

4.3. Filters and Writers

At this point, I want to diverge from the beaten path. So far, I've detailed everything that's in a "standard" SAX application, from the reader to the callbacks to the handlers. However, there are a lot of additional features in SAX that can really turn you into a power developer, and take you beyond the confines of "standard" SAX. In this section, I'll introduce you to two of these: SAX filters and writers. Using classes both in the standard SAX distribution and available separately from the SAX web site (http://www.megginson.com/SAX), you can add some fairly advanced behavior to your SAX applications. This will also get you in the mindset of using SAX as a pipeline of events, rather than a single layer of processing. I'll explain this concept in more detail, but suffice it to say that it really is the key to writing efficient and modular SAX code.

4.3.1. XMLFilters

First on the list is a class that comes in the basic SAX download from David Megginson's site, and should be included with any parser distribution supporting SAX 2.0. The class in question here is org.xml.sax.XMLFilter. This class extends the XMLReader interface, and adds two new methods to that class:

public void setParent(XMLReader parent);

public XMLReader getParent( );

It might not seem like there is much to say here; what's the big deal, right? Well, by allowing a hierarchy of XMLReaders through this filtering mechanism, you can build up a processing chain, or pipeline, of events. To understand what I mean by a pipeline, here's the normal flow of a SAX parse:

  • Events in an XML document are passed to the SAX reader.

  • The SAX reader and registered handlers pass events and data to an application.

What developers started realizing, though, is that it is simple to insert one or more additional links into this chain:

  • Events in an XML document are passed to the SAX reader.

  • The SAX reader performs some processing and passes information to another SAX reader.

  • Repeat until all SAX processing is done.

  • Finally, the SAX reader and registered handlers pass events and data to an application.

It's the middle steps that introduce a pipeline, where one reader that performed specific processing passes its information on to another reader, repeatedly, instead of having to lump all code into one reader. When this pipeline is set up with multiple readers, modular and efficient programming results. And that's what the XMLFilter class allows for: chaining of XMLReader implementations through filtering. Enhancing this even further is the class org.xml.sax.helpers.XMLFilterImpl , which provides a helpful implementation of XMLFilter. It is the convergence of an XMLFilter and the DefaultHandler class I showed you in the last section; the XMLFilterImpl class implements XMLFilter, ContentHandler, ErrorHandler, EntityResolver, and DTDHandler, providing pass-through versions of each method of each handler. In other words, it sets up a pipeline for all SAX events, allowing your code to override any methods that need to insert processing into the pipeline.

Let's use one of these filters. Example 4-5 is a working, ready-to-use filter. You're past the basics, so we will move through this rapidly.

Example 4-5. NamespaceFilter class

package javaxml2;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;

public class NamespaceFilter extends XMLFilterImpl {

    /** The old URI, to replace */
    private String oldURI;

    /** The new URI, to replace the old URI with */
    private String newURI;

    public NamespaceFilter(XMLReader reader,
                           String oldURI, String newURI) {
        super(reader);
        this.oldURI = oldURI;
        this.newURI = newURI;
    }

    public void startPrefixMapping(String prefix, String uri)
        throws SAXException {

        // Change URI, if needed
        if (uri.equals(oldURI)) {
            super.startPrefixMapping(prefix, newURI);
        } else {
            super.startPrefixMapping(prefix, uri);
        }
    }

    public void startElement(String uri, String localName,
                             String qName, Attributes attributes)
        throws SAXException {

        // Change URI, if needed
        if (uri.equals(oldURI)) {
            super.startElement(newURI, localName, qName, attributes);
        } else {
            super.startElement(uri, localName, qName, attributes);
        }
    }

    public void endElement(String uri, String localName, String qName)
        throws SAXException {

        // Change URI, if needed
        if (uri.equals(oldURI)) {
            super.endElement(newURI, localName, qName);
        } else {
            super.endElement(uri, localName, qName);
        }
    }
}

I start out by extending XMLFilterImpl, so I don't have to worry about any events that I don't explicitly need to change; the XMLFilterImpl class takes care of them by passing on all events unchanged unless a method is overridden. I can get down to the business of what I want the filter to do; in this case, that's changing a namespace URI from one to another. If this task seems trivial, don't underestimate its usefulness. Many times in the last several years, the URI of a namespace for a specification (such as XML Schema or XSLT) has changed. Rather than having to hand-edit all of my XML documents or write code for XML that I receive, this NamespaceFilter takes care of the problem for me.

Passing an XMLReader instance to the constructor sets that reader as its parent, so the parent reader receives any events passed on from the filter (which is all events, by virtue of the XMLFilterImpl class, unless the NamespaceFilter class overrides that behavior). By supplying two URIs, the original and the URI to replace it with, you set this filter up. The three overridden methods handle any needed interchanging of that URI. Once you have a filter like this in place, you supply a reader to it, and then operate upon the filter, not the reader. Going back to contents.xml and SAXTreeViewer, suppose that O'Reilly has informed me that my book's online URL is no longer http://www.oreilly.com/javaxml2, but http://www.oreilly.com/catalog/javaxml2. Rather than editing all my XML samples and uploading them, I can just use the NamespaceFilter class:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        NamespaceFilter filter =
            new NamespaceFilter(reader,
                "http://www.oreilly.com/javaxml2",
                "http://www.oreilly.com/catalog/javaxml2");
        ContentHandler jTreeContentHandler =
            new JTreeContentHandler(treeModel, base, reader);
        ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

        // Register content handler
        filter.setContentHandler(jTreeContentHandler);

        // Register error handler
        filter.setErrorHandler(jTreeErrorHandler);

        // Register entity resolver
        filter.setEntityResolver(new SimpleEntityResolver( ));

        // Parse
        InputSource inputSource =
            new InputSource(xmlURI);
        filter.parse(inputSource);
    }

Notice, as I said, that all operation occurs upon the filter, not the reader instance. With this filtering in place, you can compile both source files (NamespaceFilter.java and SAXTreeViewer.java), and run the viewer on the contents.xml file. You'll see that the O'Reilly namespace URI for my book is changed in every occurrence, shown in Figure 4-2.

Figure 4-2. SAXTreeViewer on contents.xml with NamespaceFilter in place

Of course, you can chain these filters together as well, and use them as standard libraries. When I'm dealing with older XML documents, I often create several of these with old XSL and XML Schema URIs and put them in place so I don't have to worry about incorrect URIs:

        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        NamespaceFilter xslFilter =
            new NamespaceFilter(reader,
                "http://www.w3.org/TR/XSL",
                "http://www.w3.org/1999/XSL/Transform");
        NamespaceFilter xsdFilter =
            new NamespaceFilter(xslFilter,
                "http://www.w3.org/TR/XMLSchema",
                "http://www.w3.org/2001/XMLSchema");

Here, I'm building a longer pipeline to ensure that no old namespace URIs sneak by and cause my applications any trouble. Be careful not to build too long a pipeline; each new link in the chain adds some processing time. All the same, this is a great way to build reusable components for SAX.

4.3.2. XMLWriter

Now that you understand how filters work in SAX, I want to introduce you to a specific filter, XMLWriter . This class, as well as a subclass of it, DataWriter , can be downloaded from David Megginson's SAX site at http://www.megginson.com/SAX. XMLWriter extends XMLFilterImpl, and DataWriter extends XMLWriter. Both of these filter classes are used to output XML, which may seem a bit at odds with what you've learned so far about SAX. However, just as you could insert statements that output to Java Writers in SAX callbacks, so can this class. I'm not going to spend a lot of time on this class, because it's not really the way you want to be outputting XML in the general sense; it's much better to use DOM, JDOM, or another XML API if you want mutability. However, the XMLWriter class offers a valuable way to inspect what's going on in a SAX pipeline. By inserting it between other filters and readers in your pipeline, it can be used to output a snapshot of your data at whatever point it resides in your processing chain. For example, in the case where I'm changing namespace URIs, it might be that you want to actually store the XML document with the new namespace URI (be it a modified O'Reilly URI, a updated XSL one, or the XML Schema one) for later use. This becomes a piece of cake by using the XMLWriter class. Since you've already got SAXTreeViewer using the NamespaceFilter, I'll use that as an example. First, add import statements for java.io.Writer (for output), and the com.megginson.sax.XMLWriter class. Once that's in place, you'll need to insert an instance of XMLWriter between the NamespaceFilter and the XMLReader instances; this means output will occur after namespaces have been changed but before the visual events occur. Change your code as shown here:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        XMLWriter writer =
            new XMLWriter(reader, new FileWriter("snapshot.xml"));
        NamespaceFilter filter =
            new NamespaceFilter(writer,
                "http://www.oreilly.com/javaxml2",
                "http://www.oreilly.com/catalog/javaxml2");
        ContentHandler jTreeContentHandler =
            new JTreeContentHandler(treeModel, base, reader);
        ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

        // Register content handler
        filter.setContentHandler(jTreeContentHandler);

        // Register error handler
        filter.setErrorHandler(jTreeErrorHandler);

        // Register entity resolver
        filter.setEntityResolver(new SimpleEntityResolver( ));

        // Parse
        InputSource inputSource =
            new InputSource(xmlURI);
        filter.parse(inputSource);
    }

Be sure you set the parent of the NamespaceFilter instance to be the XMLWriter, not the XMLReader. Otherwise, no output will actually occur. Once you've got these changes compiled in, run the example. You should get a snapshot.xml file created in the directory you're running the example from; an excerpt from that document is shown here:

<?xml version="1.0" standalone="yes"?>

<book xmlns="http://www.oreilly.com/catalog/javaxml2">
  <title ora:series="Java"
         xmlns:ora="http://www.oreilly.com">Java and XML</title>

  <contents>
    <chapter title="Introduction" number="1">
      <topic name="XML Matters"></topic>
      <topic name="What's Important"></topic>
      <topic name="The Essentials"></topic>
      <topic name="What's Next?"></topic>
    </chapter>
    <chapter title="Nuts and Bolts" number="2">
      <topic name="The Basics"></topic>
      <topic name="Constraints"></topic>
      <topic name="Transformations"></topic>
      <topic name="And More..."></topic>
      <topic name="What's Next?"></topic>
    </chapter>
    <!-- Other content... -->

  </contents>
</book>

Notice that the namespace, as changed by NamespaceFilter, is modified here. Snapshots like this, created by XMLWriter instances, can be great tools for debugging and logging of SAX events.

Both XMLWriter and DataWriter offer a lot more in terms of methods to output XML, both in full and in part, and you should check out the Javadoc included with the downloaded package. I do not encourage you to use these classes for general output. In my experience, they are most useful in the case demonstrated here.

Previous | Home | Next
4.2. More Handlers | Book Index | 4.4. Even More Handlers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Even More Handlers (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

4.4. Even More Handlers

Now I want to show you two more handler classes that SAX offers. Both of these interfaces are no longer part of the core SAX distribution, and are located in the org.xml.sax.ext package to indicate they are extensions to SAX. However, most parsers (such as Apache Xerces) include these two classes for use. Check your vendor documentation, and if you don't have these classes, you can download them from the SAX web site. I warn you that not all SAX drivers support these extensions, so if your vendor doesn't include them, you may want to find out why, and see if an upcoming version of the vendor's software will support the SAX extensions.

4.4.1. LexicalHandler

The first of these two handlers is the most useful: org.xml.sax.ext.LexicalHandler . This handler provides methods that can receive notification of several lexical events such as comments, entity declarations, DTD declarations, and CDATA sections. In ContentHandler, these lexical events are essentially ignored, and you just get the data and declarations without notification of when or how they were provided.

This is not really a general-use handler, as most applications don't need to know if text was in a CDATA section or not. However, if you are working with an XML editor, serializer, or other component that must know the exact format of the input document, not just its contents, the LexicalHandler can really help you out. To see this guy in action, you first need to add an import statement for org.xml.sax.ext.LexicalHandler to your SAXTreeViewer.java source file. Once that's done, you can add LexicalHandler to the implements clause in the nonpublic class JTreeContentHandler in that source file:

class JTreeContentHandler implements ContentHandler, LexicalHandler {
    // Callback implementations
}

By reusing the content handler already in this class, our lexical callbacks can operate upon the JTree for visual display of these lexical callbacks. So now you need to add implementations for all the methods defined in LexicalHandler. Those methods are as follows:

public void startDTD(String name, String publicID, String systemID)
        throws SAXException;
public void endDTD( ) throws SAXException;
public void startEntity(String name) throws SAXException;
public void endEntity(String name) throws SAXException;
public void startCDATA( ) throws SAXException;
public void endCDATA( ) throws SAXException;
public void comment(char[] ch, int start, int length)
        throws SAXException;

To get started, let's look at the first lexical event that might happen in processing an XML document: the start and end of a DTD reference or declaration. That triggers the startDTD( ) and endDTD( ) callbacks, shown here:

    public void startDTD(String name, String publicID,
                         String systemID)
        throws SAXException {

        DefaultMutableTreeNode dtdReference =
            new DefaultMutableTreeNode("DTD for '" + name + "'");
        if (publicID != null) {
            DefaultMutableTreeNode publicIDNode =
                new DefaultMutableTreeNode("Public ID: '" +
                    publicID + "'");
            dtdReference.add(publicIDNode);
        }
        if (systemID != null) {
            DefaultMutableTreeNode systemIDNode =
                new DefaultMutableTreeNode("System ID: '" +
                    systemID + "'");
            dtdReference.add(systemIDNode);
        }
        current.add(dtdReference);
    }

    public void endDTD( ) throws SAXException {
        // No action needed here
    }

This adds a visual cue when a DTD is encountered, and a system ID and public ID if present. Continuing on, there are a pair of similar methods for entity references, startEntity( ) and endEntity( ). These are triggered before and after (respectively) processing entity references. You can add a visual cue for this event as well, using the code shown here:

    public void startEntity(String name) throws SAXException {
        DefaultMutableTreeNode entity =
            new DefaultMutableTreeNode("Entity: '" + name + "'");
        current.add(entity);
        current = entity;
    }

    public void endEntity(String name) throws SAXException {
        // Walk back up the tree
        current = (DefaultMutableTreeNode)current.getParent( );
    }

This ensures that the content of, for example, the OReillyCopyright entity reference is included within an "Entity" tree node. Simple enough.

Because the next lexical event is a CDATA section, and there aren't any currently in the contents.xml document, you may want to make the following change to that document (the CDATA allows the ampersand in the title element's content):

<?xml version="1.0"?>
<!DOCTYPE book SYSTEM "DTD/JavaXML.dtd">

<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>
  <title ora:series="Java"><![CDATA[Java & XML]]></title>
  <!-- Other content -->
</book>

With this change, you are ready to add code for the CDATA callbacks. Add in the following methods to the JTreeContentHandler class:

    public void startCDATA( ) throws SAXException {
        DefaultMutableTreeNode cdata =
            new DefaultMutableTreeNode("CDATA Section");
        current.add(cdata);
        current = cdata;
    }

    public void endCDATA( ) throws SAXException {
        // Walk back up the tree
        current = (DefaultMutableTreeNode)current.getParent( );
    }

This is old hat by now; the title element's content now appears as the child of a CDATA node. And with that, only one method is left, that which receives comment notification:

    public void comment(char[] ch, int start, int length)
        throws SAXException {

        String comment = new String(ch, start, length);
        DefaultMutableTreeNode commentNode =
            new DefaultMutableTreeNode("Comment: '" + comment + "'");
        current.add(commentNode);
    }

This method behaves just like the characters( ) and ignorableWhitespace( ) methods. Keep in mind that only the text of the comment is reported to this method, not the surrounding <! -- and -- > delimiters. With these changes in place, you can compile the example program and run it. You should get output similar to that shown in Figure 4-3.

Figure 4-3. Output with LexicalHandler implementation in place

You'll notice one oddity, though: an entity named [dtd]. This occurs anytime a DOCTYPE declaration is in place, and can be removed (you probably don't want it present) with a simple clause in the startEntity( ) and endEntity( ) methods:

    public void startEntity(String name) throws SAXException {
      if (!name.equals("[dtd]")) {
            DefaultMutableTreeNode entity =
                new DefaultMutableTreeNode("Entity: '" + name + "'");
            current.add(entity);
            current = entity;
        }
    }

    public void endEntity(String name) throws SAXException {
        if (!name.equals("[dtd]")) {
            // Walk back up the tree
            current = (DefaultMutableTreeNode)current.getParent( );
        }
    }

This clause removes the offending entity. That's really about all that there is to say about LexicalHandler. Although I've filed it under advanced SAX, it's pretty straightforward.

4.4.2. DeclHandler

The last handler to deal with is the DeclHandler . This interface defines methods that receive notification of specific events within a DTD, such as element and attribute declarations. This is another item only good for very specific cases; again, XML editors and components that must know the exact lexical structure of documents and their DTDs come to mind. I'm not going to show you an example of using the DeclHandler; at this point you know more than you'll probably ever need to about handling callback methods. Instead, I'll just give you a look at the interface, shown in Example 4-6.

Example 4-6. The DeclHandler interface

package org.xml.sax.ext;

import org.xml.sax.SAXException;

public interface DeclHandler {

    public void attributeDecl(String eltName, String attName,
                              String type, String defaultValue,
                              String value)
        throws SAXException;

    public void elementDecl(String name, String model)
        throws SAXException;

    public void externalEntityDecl(String name, String publicID,
                                   String systemID)
        throws SAXException;

    public void internalEntityDecl(String name, String value)
        throws SAXException;
}

This example is fairly self-explanatory. The first two methods handle the <!ELEMENT> and <!ATTLIST> constructs. The third, externalEntityDecl( ), reports entity declarations (through <!ENTITY>) that refer to external resources. The final method, internalEntityDecl( ), reports entities defined inline. That's all there is to it.

And with that, I've given you everything that there is to know about SAX. Well, that's probably an exaggeration, but you certainly have plenty of tools to start you on your way. Now you just need to get coding to build up your own set of tools and tricks. Before closing the book on SAX, though, I want to cover a few common mistakes in dealing with SAX.

Previous | Home | Next
4.3. Filters and Writers | Book Index | 4.5. Gotcha!
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Gotcha! (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

4.5. Gotcha!

As you get into the more advanced features of SAX, you certainly don't reduce the number of problems you can get yourself into. However, these problems often become more subtle, which makes for some tricky bugs to track down. I'll point out a few of these common problems.

4.5.1. Return Values from an EntityResolver

As I mentioned in the section on EntityResolvers, you should always ensure that you return null as a starting point for resolveEntity( ) method implementations. Luckily, Java ensures that you return something from the method, but I've often seen code like this:

    public InputSource resolveEntity(String publicID, String systemID)
        throws IOException, SAXException {

        InputSource inputSource = new InputSource( );

        // Handle references to online version of copyright.xml
        if (systemID.equals(
            "http://www.newInstance.com/javaxml2/copyright.xml")) {
            inputSource.setSystemId(
                "file:///c:/javaxml2/ch04/xml/copyright.xml");
        }

        // In the default case, return null
        return inputSource;
    }

As you can see, an InputSource is created initially and then the system ID is set on that source. The problem here is that if no if blocks are entered, an InputSource with no system or public ID, as well as no specified Reader or InputStream, is returned. This can lead to unpredictable results; in some parsers, things continue with no problems. In other parsers, though, returning an empty InputSource results in entities being ignored, or in exceptions being thrown. In other words, return null at the end of every resolveEntity( ) implementation, and you won't have to worry about these details.

4.5.2. DTDHandler and Validation

I've described setting properties and features in this chapter, their affect on validation, and also the DTDHandler interface. In all that discussion of DTDs and validation, it's possible you got a few things mixed up; I want to be clear that the DTDHandler interface has nothing at all to do with validation. I've seen many developers register a DTDHandler and wonder why validation isn't occurring. However, DTDHandler doesn't do anything but provide notification of notation and unparsed entity declarations! Probably not what the developer expected. Remember that it's a property that sets validation, not a handler instance:

reader.setFeature("http://xml.org/sax/features/validation", true);

Anything less than this (short of a parser validating by default) won't get you validation, and probably won't make you very happy.

4.5.3. Parsing on the Reader Instead of the Filter

I've talked about pipelines in SAX in this chapter, and hopefully you got an idea of how useful they could be. However, there's an error I see among filter beginners time and time again, and it's a frustrating one to deal with. The problem is setting up the pipeline chain incorrectly: this occurs when each filter does not set the preceding filter as its parent, ending in an XMLReader instance. Check out this code fragment:

    public void buildTree(DefaultTreeModel treeModel,
                          DefaultMutableTreeNode base, String xmlURI)
        throws IOException, SAXException {

        // Create instances needed for parsing
        XMLReader reader =
            XMLReaderFactory.createXMLReader(vendorParserClass);
        XMLWriter writer =
            new XMLWriter(reader, new FileWriter("snapshot.xml"));
        NamespaceFilter filter =
            new NamespaceFilter(reader,
                "http://www.oreilly.com/javaxml2",
                "http://www.oreilly.com/catalog/javaxml2");
        ContentHandler jTreeContentHandler =
            new JTreeContentHandler(treeModel, base, reader);
        ErrorHandler jTreeErrorHandler = new JTreeErrorHandler( );

        // Register content handler
        reader.setContentHandler(jTreeContentHandler);

        // Register error handler
        reader.setErrorHandler(jTreeErrorHandler);

        // Register entity resolver
        reader.setEntityResolver(new SimpleEntityResolver( ));

        // Parse
        InputSource inputSource =
            new InputSource(xmlURI);
        reader.parse(inputSource);
    }

See anything wrong? Parsing is occurring on the XMLReader instance, not at the end of the pipeline chain. In addition, the NamespaceFilter instance sets its parent to the XMLReader, instead of the XMLWriter instance that should precede it in the chain. These errors are not obvious, and will throw your intended pipeline into chaos. In this example, no filtering will occur at all, because parsing occurs on the reader, not the filters. If you correct that error, you still won't get output, as the writer is left out of the pipeline through improper setting of the NamespaceFilter's parent. Setting the parent properly sets you up, though, and you'll finally get the behavior you expected in the first place. Be very careful with parentage and parsing when handling SAX pipelines.

Previous | Home | Next
4.4. Even More Handlers | Book Index | 4.6. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

4.6. What's Next?

That's plenty of information on the Simple API for SAX. Although there is certainly more to dig into, the information in this chapter and the last should have you ready for almost anything you'll run into. Of course, SAX isn't the only API for working with XML; to be a true XML expert you'll need to master DOM, JDOM, JAXP, and more. I'll start you on the next API in this laundry list, the Document Object Model (DOM), in the next chapter.

To introduce DOM, I'll start with the basics, much as the last chapter gave you a solid start on SAX. You'll find out about tree APIs and how DOM is significantly different from SAX, and see the DOM core classes. I'll show you a sample application that serializes DOM trees, and soon you'll be writing your own DOM code.

Previous | Home | Next
4.5. Gotcha! | Book Index | 5. DOM
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DOM (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 5. DOM

Contents:

The Document Object Model
Serialization
Mutability
Gotcha!
What's Next?

In the previous chapters, I've talked about Java and XML in the general sense, but I have described only SAX in depth. As you may be aware, SAX is just one of several APIs that allow XML work to be done within Java. This chapter and the next will widen your API knowledge as I introduce the Document Object Model, commonly called the DOM. This API is quite a bit different from SAX, and complements the Simple API for XML in many ways. You'll need both, as well as the other APIs and tools in the rest of this book, to be a competent XML developer.

Because DOM is fundamentally different from SAX, I'll spend a good bit of time discussing the concepts behind DOM, and why it might be used instead of SAX for certain applications. Selecting any XML API involves tradeoffs, and choosing between DOM and SAX is certainly no exception. I'll move on to possibly the most important topic: code. I'll introduce you to a utility class that serializes DOM trees, something that the DOM API itself doesn't currently supply. This will provide a pretty good look at the DOM structure and related classes, and get you ready for some more advanced DOM work. Finally, I'll show you some problem areas and important aspects of DOM in the "Gotcha!" section.

5.1. The Document Object Model

The Document Object Model, unlike SAX, has its origins in the World Wide Web Consortium (W3C). Whereas SAX is public-domain software, developed through long discussions on the XML-dev mailing list, DOM is a standard just like the actual XML specification. The DOM is not designed specifically for Java, but to represent the content and model of documents across all programming languages and tools. Bindings exist for JavaScript, Java, CORBA, and other languages, allowing the DOM to be a cross-platform and cross-language specification.

In addition to being different from SAX in regard to standardization and language bindings, the DOM is organized into "levels" instead of versions. DOM Level One is an accepted recommendation, and you can view the completed specification at http://www.w3.org/TR/REC-DOM-Level-1/. Level 1 details the functionality and navigation of content within a document. A document in the DOM is not just limited to XML, but can be HTML or other content models as well! Level Two, which was finalized in November of 2000, adds upon Level 1 by supplying modules and options aimed at specific content models, such as XML, HTML, and Cascading Style Sheets (CSS). These less-generic modules begin to "fill in the blanks" left by the more general tools provided in DOM Level 1. You can view the current Level 2 Recommendation at http://www.w3.org/TR/DOM-Level-2/. Level Three is already being worked on, and should add even more facilities for specific types of documents, such as validation handlers for XML, and other features that I'll discuss in Chapter 6, "Advanced DOM".

5.1.1. Language Bindings

Using the DOM for a specific programming language requires a set of interfaces and classes that define and implement the DOM itself. Because the methods involved are not outlined specifically in the DOM specification, and instead focus on the model of a document, language bindings must be developed to represent the conceptual structure of the DOM for its use in Java or any other language. These language bindings then serve as APIs for you to manipulate documents in the fashion outlined in the DOM specification.

I am obviously concerned with the Java language binding in this book. The latest Java bindings, the DOM Level 2 Java bindings, can be downloaded from http://www.w3.org/TR/DOM-Level-2/java-binding.html. The classes you should be able to add to your classpath are all in the org.w3c.dom package (and its subpackages). However, before downloading these yourself, you should check the XML parser and XSLT processor you purchased or downloaded; like the SAX packages, the DOM packages are often included with these products. This also ensures a correct match between your parser, processor, and the version of DOM that is supported.

Most XSLT processors do not handle the task of generating a DOM input themselves, but instead rely on an XML parser that is capable of generating a DOM tree. This maintains the loose coupling between parser and processor, letting one or the other be substituted with comparable products. As Apache Xalan, by default, uses Apache Xerces for XML parsing and DOM generation, it is the level of support for DOM that Xerces provides that is of interest. The same would be true if you were using Oracle's XSLT and XML processor and parser.[6]

[6]I don't want to imply that you cannot use one vendor's parser and another vendor's processor. In most of these cases, it's possible to specify a different parser for use. However, the default is always going to be the use of the vendor's software across the board.

5.1.2. The Basics

In addition to fundamentals about the DOM specification, I want to give you a bit of information about the DOM programming structure itself. At the core of DOM is a tree model. Remember that SAX gave you a piece-by-piece view of an XML document, reporting each event in the parsing lifecycle as it happened. DOM is in many ways the converse of this, supplying a complete in-memory representation of the document. The document is supplied to you in a tree format, and all of this is built upon the DOM org.w3c.dom.Node interface. Deriving from this interface, DOM provides several XML-specific interfaces, like Element, Document, Attr, and Text. So, in a typical XML document, you might get a structure that looks like Figure 5-1.

Figure 5-1. DOM structure representing XML

A tree model is followed in every sense. This is particularly notable in the case of the Element nodes that have textual values (as in the Title element). Instead of the textual value of the node being available through the Element node (through, for example, a getText( ) method), there is a child node of type Text. So you would get the child (or children) and the value of the element from the Text node itself. While this might seem a little odd, it does preserve a very strict tree model in DOM, and allows tasks like walking the tree to be very simple algorithms, without a lot of special cases. Because of this model, all DOM structures can be treated either as their generic type, Node, or as their specific type (Element, Attr, etc.). Many of the navigation methods, like getParent( ) and getChildren( ), are on that basic Node interface, so you can walk up and down the tree without worrying about the specific structure type.

Another facet of DOM to be aware of is that, like SAX, it defines its own list structures. You'll need to use the NodeList and NamedNodeMap classes when working with DOM, rather than Java collections. Depending on your point of view, this isn't a positive or negative, just a fact of life. Figure 5-2 shows a simple UML-style model of the DOM core interfaces and classes, which you can refer to throughout the rest of the chapter.

Figure 5-2. UML model of core DOM classes and interfaces

5.1.3. Why Not SAX?

As a final conceptual note before getting into the code, newbies to XML may be wondering why they can't just use SAX for dealing with XML. But sometimes using SAX is like taking a hammer to a scratch on a wall; it's just not the right tool for the job. I discuss a few issues with SAX that make it less than ideal in certain situations.

5.1.3.1. SAX is sequential

The sequential model that SAX provides does not allow for random access to an XML document. In other words, in SAX you get information about the XML document as the parser does, and lose that information when the parser does. When the second element in a document comes along, it cannot access information in the fourth element, because that fourth element hasn't been parsed yet. When the fourth element does comes along, it can't "look back" on that second element. Certainly, you have every right to save the information encountered as the process moves along; coding all these special cases can be very tricky, though. The other, more extreme option is to build an in-memory representation of the XML document. We will see in a moment that a DOM parser does exactly that, so performing the same task in SAX would be pointless, and probably slower and more difficult.

5.1.3.2. SAX siblings

Moving laterally between elements is also difficult with the SAX model. The access provided in SAX is largely hierarchical, as well as sequential. You are going to reach leaf nodes of the first element, then move back up the tree, then down again to leaf nodes of the second element, and so on. At no point is there any clear indication of what "level" of the hierarchy you are at. Although this can be implemented with some clever counters, it is not what SAX is designed for. There is no concept of a sibling element, or of the next element at the same level, or of which elements are nested within which other elements.

The problem with this lack of information is that an XSLT processor (refer to Chapter 2, "Nuts and Bolts") must be able to determine the siblings of an element, and more importantly, the children of an element. Consider the following code snippet in an XSL template:

<xsl:template match="parentElement">
  <!-- Add content to the output tree -->
  <xsl:apply-templates select="childElementOne|childElementTwo" />
</xsl:template>

Here, templates are applied via the xsl:apply-templates construct, but they are being applied to a specific node set that matches the given XPath expression. In this example, the template should be applied only to the elements childElementOne or childElementTwo (separated by the XPath OR operator, the pipe). In addition, because a relative path is used, these must be direct children of the element parentElement. Determining and locating these nodes with a SAX representation of an XML document would be extremely difficult. With an in-memory, hierarchical representation of the XML document, locating these nodes is trivial, a primary reason why the DOM approach is heavily used for input into XSLT processors.

5.1.3.3. Why use SAX at all?

All these discussions about the "shortcomings" of SAX may have you wondering why one would ever choose to use SAX at all. But these shortcomings are all in regard to a specific application of XML data, in this case processing it through XSL, or using random access for any other purpose. In fact, all of these "problems" with using SAX are the exact reason you would choose to use SAX.

Imagine parsing a table of contents represented in XML for an issue of National Geographic. This document could easily be 500 lines in length, more if there is a lot of content within the issue. Imagine an XML index for an O'Reilly book: hundreds of words, with page numbers, cross-references, and more. And these are all fairly small, concise applications of XML. As an XML document grows in size, so does the in-memory representation when represented by a DOM tree. Imagine (yes, keep imagining) an XML document so large and with so many nestings that the representation of it using the DOM begins to affect the performance of your application. And now imagine that the same results could be obtained by parsing the input document sequentially using SAX, and would only require one-tenth, or one-hundredth, of your system's resources to accomplish the task.

Just as in Java there are many ways to do the same job, there are many ways to obtain the data in an XML document. In some scenarios, SAX is easily the better choice for quick, less-intensive parsing and processing. In others, the DOM provides an easy-to-use, clean interface to data in a desirable format. You, the developer, must always analyze your application and its purpose to make the correct decision as to which method to use, or how to use both in concert. As always, the power to make good or bad decisions lies in your knowledge of the alternatives. Keeping that in mind, it's time to look at the DOM in action.

Previous | Home | Next
4.6. What's Next? | Book Index | 5.2. Serialization
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Serialization (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

5.2. Serialization

One of the most common questions about using DOM is, "I have a DOM tree; how do I write it out to a file?" This question is asked so often because DOM Levels 1 and 2 do not provide a standard means of serialization for DOM trees. While this is a bit of a shortcoming of the API, it provides a great example in using DOM (and as you'll see in the next chapter, DOM Level 3 seeks to correct this problem). In this section, to familiarize you with the DOM, I'm going to walk you through a class that takes a DOM tree as input, and serializes that tree to a supplied output.

5.2.1. Getting a DOM Parser

Before I talk about outputting a DOM tree, I will give you information on getting a DOM tree in the first place. For the sake of example, all that the code in this chapter does is read in a file, create a DOM tree, and then write that DOM tree back out to another file. However, this still gives you a good start on DOM and prepares you for some more advanced topics in the next chapter.

As a result, there are two Java source files of interest in this chapter. The first is the serializer itself, which is called (not surprisingly) DOMSerializer.java. The second, which I'll start on now, is SerializerTest.java. This class takes in a filename for the XML document to read and a filename for the document to serialize out to. Additionally, it demonstrates how to take in a file, parse it, and obtain the resultant DOM tree object, represented by the org.w3c.dom.Document class. Go ahead and download this class from the book's web site, or enter in the code as shown in Example 5-1, for the SerializerTest class.

Example 5-1. The SerializerTest class

package javaxml2;

import java.io.File;
import org.w3c.dom.Document;

// Parser import
import org.apache.xerces.parsers.DOMParser;

public class SerializerTest {

    public void test(String xmlDocument, String outputFilename)
        throws Exception {

        File outputFile = new File(outputFilename);
        DOMParser parser = new DOMParser( );

        // Get the DOM tree as a Document object

        // Serialize
    }

    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println(
                "Usage: java javaxml2.SerializerTest " +
                "[XML document to read] " +
                "[filename to write out to]");
            System.exit(0);
        }

        try {
            SerializerTest tester = new SerializerTest( );
            tester.test(args[0], args[1]);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

This example obviously has a couple of pieces missing, represented by the two comments in the test( ) method. I'll supply those in the next two sections, first explaining how to get a DOM tree object, and then detailing the DOMSerializer class itself.

5.2.2. DOM Parser Output

Remember that in SAX, the focus of interest in the parser was the lifecycle of the process, as all the callback methods provided us "hooks" into the data as it was being parsed. In the DOM, the focus of interest lies in the output from the parsing process. Until the entire document is parsed and added into the output tree structure, the data is not in a usable state. The output of a parse intended for use with the DOM interface is an org.w3c.dom.Document object. This object acts as a "handle" to the tree your XML data is in, and in terms of the element hierarchy I've discussed, it is equivalent to one level above the root element in your XML document. In other words, it "owns" each and every element in the XML document input.

Because the DOM standard focuses on manipulating data, there is a variety of mechanisms used to obtain the Document object after a parse. In many implementations, such as older versions of the IBM XML4J parser, the parse( ) method returned the Document object. The code to use such an implementation of a DOM parser would look like this:

File outputFile = new File(outputFilename);
DOMParser parser = new DOMParser( );
Document doc = parser.parse(xmlDocument);

Most newer parsers, such as Apache Xerces, do not follow this methodology. In order to maintain a standard interface across both SAX and DOM parsers, the parse( ) method in these parsers returns void, as the SAX example of using the parse( ) method did. This change allows an application to use a DOM parser class and a SAX parser class interchangeably; however, it requires an additional method to obtain the Document object result from the XML parsing. In Apache Xerces, this method is named getDocument( ). Using this type of parser (as I do in the example), you can add the following example to your test( ) method to obtain the resulting DOM tree from parsing the supplied input file:

    public void test(String xmlDocument, String outputFilename)
        throws Exception {

        File outputFile = new File(outputFilename);
        DOMParser parser = new DOMParser( );

        // Get the DOM tree as a Document object
        parser.parse(xmlDocument);
        Document doc = parser.getDocument( );

        // Serialize
    }

This of course assumes you are using Xerces, as the import statement at the beginning of the source file indicates:

import org.apache.xerces.parsers.DOMParser;

If you are using a different parser, you'll need to change this import to your vendor's DOM parser class. Then consult your vendor's documentation to determine which of the parse( ) mechanisms you need to employ to get the DOM result of your parse. In Chapter 7, "JDOM", I'll look at Sun's JAXP API and other ways to standardize a means of accessing a DOM tree from any parser implementation. Although there is some variance in getting this result, all the uses of this result that we look at are standard across the DOM specification, so you should not have to worry about any other implementation curveballs in the rest of this chapter.

5.2.3. DOMSerializer

I've been throwing the term serialization around quite a bit, and should probably make sure you know what I mean. When I say serialization, I simply mean outputting the XML. This could be a file (using a Java File), an OutputStream, or a Writer. There are certainly more output forms available in Java, but these three cover most of the bases (in fact, the latter two do, as a File can be easily converted to a Writer, but accepting a File is a nice convenience feature). In this case, the serialization taking place is in an XML format; the DOM tree is converted back to a well-formed XML document in a textual format. It's important to note that the XML format is used, as you could easily code serializers to write HTML, WML, XHTML, or any other format. In fact, Apache Xerces provides these various classes, and I'll touch on them briefly at the end of this chapter.

5.2.3.1. Getting started

To get you past the preliminaries, Example 5-2 is the skeleton for the DOMSerializer class. It imports all the needed classes to get the code going, and defines the different entry points (for a File, OutputStream, and Writer) to the class. Two of these three methods simply defer to the third (with a little I/O magic). The example also sets up some member variables for the indentation to use, the line separator, and methods to modify those properties.

Example 5-2. The DOMSerializer skeleton

package javaxml2;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DOMSerializer {

    /** Indentation to use */
    private String indent;

    /** Line separator to use */
    private String lineSeparator;

    public DOMSerializer( ) {
        indent = "";
        lineSeparator = "\n";
    }

    public void setLineSeparator(String lineSeparator) {
        this.lineSeparator = lineSeparator;
    }

    public void serialize(Document doc, OutputStream out)
        throws IOException {

        Writer writer = new OutputStreamWriter(out);
        serialize(doc, writer);
    }

    public void serialize(Document doc, File file)
        throws IOException {

        Writer writer = new FileWriter(file);
        serialize(doc, writer);
    }

    public void serialize(Document doc, Writer writer)
        throws IOException {

        // Serialize document
    }
}

Once this code is saved into a DOMSerializer.java source file, everything ends up in the version of the serialize( ) method that takes a Writer. Nice and tidy.

5.2.3.2. Launching serialization

With the setup in place for starting serialization, it's time to define the process of working through the DOM tree. One nice facet of DOM already mentioned is that all of the specific DOM structures that represent XML (including the Document object) extend the DOM Node interface. This enables the coding of a single method that handles serialization of all DOM node types. Within that method, you can differentiate between node types, but by accepting a Node as input, it enables a very simple way of handling all DOM types. Additionally, it sets up a methodology that allows for recursion, any programmer's best friend. Add the serializeNode( ) method shown here, as well as the initial invocation of that method in the serialize( ) method (the common code point just discussed):

    public void serialize(Document doc, Writer writer)
        throws IOException {

        // Start serialization recursion with no indenting
        serializeNode(doc, writer, "");
        writer.flush( );
    }

    public void serializeNode(Node node, Writer writer,
                              String indentLevel)
        throws IOException {
    }

Additionally, an indentLevel variable is put in place; this sets us up for recursion. In other words, the serializeNode( ) method can indicate how much the node being worked with should be indented, and when recursion takes place, can add another level of indentation (using the indent member variable). Starting out (within the serialize( ) method), there is an empty String for indentation; at the next level, the default is two spaces for indentation, then four spaces at the next level, and so on. Of course, as recursive calls unravel, things head back up to no indentation. All that's left now is to handle the various node types.

5.2.3.3. Working with nodes

Once within the serializeNode( ) method, the first task is to determine what type of node has been passed in. Although you could approach this with a Java methodology, using the instanceof keyword and Java reflection, the DOM language bindings for Java make this task much simpler. The Node interface defines a helper method, getNodeType( ), which returns an integer value. This value can be compared against a set of constants (also defined within the Node interface), and the type of Node being examined can be quickly and easily determined. This also fits very naturally into the Java switch construct, which can be used to break up serialization into logical sections. The code here covers almost all DOM node types; although there are some additional node types defined (see Figure 5-2), these are the most common, and the concepts here can be applied to the less common node types as well:

    public void serializeNode(Node node, Writer writer,
                              String indentLevel)
        throws IOException {

        // Determine action based on node type
        switch (node.getNodeType( )) {
            case Node.DOCUMENT_NODE:
                break;

            case Node.ELEMENT_NODE:
                break;

            case Node.TEXT_NODE:
                break;

            case Node.CDATA_SECTION_NODE:
                break;

            case Node.COMMENT_NODE:
                break;

            case Node.PROCESSING_INSTRUCTION_NODE:
                break;

            case Node.ENTITY_REFERENCE_NODE:
                break;

            case Node.DOCUMENT_TYPE_NODE:
                break;
        }
    }

This code is fairly useless; however, it helps to see all of the DOM node types laid out here in a line, rather than mixed in with all of the code needed to perform actual serialization. I want to get to that now, though, starting with the first node passed into this method, an instance of the Document interface.

Because the Document interface is an extension of the Node interface, it can be used interchangeably with the other node types. However, it is a special case, as it contains the root element as well as the XML document's DTD and some other special information not within the XML element hierarchy. As a result, you need to extract the root element and pass that back to the serialization method (starting recursion). Additionally, the XML declaration itself is printed out:

            case Node.DOCUMENT_NODE:
                writer.write("<?xml version=\"1.0\"?>");
                writer.write(lineSeparator);

                Document doc = (Document)node;
                serializeNode(doc.getDocumentElement( ), writer, "");
                break;
WARNING: DOM Level 2 (as well as SAX 2.0) does not expose the XML declaration. This may not seem like a big deal, until you consider that the encoding of the document is included in this declaration. DOM Level 3 is expected to address this deficiency, and I'll cover that in the next chapter. Be careful not to write DOM applications that depend on this information until this feature is in place.

Since the code needs to access a Document-specific method (as opposed to one defined in the generic Node interface), the Node implementation must be cast to the Document interface. Then invoke the object's getDocumentElement( ) method to obtain the root element of the XML input document, and in turn pass that on to the serializeNode( ) method, starting the recursion and traversal of the DOM tree.

Of course, the most common task in serialization is to take a DOM Element and print out its name, attributes, and value, and then print its children. As you would suspect, all of these can be easily accomplished with DOM method calls. First you need to get the name of the XML element, which is available through the getNodeName( ) method within the Node interface. The code then needs to get the children of the current element and serialize these as well. A Node's children can be accessed through the getChildNodes( ) method, which returns an instance of a DOM NodeList. It is trivial to obtain the length of this list, and then iterate through the children calling the serialization method on each, continuing the recursion. There's also quite a bit of logic that ensures correct indentation and line feeds; these are really just formatting issues, and I won't spend time on them here. Finally, the closing bracket of the element can be output:

            case Node.ELEMENT_NODE:
                String name = node.getNodeName( );
                writer.write(indentLevel + "<" + name);
                writer.write(">");

                // recurse on each child
                NodeList children = node.getChildNodes( );
                if (children != null) {
                    if ((children.item(0) != null) &&
                        (children.item(0).getNodeType( ) ==
                        Node.ELEMENT_NODE)) {

                        writer.write(lineSeparator);
                    }
                    for (int i=0; i<children.getLength( ); i++) {
                        serializeNode(children.item(i), writer,
                            indentLevel + indent);
                    }
                    if ((children.item(0) != null) &&
                        (children.item(children.getLength( )-1)
                                .getNodeType( ) ==
                        Node.ELEMENT_NODE)) {

                        writer.write(indentLevel);
                    }
                }

                writer.write("</" + name + ">");
                writer.write(lineSeparator);
                break;

Of course, astute readers (or DOM experts) will notice that I left out something important: the element's attributes! These are the only pseudo-exception to the strict tree that DOM builds. They should be an exception, though, since an attribute is not really a child of an element; it's (sort of) lateral to it. Basically the relationship is a little muddy. In any case, the attributes of an element are available through the getAttributes( ) method on the Node interface. This method returns a NamedNodeMap, and that too can be iterated through. Each Node within this list can be polled for its name and value, and suddenly the attributes are handled! Enter the code as shown here to take care of this:

            case Node.ELEMENT_NODE:
                String name = node.getNodeName( );
                writer.write(indentLevel + "<" + name);
                NamedNodeMap attributes = node.getAttributes( );
                for (int i=0; i<attributes.getLength( ); i++) {
                    Node current = attributes.item(i);
                    writer.write(" " + current.getNodeName( ) +
                                 "=\"" + current.getNodeValue( ) +
                                 "\"");
                }
                writer.write(">");

                // recurse on each child
                NodeList children = node.getChildNodes( );
                if (children != null) {
                    if ((children.item(0) != null) &&
                        (children.item(0).getNodeType( ) ==
                        Node.ELEMENT_NODE)) {

                        writer.write(lineSeparator);
                    }
                    for (int i=0; i<children.getLength( ); i++) {
                      serializeNode(children.item(i), writer,
                            indentLevel + indent);
                    }
                    if ((children.item(0) != null) &&
                        (children.item(children.getLength( )-1)
                                .getNodeType( ) ==
                        Node.ELEMENT_NODE)) {

                        writer.write(indentLevel);
                    }
                }

                writer.write("</" + name + ">");
                writer.write(lineSeparator);
                break;

Next on the list of node types is Text nodes. Output is quite simple, as you only need to use the now-familiar getNodeValue( ) method of the DOM Node interface to get the textual data and print it out; the same is true for CDATA nodes, except that the data within a CDATA section should be enclosed within the CDATA XML semantics (surrounded by <![CDATA[ and ]]>). You can add the logic within those two cases now:

            case Node.TEXT_NODE:
                writer.write(node.getNodeValue( ));
                break;

            case Node.CDATA_SECTION_NODE:
                writer.write("<![CDATA[" +
                             node.getNodeValue( ) + "]]>");
                break;

Dealing with comments in DOM is about as simple as it gets. The getNodeValue( ) method returns the text within the <!-- and --> XML constructs. That's really all there is to it; see this code addition:

            case Node.COMMENT_NODE:
                writer.write(indentLevel + "<!-- " +
                             node.getNodeValue( ) + " -->");
                writer.write(lineSeparator);
                break;

Moving on to the next DOM node type: the DOM bindings for Java define an interface to handle processing instructions that are within the input XML document, rather obviously called ProcessingInstruction. This is useful, as these instructions do not follow the same markup model as XML elements and attributes, but are still important for applications to know about. In the table of contents XML document, there aren't any PIs present (although you could easily add some for testing).

The PI node in the DOM is a little bit of a break from what you have seen so far: to fit the syntax into the Node interface model, the getNodeValue( ) method returns all data instructions within a PI in one String. This allows quick output of the PI; however, you still need to use getNodeName( ) to get the name of the PI. If you were writing an application that received PIs from an XML document, you might prefer to use the actual ProcessingInstruction interface; although it exposes the same data, the method names (getTarget( ) and getData( )) are more in line with a PI's format. With this understanding, you can add in the code to print out any PIs in supplied XML documents:

            case Node.PROCESSING_INSTRUCTION_NODE:
                writer.write("<?" + node.getNodeName( ) +
                             " " + node.getNodeValue( ) +
                             "?>");
                writer.write(lineSeparator);
                break;

While the code to deal with PIs is perfectly workable, there is a problem. In the case that handled document nodes, all the serializer did was pull out the document element and recurse. The problem is that this approach ignores any other child nodes of the Document object, such as top-level PIs and any DOCTYPE declarations. Those node types are actually lateral to the document element (root element), and are ignored. Instead of just pulling out the document element, then, the following code serializes all child nodes on the supplied Document object:

            case Node.DOCUMENT_NODE:
                writer.write("<xml version=\"1.0\">");
                writer.write(lineSeparator);

                // recurse on each child
                NodeList nodes = node.getChildNodes( );
                if (nodes != null) {
                    for (int i=0; i<nodes.getLength( ); i++) {
                        serializeNode(nodes.item(i), writer, "");
                    }
                }
                /*
                Document doc = (Document)node;
                serializeNode(doc.getDocumentElement( ), writer, "");
                */
                break;

With this in place, the code can deal with DocumentType nodes, which represent a DOCTYPE declaration. Like PIs, a DTD declaration can be helpful in exposing external information that might be needed in processing an XML document. However, since there can be public and system IDs as well as other DTD-specific data, the code needs to cast the Node instance to the DocumentType interface to access this additional data. Then, use the helper methods to get the name of the Node, which returns the name of the element in the document that is being constrained, the public ID (if it exists), and the system ID of the DTD referenced. Using this information, the original DTD can be serialized:

            case Node.DOCUMENT_TYPE_NODE:
                DocumentType docType = (DocumentType)node;
                writer.write("<!DOCTYPE " + docType.getName( ));
                if (docType.getPublicId( ) != null)  {
                    System.out.print(" PUBLIC \"" +
                        docType.getPublicId( ) + "\" ");
                } else {
                    writer.write(" SYSTEM ");
                }
                writer.write("\"" + docType.getSystemId( ) + "\">";
                writer.write(lineSeparator);
                break;

All that's left at this point is handling entities and entity references. In this chapter, I will skim over entities and focus on entity references; more details on entities and notations are in the next chapter. For now, a reference can simply be output with the & and ; characters surrounding it:

            case Node.ENTITY_REFERENCE_NODE:
                writer.write("&" + node.getNodeName( ) + ";");
                break;

There are a few surprises that may trip you up when it comes to the output from a node such as this. The definition of how entity references should be processed within DOM allows a lot of latitude, and also relies heavily on the underlying parser's behavior. In fact, most XML parsers have expanded and processed entity references before the XML document's data ever makes its way into the DOM tree. Often, when expecting to see an entity reference within your DOM structure, you will find the text or values referenced rather than the entity reference itself. To test this for your parser, you'll want to run the SerializerTest class on the contents.xml document (which I'll cover in the next section) and see what it does with the OReillyCopyright entity reference. In Apache, this comes across as an entity reference, by the way.

And that's it! As I mentioned, there are a few other node types, but covering them isn't worth the trouble at this point; you get the idea about how DOM works. In the next chapter, I'll take you deeper than you probably ever wanted to go. For now, let's put the pieces together and see some results.

5.2.4. The Results

With the DOMSerializer class complete, all that's left is to invoke the serializer's serialize( ) method in the test class. To do this, add the following lines to the SerializerTest class:

    public void test(String xmlDocument, String outputFilename)
        throws Exception {

        File outputFile = new File(outputFilename);
        DOMParser parser = new DOMParser( );

        // Get the DOM tree as a Document object
        parser.parse(xmlDocument);
        Document doc = parser.getDocument( );

        // Serialize
        DOMSerializer serializer = new DOMSerializer( );
        serializer.serialize(doc, new File(outputFilename));
    }

This fairly simple addition completes the classes, and you can run the example on Chapter 2, "Nuts and Bolts"'s contents.xml file, as shown:

C:\javaxml2\build>java javaxml2.SerializerTest
    c:\javaxml2\ch05\xml\contents.xml
    output.xml

While you don't get any exciting output here, you can open up the newly created output.xml file and check it over for accuracy. It should contain all the information in the original XML document, with only the differences already discussed in previous sections. A portion of my output.xml is shown in Example 5-3.

Example 5-3. A portion of the output.xml serialized DOM tree

<?xml version="1.0"?>
<!DOCTYPE book SYSTEM "DTD/JavaXML.dtd">
<!--  Java and XML Contents  -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com">
  <title ora:series="Java">Java and XML</title>

  <!--  Chapter List  -->

  <contents>
    <chapter number="2" title="Nuts and Bolts">
      <topic name="The Basics"></topic>

      <topic name="Constraints"></topic>

      <topic name="Transformations"></topic>

      <topic name="And More..."></topic>

      <topic name="What's Next?"></topic>

    </chapter>

You may notice that there is quite a bit of extra whitespace in the output; that's because the serializer adds some line feeds every time writer.write(lineSeparator) appears in the code. Of course, the underlying DOM tree has some line feeds in it as well, which are reported as Text nodes. The end result in many of these cases is the double line breaks, as seen in the output.

WARNING: Let me be very clear that the DOMSerializer class shown in this chapter is for example purposes, and is not a good production solution. While you are welcome to use the class in your own applications, realize that several important options are left out, like encoding and setting advanced options for indentation, line feeds, and line wrapping. Additionally, entities are handled only in passing (complete treatment would be twice as long as this chapter already is!). Your parser probably has its own serializer class, if not multiple classes, that perform this task at least as well, if not better, than the example in this chapter. However, you now should understand what's going on under the hood in those classes. As a matter of reference, if you are using Apache Xerces, the classes to look at are in the org.apache.xml.serialize. Some particularly useful ones are the XMLSerializer, XHTMLSerializer, and HTMLSerializer. Check them out -- they offer a good solution, until DOM Level 3 comes out with a standardized one.

Previous | Home | Next
5. DOM | Book Index | 5.3. Mutability
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Mutability (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

5.3. Mutability

One glaring omission in this chapter is the topic of modifying a DOM tree. That's not an accident; working with DOM is a lot more complex than working with SAX. Rather than drowning you in information, I wanted to give a clear picture of the various node types and structures used in DOM. In the next chapter, in addition to looking at some of the finer points of DOM Levels 2 and 3, I'll address the mutability of DOM trees, and in particular how to create DOM trees. So don't panic -- help is on the way!

Previous | Home | Next
5.2. Serialization | Book Index | 5.4. Gotcha!
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Gotcha! (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

5.4. Gotcha!

As in previous chapters, I want to revisit some of the common pitfalls for new XML Java developers. In this chapter, I have focused on the Document Object Model, and this section continues that emphasis. Although some of the points made here are more informational than directly affective on your programming, they can be helpful in making design decisions about when to use DOM, and instrumental in understanding what is going on under the hood of your XML applications.

5.4.1. Memory, Performance, and Deferred DOMs

Earlier, I described the reasons to use DOM or SAX. Although I emphasized that using the DOM requires that the entire XML document be read into memory and stored in a tree structure, enough cannot be said on the subject. All too common is the scenario where a developer loads up his extensive collection of complex XML documents into an XSLT processor and begins a series of offline transformations, leaving the process to grab a bite to eat. Upon returning, he finds that his Windows machine is showing the dreaded "blue screen of death" and his Linux box is screaming about memory problems. For this developer and the hundreds like him, beware the DOM for excessively large data!

Using the DOM requires an amount of memory proportional to the size and complexity of an XML document. However, you should dig a bit further into your parser's documentation. Often, today's parsers contain a feature modeled on what it typically called a deferred DOM . A deferred DOM tries to lower the memory cost of using DOM by not reading and allocating all information needed by a DOM node until that node is requested. Until that time, the nodes in existence, but not in use, are simply nulled out. This reduces the memory overhead for large documents when only a specific portion of the document must be processed. However, realize that with this decrease in memory, there is an increase in processing. Since nodes are not in memory, and must be filled with data when requested, there is generally more lag time when a node not previously accessed is requested. It's a tradeoff. However, a deferred DOM can often help save the day when dealing with large documents.

5.4.2. Polymorphism and the Node Interface

Previously in this chapter I stressed the tree model that DOM is built upon. I also told you that the key to this was a common interface, org.w3c.dom.Node. This class provides common functionality for all DOM classes, but sometimes it provides more. For example, this class defines a method called getNodeValue( ), which returns a String. Sounds like a good idea, right? Without having to cast the Node to a specific type, you can quickly get its value. However, things get a little sticky when you consider types like Element. Remember that an Element has no textual content, but instead has children of type Text. So an Element in DOM has no value that has any meaning; the result is that you get something like #ELEMENT#. The exact value is parser-dependent, but you get the idea.

The same situation applies to other methods on the Node interface, like getNodeName( ). For Text nodes, you get #TEXT#, which doesn't help too much. So what exactly is the gotcha here? You simply need to be careful when working with different DOM types through the Node interface. You may get some unexpected results along with the convenience of the common interface.

5.4.3. DOM Parsers Throwing SAX Exceptions

In this chapter's example of using DOM, I did not explicitly list the exceptions that could result from a document parse; instead a higher-level exception was caught. This was because, as I mentioned, the process of generating a DOM tree is left up to the parser implementation, and is not always the same. However, it is typically good practice to catch the specific exceptions that can occur and react to them differently, as the type of exception gives information about the problem that occurred. Rewriting the SerializerTest class's parser invocation this way might make a surprising facet of this process surface. For Apache Xerces this could be done as follows:

    public void test(String xmlDocument, String outputFilename)
        throws Exception {

        try {
            File outputFile = new File(outputFilename);
            DOMParser parser = new DOMParser( );
            parser.parse(xmlDocument);
            Document doc = parser.getDocument( );
        } catch (IOException e) {
            System.out.println("Error reading URI: " + e.getMessage( ));
        } catch (SAXException e) {
            System.out.println("Error in parsing: " + e.getMessage( ));
        }

        // Serialize
        DOMSerializer serializer = new DOMSerializer( );
        serializer.serialize(doc, new File(outputFilename));
    }

The IOException seen here should not come as a surprise, as it signifies an error in locating the specified filename as it did in the earlier SAX examples. Something else from the SAX section might make you think something was amiss; did you notice the SAXException that can be thrown? The DOM parser throws a SAX exception? Surely I have imported the wrong set of classes! Not so; these are the right classes. Remember that it would be possible to build a tree structure of the data in an XML document yourself, using SAX, but the DOM provides an alternative. However, this does not preclude SAX from being used in that alternative! In fact, SAX provides a lightweight, fast way to parse a document; in this case, it just happens that as it is parsed, it is inserted into a DOM tree. Because no standard for the DOM creation exists, this is acceptable and not even uncommon. So don't be surprised or taken aback when you find yourself importing and catching org.xml.sax.SAXException in your DOM applications.

Previous | Home | Next
5.3. Mutability | Book Index | 5.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

5.5. What's Next?

In Chapter 6, "Advanced DOM", I'll continue being tour guide through the world of DOM, as we look at some of DOM's more advanced (and less known) features. To get rolling, I'll show you how to modify DOM trees, as well as create them. Then, it's on to the less common functionality in the DOM. For starters, the additions included in DOM Level 2 will be examined (some you've already used, and some you haven't). Next, I'll cover using the DOM HTML bindings, which will help you when dealing with DOM and web pages. Finally, I'll give you some information about changes expected in the upcoming DOM Level 3 specification. That should give you plenty of ammo to take over the world using DOM!

Previous | Home | Next
5.4. Gotcha! | Book Index | 6. Advanced DOM
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Advanced DOM (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 6. Advanced DOM

Contents:

Changes
Namespaces
DOM Level 2 Modules
DOM Level 3
Gotcha!
What's Next?

Just like in Chapter 4, "Advanced SAX ", there's nothing mystical about anything I'll cover in this chapter. The topics build upon a foundation that I set in the DOM basics from the last chapter. However, with the exception of the first section on mutation, many of these features are rarely used. While almost everything you've seen in SAX (except, perhaps, the DTDHandler and DeclHandler) will be handy, I've found many of the fringe features of DOM useful only in specific applications. For example, if you aren't doing any presentation logic, you'll probably never touch the DOM HTML bindings. The same goes for many of DOM Level 2's features; if you need them, you need them badly, and if you don't, you really don't.

In this chapter, I'll present some specific DOM topics that will be useful in your own DOM programming. I've tried to organize the chapter more like a reference than the previous chapters; if you want to find out more about the DOM Level 2 Traversal module, for example, you can simply thumb to that section. However, the code examples in this chapter do build upon each other, so you may still want to work through each section in order to get a complete picture of the current DOM model. This results in more practical code samples, rather than useless contrived ones that won't get you anywhere. So buckle up, and let's dive a little deeper into the world of DOM.

6.1. Changes

First and foremost, I want to talk about the mutability of a DOM tree. The biggest limitation when using SAX for dealing with XML is that you cannot change any of the XML structure you encounter, at least not without using filters and writers. Those aren't intended to be used for wholesale document changes anyway, so you'll need to use another API when you want to modify XML. DOM fits the bill nicely, as it provides XML creation and modification facilities.

In working with DOM, the process of creating an XML document is quite different from changing an existing one, so I'll take them one at a time. This section gives you a fairly realistic example to mull over. If you've ever been to an online auction site like eBay, you know that the most important aspects of the auction are the ability to find items, and the ability to find out about items. These functions depend on a user entering in a description of an item, and the auction using that information. The better auction sites allow users to enter in some basic information as well as actual HTML descriptions, which means the savvy user can bold, italicize, link, and add other formatting to their items' descriptions. This provides a good case for using DOM.

6.1.1. Creating a New DOM Tree

To get started, a little bit of groundwork is needed. Example 6-1 shows a simple HTML form that takes basic information about an item to be listed on an auction site. This would obviously be dressed up more for a real site, but you get the idea.

Example 6-1. HTML input form for item listing

<html>
 <head><title>Input/Update Item Listing</title></head>
 <body>
  <h1 align="center">Input/Update Item Listing</h1>
  <p align="center">
   <form method="POST" action="/javaxml2/servlet/javaxml2.UpdateItemServlet">
    Item ID (Unique Identifier): <br />
    <input name="id" type="text" maxLength="10" /><br /><br />
    Item Name: <br />
    <input name="name" type="text" maxLength="50" /><br /><br />
    Item Description: <br />
    <textarea name="description" rows="10" cols="30" wrap="wrap" ></textarea>
    <br /><br />
    <input type="reset" value="Reset Form" />&nbsp;&nbsp;
    <input type="submit" value="Add/Update Item" />
   </form>
  </p>
 </body>
</html>

Notice that the target of this form submission is a servlet. That servlet is shown in Example 6-2. The doPost( ) method reads in these input parameters and puts their values into temporary variables. At that point, the servlet checks the filesystem for a specific file that has this information stored within it.

WARNING: For the sake of clarity, I'm dealing directly with the filesystem in this servlet. However, this is generally not a good idea. Consider using the ServletContext to get access to local resources, allowing your servlet to be distributed and modified easily depending on the server and servlet engine hosting it. That sort of detail tends to muddy examples up, so I'm keeping it simple here.

If the file doesn't exist (for a new listing, it wouldn't), it creates a new DOM tree and builds up the tree structure using the values supplied. Once that's complete, the servlet uses the DOMSerializer class (from Chapter 5, "DOM") to write the DOM tree out to the file, making it available the next time this servlet is invoked. Additionally, I've coded up a doGet( ) method; this method just displays the HTML shown in Example 6-1. I'll use this later to allow modification of item listings. For now, don't worry too much about it.

Example 6-2. The UpdateItemServlet class

package javaxml2;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// DOM imports
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

// Parser import
import org.apache.xerces.dom.DOMImplementationImpl;

public class UpdateItemServlet extends HttpServlet {

    private static final String ITEMS_DIRECTORY = "/javaxml2/ch06/xml/";

    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        // Get output
        PrintWriter out = res.getWriter( );
        res.setContentType("text/html");

        // Output HTML
        out.println("<html>");
        out.println(" <head><title>Input/Update Item Listing</title></head>");
        out.println(" <body>");
        out.println("  <h1 align='center'>Input/Update Item Listing</h1>");
        out.println("  <p align='center'>");
        out.println("   <form method='POST' " +
            "action='/javaxml2/servlet/javaxml2.UpdateItemServlet'>");
        out.println("    Item ID (Unique Identifier): <br />");
        out.println("    <input name='id' type='text' maxLength='10' />" +
            "<br /><br />");
        out.println("    Item Name: <br />");
        out.println("    <input name='name' type='text' maxLength='50' />" +
            "<br /><br />");
        out.println("    Item Description: <br />");
        out.println("    <textarea name='description' rows='10' cols='30' " +
            "wrap='wrap' ></textarea><br /><br />");
        out.println("    <input type='reset' value='Reset Form' />&nbsp;&nbsp;");
        out.println("    <input type='submit' value='Add/Update Item' />");
        out.println("   </form>");
        out.println("  </p>");
        out.println(" </body>");
        out.println("</html>");

        out.close( );
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        // Get parameter values
        String id = req.getParameterValues("id")[0];
        String name = req.getParameterValues("name")[0];
        String description = req.getParameterValues("description")[0];

        // Create new DOM tree
        DOMImplementation domImpl = new DOMImplementationImpl( );
        Document doc = domImpl.createDocument(null, "item", null);
        Element root = doc.getDocumentElement( );

        // ID of item (as attribute)
        root.setAttribute("id", id);

        // Name of item
        Element nameElement = doc.createElement("name");
        Text nameText = doc.createTextNode(name);
        nameElement.appendChild(nameText);
        root.appendChild(nameElement);

        // Description of item
        Element descriptionElement = doc.createElement("description");
        Text descriptionText = doc.createTextNode(description);
        descriptionElement.appendChild(descriptionText);
        root.appendChild(descriptionElement);

        // Serialize DOM tree
        DOMSerializer serializer = new DOMSerializer( );
        serializer.serialize(doc, new File(ITEMS_DIRECTORY + "item-" + name +
            ".xml"));

        // Print confirmation
        PrintWriter out = res.getWriter( );
        res.setContentType("text/html");
        out.println("<HTML><BODY>Thank you for your submission. " +
            "Your item has been processed.</BODY></HTML>");
        out.close( );
    }

}

Go ahead and compile this class. I'll walk you through it in just a moment, but ensure that you have your environment set up to include the needed classes.

NOTE: Make sure the DOMSerializer class from the last chapter is in your classpath when compiling the UpdateItemServlet class. You'll also want to add this to the classes in your servlet engine's context. In my setup, using Tomcat, my context is called javaxml2, in a directory named javaxml2 under the webapps directory. In my WEB-INF/classes directory, there is a javaxml2 directory (for the package), and then the DOMSerializer.class and UpdateItemServlet.class files are within that directory. You should also ensure that a copy of your parser's jar file (xerces.jar in my case) is in the classpath of your engine. In Tomcat, you can simply drop a copy in Tomcat's lib directory. Finally, you'll need to ensure that Xerces, and the DOM Level 2 implementation within it, is loaded before the DOM Level 1 implementation in Tomcat's parser.jar archive. Do this by renaming parser.jar to z_parser.jar. I'll explain more about this in Chapter 10, "Web Publishing Frameworks", but for now just trust me and make the change. Then restart Tomcat and everything should work.

Once you've got your servlet in place and the servlet engine started, browse to the servlet and let the GET request your browser generates load the HTML input form. Fill this form out, as I have in Figure 6-1.

Figure 6-1. Filling out the items form

Since I'll talk in depth about the description field later, I want to show you the complete content I typed into that field. I know there's lots of markup (I went crazy on the bolding and italics!), but this will be important later on:

This is a <i>beautiful</i> <b>Sitka-topped</b> guitar with <b>Indian Rosewood</b>
back and sides. Made by luthier <a href="http://www.bourgeoisguitars.com">Dana
Bourgeois</a>, this OM has a <b>huge sound</b>.
The guitar has <i>great action</i>, a 1 3/4" nut, and all
<i>fossilized ivory</i> nut and saddle, with <i>ebony</i> end pins.
New condition, this is a <b>great guitar</b>!

Submitting this form posts its data (via a POST request) to the servlet, and the doPost( ) method takes effect. As for the actual DOM creation, it turns out to be pretty simple. First, you'll need to instantiate an instance of the org.w3c.dom.DOMImplementation class. This will be the base for all your DOM creation work. While you could certainly directly instantiate a DOM Document implementation, you would not be able to create a DocType class from it as you could from a DOMImplementation; using DOMImplementation is a better practice. Additionally, the DOMImplementation class has one more useful method, hasFeature( ). I'll cover this method in detail later, so don't worry about it for now. In the example code, I've used Xerces' implementation, org.apache.xerces.dom.DOMImplementationImpl (sort of a confusing name, isn't it?). There is currently no vendor-neutral way to handle this, although DOM Level 3 (covered at the end of this chapter) provides some possibilities for the future. JAXP, detailed in Chapter 9, "JAXP", offers some solutions, but I'll get to those later.

Once you've got an instance of DOMImplementation, though, things are pretty simple. Take a look at the relevant code again:

        // Create new DOM tree
        DOMImplementation domImpl = new DOMImplementationImpl( );
        Document doc = domImpl.createDocument(null, "item", null);
        Element root = doc.getDocumentElement( );

        // ID of item (as attribute)
        root.setAttribute("id", id);

        // Name of item
        Element nameElement = doc.createElement("name");
        Text nameText = doc.createTextNode(name);
        nameElement.appendChild(nameText);
        root.appendChild(nameElement);

        // Description of item
        Element descriptionElement = doc.createElement("description");
        Text descriptionText = doc.createTextNode(description);
        descriptionElement.appendChild(descriptionText);
        root.appendChild(descriptionElement);

        // Serialize DOM tree
        DOMSerializer serializer = new DOMSerializer( );
        serializer.serialize(doc, new File(ITEMS_DIRECTORY + "item-" + id +
            ".xml"));

First, the createDocument( ) method is used to get a new Document instance. The first argument to this method is the namespace for the document's root element. I haven't gotten to the namespace yet, so I omit one by passing in a null value. The second argument is the name of the root element itself, which is simply "item". The last argument is an instance of a DocType class, and I again pass in a null value since I have none for this document. If I did want a DocType, I could create one with the createDocType( ) method on the same class, DOMImplementation. If you're interested in that method, check out the complete DOM API coverage in Appendix A, "API Reference".

With a DOM tree to operate upon, I can retrieve the root element to work with (using getDocumentElement( ), covered in the last chapter). Once I've got that, I add an attribute with the ID of the item using setAttribute( ). I pass in the attribute name and value, and the root element is ready to go. Things begin to get simple now; each type of DOM construct can be created using the Document object as a factory. To create the "name" and "description" elements, I use the createElement( ) method, simply passing in the element name in each case. The same approach is used to create textual content for each; since an element has no content but instead has children that are Text nodes (remember this from the last chapter?), the createTextNode( ) method is the right selection. This method takes in the text for the node, which works out to be the description and item name. You might be tempted to use the createCDATASection( ) method, and wrap this text in CDATA tags. There is HTML within this element. However, that would prevent the content from being read in as a set of elements, and provide the content as a big blob of text. Later on, we'll want to deal with this as elements, so leave this as a Text node instead, using createTextNode( ) again. Once you've gotten all of these nodes created, all that's left is to link them together. Your best bet is to use appendChild( ) on each, appending the elements to the root, and the textual content of the elements to the correct parent. This is pretty self-explanatory. And finally, the whole document is passed into the DOMSerializer class from the last chapter and written out to an XML file on disk.

WARNING: I have assumed that the user is entering well-formed HTML; in other words, XHTML. In a production application you would probably run this input through JTidy (http://www.sourceforge.net/projects/jtidy) to ensure this; for this example, I'll just assume the input is XHTML.

I've provided a constant in the servlet, ITEMS_DIRECTORY, where you can specify what directory to use. The example code uses a Windows directory, and notice that the backslashes are all escaped. Don't forget this! Simply change this to the directory you want to use on your system. You can view the XML generated from the servlet by browsing to the directory you specified in this constant, and open up the XML file that should be located there. Mine looked as shown in Example 6-3.

Example 6-3. The XML generated from the UpdateItemServlet

<?xml version="1.0"?>
<item id="bourgOM">
<name>Bourgeois OM Guitar</name>
<description>This is a <i>beautiful</i> <b>Sitka-topped</b> guitar with
<b>Indian Rosewood</b> back and sides. Made by luthier
<a href="http://www.bourgeoisguitars.com">Dana Bourgeois</a>, this OM has a
<b>huge sound</b>.
The guitar has <i>great action</i>, a 1 3/4" nut, and all
<i>fossilized ivory</i> nut and saddle, with <i>ebony</i> end pins.
New condition, this is a <b>great guitar</b>!</description>
</item>

I've moved fairly quickly through this, but you should be starting to really catch your stride with DOM. Next, I want to discuss actually modifying a DOM tree that is already in existence.

6.1.2. Modifying a DOM Tree

The process of changing an existing DOM tree is slightly different from the process of creating one; in general, it involves loading the DOM from some source, traversing the tree, and then making changes. These changes are usually either to structure or content. If the change is to structure, it becomes a matter of creation again:

// Add a copyright element to the root
Element root = doc.getDocumentElement( );
Element copyright = doc.createElement("copyright");
copyright.appendChild(doc.createTextNode("Copyright O'Reilly 2001"));
root.appendChild(copyright);

This is what I just described. The process of changing existing content is a little different, although not overly complex. As an example, I will show you a modified version of the UpdateItemServlet. This version reads the supplied ID and tries to load an existing file if it exists. If so, it doesn't create a new DOM tree, but instead modifies the existing one. Since there are so many additions, I'll reprint the entire class and highlight the changes:

package javaxml2;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.xml.sax.SAXException;

// DOM imports
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

// Parser import
import org.apache.xerces.dom.DOMImplementationImpl;
import org.apache.xerces.parsers.DOMParser;

public class UpdateItemServlet extends HttpServlet {

    private static final String ITEMS_DIRECTORY = "/javaxml2/ch06/xml/";

    // doGet( ) method is unchanged

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        // Get parameter values
        String id = req.getParameterValues("id")[0];
        String name = req.getParameterValues("name")[0];
        String description = req.getParameterValues("description")[0];

        // See if this file exists
        Document doc = null;
        File xmlFile = new File(ITEMS_DIRECTORY + "item-" + id + ".xml");

        if (!xmlFile.exists( )) {
            // Create new DOM tree
            DOMImplementation domImpl = new DOMImplementationImpl( );
            doc = domImpl.createDocument(null, "item", null);
            Element root = doc.getDocumentElement( );

            // ID of item (as attribute)
            root.setAttribute("id", id);

            // Name of item
            Element nameElement = doc.createElement("name");
            Text nameText = doc.createTextNode(name);
            nameElement.appendChild(nameText);
            root.appendChild(nameElement);

            // Description of item
            Element descriptionElement = doc.createElement("description");
            Text descriptionText = doc.createText(description);
            descriptionElement.appendChild(descriptionText);
            root.appendChild(descriptionElement);
        } else {
            // Load document
            try {
                DOMParser parser = new DOMParser( );
                parser.parse(xmlFile.toURL().toString( ));
                doc = parser.getDocument( );

                Element root = doc.getDocumentElement( );

                // Name of item
                NodeList nameElements =
                    root.getElementsByTagNameNS(docNS, "name");
                Element nameElement = (Element)nameElements.item(0);
                Text nameText = (Text)nameElement.getFirstChild( );
                nameText.setData(name);

                // Description of item
                NodeList descriptionElements =
                    root.getElementsByTagNameNS(docNS, "description");
                Element descriptionElement = (Element)descriptionElements.item(0);

                // Remove and recreate description
                root.removeChild(descriptionElement);
                descriptionElement = doc.createElement("description");
                Text descriptionText = doc.createTextNode(description);
                descriptionElement.appendChild(descriptionText);
                root.appendChild(descriptionElement);
            } catch (SAXException e) {
                // Print error
                PrintWriter out = res.getWriter( );
                res.setContentType("text/html");
                out.println("<HTML><BODY>Error in reading XML: " +
                    e.getMessage( ) + ".</BODY></HTML>");
                out.close( );
                return;
            }
        }

        // Serialize DOM tree
        DOMSerializer serializer = new DOMSerializer( );
        serializer.serialize(doc, xmlFile);

        // Print confirmation
        PrintWriter out = res.getWriter( );
        res.setContentType("text/html");
        out.println("<HTML><BODY>Thank you for your submission. " +
            "Your item has been processed.</BODY></HTML>");
        out.close( );
    }
}

The changes are fairly simple, nothing to throw you for a loop. I create the File instance for the named file (using the ID supplied), and check for its existence. This tells the servlet whether the XML file representing the submitted item already exists. If not, it does everything discussed in the last section, with no changes. If the XML already exists (indicating the item has already been submitted), it is loaded and read into a DOM tree using techniques covered in the last chapter. At that point, some basic tree traversal begins.

The code grabs the root element, and then uses the getElementsByTagName( ) method to locate all elements named "name" and then all named "description." In each case, I know that only one will be found within the returned NodeList. I can access this using the item( ) method on the NodeList, and supplying "0" as the argument (the indexes are all zero-based). This effectively gives me the element desired. I could have simply gotten the children of the root through getChildren( ), and peeled off the first and second. However, using the element names is easier to document and clearer. I get the "name" element's textual content by invoking getFirstChild( ). Since I know that the "name" element has a single Text node, I can directly cast this to the appropriate type. Finally, the setData( ) method allows the code to change the existing value for a new name, which is the information the user supplied through the form.

You'll notice that I used a slightly different approach for the description of the item. Since there could conceivably be a complete document fragment within the element (remember the user could enter HTML, allowing for nested elements like "b", "a", and "img"), it's easier to just remove the existing "description" element and replace it with a new one. This avoids having to recurse through the tree and remove each child node, a time-consuming task. Once I've removed the node using the removeChild( ) method, it's simple to recreate and reappend it to the document's root element.

It's no accident that this code is hardwired to the format the XML was written out to. In fact, most DOM modification code relies on at least some understanding of the content to be dealt with. For cases when the structure or format is unknown, the DOM Level 2 traversal model is a better fit; I'll cover that a little later on in this chapter. For now, accept that knowing how the XML is structured (since this servlet created it earlier on!) is a tremendous advantage. Methods like getFirstChild( ) can be used and the result cast to a specific type, rather than needing lengthy type checking and switch blocks.

Once the creation or modification is complete, the resulting DOM tree is serialized back to XML, and the process can repeat itself. I've also had to add some error handling for SAX problems resulting from the DOM parsing, but this is also nothing new after the last chapter. As an exercise, update the doGet( ) method to read in a parameter from the URL and load the XML preferences, letting the user change them on the form. For example, the URL http://localhost:8080/javaxml2/servlet/javaxml2.UpdateItemServlet?id=bourgOM would indicate that the item with the ID "bourgOM" should be loaded for editing. This is a simple change, and one you should be ready to knock out on your own by now.

Previous | Home | Next
5.5. What's Next? | Book Index | 6.2. Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespaces (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

6.2. Namespaces

An important addition to DOM Level 2 not yet discussed is DOM's support for XML namespaces. You'll remember from Chapter 3, "SAX" and Chapter 4, "Advanced SAX ", that SAX 2.0 added namespace support, and the same is true for the second iteration of DOM. The key here is two new methods on the Node interface: getPrefix( ) and getNamespaceURI( ). Additionally, all of the creation methods have namespace-aware versions available. So, instead of calling createElement( ), you call createElementNS( ).

Overloaded?

To all of the Java programmers out there, createElementNS( ) looks pretty odd. Why not just overload createElement( ) to take in additional parameters? Well, you could if DOM was used only in Java or in languages that supported overloading. However, it's not; it's a cross-language specification, and that results in limitations with method names and backwards compatiblity when it comes to changing existing method signatures. So, DOM defined new methods with the NS suffix to identify them as namespace-aware versions. It's bad for straight Java, but good for DOM as a cross-language standard.

In each of these new namespace-aware methods, the first argument is the namespace URI, and the second is the qualified name of the element, attribute, etc. Note that I said qualified; this means that if you want to use a namespace URI of "http://www.oreilly.com" and a prefix of "ora" on an element called "copyright", you would call createElementNS("http://www.oreilly.com", "ora:copyright"). This is very important, and remembering to use that prefix will save you a lot of time down the road. Calling getPrefix( ) on that new element will correctly return "ora", though, as it should. If you want the element in the default namespace (with no prefix), just pass in the element name (the local name, in this case), and you're all set. Calling getPrefix( ) on a default-namespaced element returns null, by the way, as it does on an element not in any namespace.

WARNING: The prefix tells you very little about whether an element is a namespace. Elements with a default namespace (and no prefix) have the same return value from getPrefix( ) as elements not in any namespace. I'm hoping the next version of the specification modifies this to return an empty string ("") when the element is in the default namespace.

Rather than simply list all the new namespace-aware methods (you can find that list in Appendix A, "API Reference"), I'd rather show you some real code. In fact, converting the UpdateItemServlet's doPost( ) method to use namespaces is a perfect example:

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        // Get parameter values
        String id = req.getParameterValues("id")[0];
        String name = req.getParameterValues("name")[0];
        String description = req.getParameterValues("description")[0];

        // See if this file exists
        Document doc = null;
        File xmlFile = new File(ITEMS_DIRECTORY + "item-" + id + ".xml");
        }String docNS = "http://www.oreilly.com/javaxml2";}

        if (!xmlFile.exists( )) {
            // Create new DOM tree
            DOMImplementation domImpl = new DOMImplementationImpl( );
            doc = domImpl.createDocument(docNS, "item", null);
            Element root = doc.getDocumentElement( );

            // ID of item (as attribute)
            root.setAttribute("id", id);

            // Name of item
            Element nameElement = doc.createElementNS(docNS, "name");
            Text nameText = doc.createTextNode(name);
            nameElement.appendChild(nameText);
            root.appendChild(nameElement);

            // Description of item
            Element descriptionElement =
                doc.createElementNS(docNS, "description");
            Text descriptionText = doc.createText(description);
            descriptionElement.appendChild(descriptionText);
            root.appendChild(descriptionElement);
  } else {
            // Load document
            try {
                DOMParser parser = new DOMParser( );
                parser.parse(xmlFile.toURL().toString( ));
                doc = parser.getDocument( );

                Element root = doc.getDocumentElement( );

                // Name of item
                NodeList nameElements =
                    root.getElementsByTagNameNS(docNS, "name");
                Element nameElement = (Element)nameElements.item(0);
                Text nameText = (Text)nameElement.getFirstChild( );
                nameText.setData(name);

                // Description of item
                NodeList descriptionElements =
                    root.getElementsByTagNameNS(docNS, "description");
                Element descriptionElement = (Element)descriptionElements.item(0);

                // Remove and recreate description
                root.removeChild(descriptionElement);
                descriptionElement = doc.createElementNS(docNS, "description");
                Text descriptionText = doc.createTextNode(description);
                descriptionElement.appendChild(descriptionText);
                root.appendChild(descriptionElement);
            } catch (SAXException e) {
                // Print error
                PrintWriter out = res.getWriter( );
                res.setContentType("text/html");
                out.println("<HTML><BODY>Error in reading XML: " +
                    e.getMessage( ) + ".</BODY></HTML>");
                out.close( );
                return;
            }
        }

        // Serialize DOM tree
        DOMSerializer serializer = new DOMSerializer( );
        serializer.serialize(doc, xmlFile);

        // Print confirmation
        PrintWriter out = res.getWriter( );
        res.setContentType("text/html");
        out.println("<HTML><BODY>Thank you for your submission. " +
            "Your item has been processed.</BODY></HTML>");
        out.close( );
    }

Using the createElementNS( ) method to create namespaced elements and searching for them with getElementsByTagNameNS( ) seems to be perfect. The createDocument( ) method even has a handy place to insert the namespace URI for the root element. These elements are all put into the default namespace, and everything looks fine. However, there is a big problem here. Look at the output from running this servlet with no existing XML (this is generated XML, rather than modified XML):

<?xml version="1.0"?>
<item id="bourgOM">
<name>Bourgeois OM Guitar</name>
<description>This is a <i>beautiful</i> <b>Sitka-topped</b> guitar with
<b>Indian Rosewood</b> back and sides. Made by luthier
<a href="http://www.bourgeoisguitars.com">Dana Bourgeois</a>, this OM has a
<b>huge sound</b>.
The guitar has <i>great action</i>, a 1 3/4" nut, and all
<i>fossilized ivory</i> nut and saddle, with <i>ebony</i> end pins.
New condition, this is a <b>great guitar</b>!</description>
</item>

Does this look familiar? It is the XML from earlier, with no change! The one thing that DOM does not do is add namespace declarations. Instead, you'll need to manually add the xmlns attribute to your DOM tree; otherwise, when reading in the document, the elements won't be placed into a namespace and you will have some problems. One small change takes care of this, though:

// Create new DOM tree
DOMImplementation domImpl = new DOMImplementationImpl( );
doc = domImpl.createDocument(docNS, "item", null);
Element root = doc.getDocumentElement( );
root.setAttribute("xmlns", docNS);

Now you'll get the namespace declaration that you were probably expecting to show up the first go round. You can compile these changes, and try things out. You won't notice any difference; changes are made just as they were before. However, your documents should now have namespaces, both in the reading and writing portion of the servlet application.

A final word on this namespace detail: keep in mind that you could certainly modify the DOMSerializer class to look for namespaces on elements, and print out the appropriate xmlns declarations as it walks the tree. This is a perfectly legal change, and would be sort of valuable; in fact, it's what many solutions, like those found within Xerces, already do. In any case, as long as you are aware of this behavior, you are protected from being the victim of it.

Previous | Home | Next
6. Advanced DOM | Book Index | 6.3. DOM Level 2 Modules
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DOM Level 2 Modules (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

6.3. DOM Level 2 Modules

Now that you've seen what the DOM and the Level 2 core offering provide, I will talk about some additions to DOM Level 2. These are the various modules that add functionality to the core. They are useful from time to time, in certain DOM applications.

First, though, you must have a DOM Level 2 parser available. If you are using a parser that you have purchased or downloaded on your own, this is pretty easy. For example, you can go to the Apache XML web site at http://xml.apache.org, download the latest version of Xerces, and you've got DOM Level 2. However, if you're using a parser bundled with another technology, things can get a little trickier. For example, if you've got Jakarta's Tomcat servlet engine, you will find xml.jar and parser.jar in the lib/ directory and in the Tomcat classpath. This isn't so good, as these are DOM Level 1 implementations and won't support many of the features I talk about in this section; in that case, download a DOM Level 2 parser manually and ensure that it is loaded before any DOM Level 1 parsers.

WARNING: Beware of the newer versions of Tomcat. They do something ostensibly handy: load all jar files in the lib/ directory at startup. Unfortunately, because this is done alphabetically, putting xerces.jar in the lib/ directory means that parser.jar, a DOM Level 1 parser, will still be loaded first and you won't get DOM Level 2 support. A common trick to solve this problem is to rename the files: parser.jar becomes z_parser.jar, and xml.jar becomes z_xml.jar. This causes them to be loaded after Xerces, and then you will get DOM Level 2 support. This is the problem I mentioned earlier in the servlet example.

Once you've got a capable parser, you're ready to go. Before diving into the new modules, though, I want to show you a high-level overview of what these modules are all about.

6.3.1. Branching Out

When the DOM Level 1 specification came out, it was a single specification. It was defined basically as you read in Chapter 5, "DOM", with a few minor exceptions. However, when activity began on DOM Level 2, a whole slew of specifications resulted, each called a module. If you take a look at the complete set of DOM Level 2 specifications, you'll see six different modules listed. Seems like a lot, doesn't it? I'm not going to cover all of these modules; you'd be reading about DOM for the next four or five chapters. However, I will give you the rundown on the purpose of each module, summarized in Table 6-1. I've included the module's specification, name, and purpose, which you'll need to use shortly.

Table 6-1. DOM specifications and purpose

Specification | Module name | Summary of purpose
DOM Level 2 Core | XML | Extends the DOM Level 1 specification; deals with basic DOM structures like Element, Attr, Document, etc.
DOM Level 2 Views | Views | Provides a model for scripts to dynamically update a DOM structure.
DOM Level 2 Events | Events | Defines an event model for programs and scripts to use in working with DOM.
DOM Level 2 Style | CSS | Provides a model for CSS (Cascading Style Sheets) based on the DOM Core and DOM Views specifications.
DOM Level 2 Traversal and Range | Traversal/Range | Defines extensions to the DOM for traversing a document and identifying the range of content within that document.
DOM Level 2 HTML | HTML | Extends the DOM to provide interfaces for dealing with HTML structures in a DOM format.

If views, events, CSS, HTML, and traversal were all in a single specification, nothing would ever get done at the W3C! To facilitate all of this moving along, and yet not hamstringing the DOM in the process, the different concepts were broken up into separate specifications.

Once you figure out which specifications to use, you're almost ready to roll. A DOM Level 2 parser is not required to support each of these specifications; as a result, you need to verify that the features you want to use are present in your XML parser. Happily, this is fairly simple to accomplish. Remember the hasFeature( ) method I showed you on the DOMImplementation class? Well, if you supply it a module name and version, it will let you know if the module and feature requested are supported. Example 6-4 is a small program that queries an XML parser's support for the DOM modules listed in Table 6-1. You will need to change the name of your vendor's DOMImplementation implementation class, but other than that adjustment, it should work for any parser.

Example 6-4. Checking features on a DOM implementation

package javaxml2;

import org.w3c.dom.DOMImplementation;

public class DOMModuleChecker {

    /** Vendor DOMImplementation impl class */
    private String vendorImplementationClass =
        "org.apache.xerces.dom.DOMImplementationImpl";

    /** Modules to check */
    private String[] moduleNames =
        {"XML", "Views", "Events", "CSS", "Traversal", "Range", "HTML"};

    public DOMModuleChecker( ) {
    }

    public DOMModuleChecker(String vendorImplementationClass) {
        this.vendorImplementationClass = vendorImplementationClass;
    }

    public void check( ) throws Exception {
        DOMImplementation impl =
            (DOMImplementation)Class.forName(vendorImplementationClass)
                                    .newInstance( );
        for (int i=0; i<moduleNames.length; i++) {
            if (impl.hasFeature(moduleNames[i], "2.0")) {
                System.out.println("Support for " + moduleNames[i] +
                    " is included in this DOM implementation.");
            } else {
                System.out.println("Support for " + moduleNames[i] +
                    " is not included in this DOM implementation.");
            }
        }
    }

    public static void main(String[] args) {
        if ((args.length != 0) && (args.length != 1)) {
            System.out.println("Usage: java javaxml2.DOMModuleChecker " +
                "[DOMImplementation impl class to query]");
            System.exit(-1);
        }

        try {
            DOMModuleChecker checker = null;
            if (args.length == 1) {
                checker = new DOMModuleChecker(args[1]);
            } else {
                checker = new DOMModuleChecker( );
            }
            checker.check( );
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

Running this program with xerces.jar in my classpath, I got the following output:

C:\javaxml2\build>java javaxml2.DOMModuleChecker
Support for XML is included in this DOM implementation.
Support for Views is not included in this DOM implementation.
Support for Events is included in this DOM implementation.
Support for CSS is not included in this DOM implementation.
Support for Traversal is included in this DOM implementation.
Support for Range is not included in this DOM implementation.
Support for HTML is not included in this DOM implementation.

By specifying the DOMImplementation implementation class for your vendor, you can check the supported modules in your own DOM parser. In the next few subsections, I will address a few of the modules that I've found useful, and that you will want to know about as well.

6.3.2. Traversal

First up on the list is the DOM Level 2 Traversal module. This is intended to provide tree-walking capability, but also to allow you to refine the nature of that behavior. In the earlier section on DOM mutation, I mentioned that most of your DOM code will know something about the structure of a DOM tree being worked with; this allows for quick traversal and modification of both structure and content. However, for those times when you do not know the structure of the document, the traversal module comes into play.

Consider the auction site again, and the items input by the user. Most critical are the item name and the description. Since most popular auction sites provide some sort of search, you would want to provide the same in this fictional example. Just searching item titles isn't going to cut it in the real world; instead, a set of key words should be extracted from the item descriptions. I say key words because you don't want a search on "adirondack top" (which to a guitar lover obviously applies to the wood on the top of a guitar) to return toys ("top") from a particular mountain range ("Adirondack"). The best way to do this in the format discussed so far is to extract words that are formatted in a certain way. So the words in the description that are bolded, or in italics, are perfect candidates. Of course, you could grab all the nontextual child elements of the description element. However, you'd have to weed through links (the a element), image references (img), and so forth. What you really want is to specify a custom traversal. Good news; you're in the right place.

The whole of the traversal module is contained within the org.w3c.dom.traversal package. Just as everything within core DOM begins with a Document interface, everything in DOM Traversal begins with the org.w3c.dom.traversal.DocumentTraversal interface. This interface provides two methods:

NodeIterator createNodeIterator(Node root, int whatToShow, NodeFilter filter,
                                boolean expandEntityReferences);
TreeWalker createTreeWalker(Node root, int whatToShow, NodeFilter filter,
                            boolean expandEntityReferences);

Most DOM implementations that support traversal choose to have their org.w3c.dom.Document implementation class implement the DocumentTraversal interface as well; this is how it works in Xerces. In a nutshell, using a NodeIterator provides a list view of the elements it iterates over; the closest analogy is a standard Java List (in the java.util package). TreeWalker provides a tree view, which you may be more used to in working with XML by now.

6.3.2.1. NodeIterator

I want to get past all the conceptualization and into the code sample I referred to earlier. I want access to all content within the description of an item from the auction site that is within a specific set of formatting tags. To do this, I first need access to the DOM tree itself. Since this doesn't fit into the servlet approach (you probably wouldn't have a servlet building the search phrases, you'd have some standalone class), I need a new class, ItemSearcher (Example 6-5). This class takes any number of item files to search through as arguments.

Example 6-5. The ItemSearcher class

package javaxml2;

import java.io.File;

// DOM imports
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.NodeIterator;

// Vendor parser
import org.apache.xerces.parsers.DOMParser;

public class ItemSearcher {

    private String docNS = "http://www.oreilly.com/javaxml2";

    public void search(String filename) throws Exception {
        // Parse into a DOM tree
        File file = new File(filename);
        DOMParser parser = new DOMParser( );
        parser.parse(file.toURL().toString( ));
        Document doc = parser.getDocument( );

        // Get node to start iterating with
        Element root = doc.getDocumentElement( );
        NodeList descriptionElements =
            root.getElementsByTagNameNS(docNS, "description");
        Element description = (Element)descriptionElements.item(0);

        // Get a NodeIterator
        NodeIterator i = ((DocumentTraversal)doc)
            .createNodeIterator(description, NodeFilter.SHOW_ALL, null, true);

        Node n;
        while ((n = i.nextNode( )) != null) {
            if (n.getNodeType( ) == Node.ELEMENT_NODE) {
                System.out.println("Encountered Element: '" +
                    n.getNodeName( ) + "'");
            } else if (n.getNodeType( ) == Node.TEXT_NODE) {
                System.out.println("Encountered Text: '" +
                    n.getNodeValue( ) + "'");
            }
        }
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("No item files to search through specified.");
            return;
        }

        try {
            ItemSearcher searcher = new ItemSearcher( );
            for (int i=0; i<args.length; i++) {
                System.out.println("Processing file: " + args[i]);
                searcher.search(args[i]);
            }
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

As you can see, I've created a NodeIterator, and supplied it the description element to start with for iteration. The constant value passed as the filter instructs the iterator to show all nodes. You could just as easily provide values like Node.SHOW_ELEMENT and Node.SHOW_TEXT, which would show only elements or textual nodes, respectively. I haven't yet provided a NodeFilter implementation (I'll get to that next), and I allowed for entity reference expansion. What is nice about all this is that the iterator, once created, doesn't have just the child nodes of description. Instead, it actually has all nodes under description, even when nested multiple levels deep. This is extremely handy for dealing with unknown XML structure!

At this point, you still have all the nodes, which is not what you want. I added some code (the last while loop) to show you how to print out the element and text node results. You can run the code as is, but it's not going to help much. Instead, the code needs to provide a filter, so it only picks up elements with the formatting desired: the text within an i or b block. You can provide this customized behavior by supplying a custom implementation of the NodeFilter interface, which defines only a single method:

public short acceptNode(Node n);

This method should return NodeFilter.FILTER_SKIP, NodeFilter.FILTER_REJECT, or NodeFilter.FILTER_ACCEPT. The first skips the examined node, but continues to iterate over its children; the second rejects the examined node and its children (only applicable in TreeWalker); and the third accepts and passes on the examined node. It behaves a lot like SAX, in that you can intercept nodes as they are being iterated and decide if they should be passed on to the calling method. Add the following nonpublic class to the ItemSearcher.java source file:

class FormattingNodeFilter implements NodeFilter {

    public short acceptNode(Node n) {
        if (n.getNodeType( ) == Node.TEXT_NODE) {
            Node parent = n.getParentNode( );
            if ((parent.getNodeName( ).equalsIgnoreCase("b")) ||
                (parent.getNodeName( ).equalsIgnoreCase("i"))) {
                return FILTER_ACCEPT;
            }
        }
        // If we got here, not interested
        return FILTER_SKIP;
    }
}

This is just plain old DOM code, and shouldn't pose any difficulty to you. First, the code only wants text nodes; the text of the formatted elements is desired, not the elements themselves. Next, the parent is determined, and since it's safe to assume that Text nodes have Element node parents, the code immediately invokes getNodeName( ). If the element name is either "b" or "i", the code has found search text, and returns FILTER_ACCEPT. Otherwise, FILTER_SKIP is returned.

All that's left now is a change to the iterator creation call instructing it to use the new filter implementation, and to the output, both in the existing search( ) method of the ItemSearcher class:

// Get a NodeIterator
NodeIterator i = ((DocumentTraversal)doc)
    .createNodeIterator(description, NodeFilter.SHOW_ALL,
        new FormattingNodeFilter( ), true);

Node n;
while ((n = i.nextNode( )) != null) {
    System.out.println("Search phrase found: '" + n.getNodeValue( ) + "'");
}
NOTE: Some astute readers will wonder what happens when a NodeFilter implementation conflicts with the constant supplied to the createNodeIterator( ) method (in this case that constant is NodeFilter.SHOW_ALL). Actually, the short constant filter is applied first, and then the resulting list of nodes is passed to the filter implementation. If I had supplied the constant NodeFilter.SHOW_ELEMENT, I would not have gotten any search phrases, because my filter would not have received any Text nodes to examine; just Element nodes. Be careful to use the two together in a way that makes sense. In the example, I could have safely used NodeFilter.SHOW_TEXT also.

Now, the class is useful and ready to run. Executing it on the bourgOM.xml file I explained in the first section, I get the following results:

bmclaugh@GANDALF ~/javaxml2/build
$ java javaxml2.ItemSearcher ../ch06/xml/item-bourgOM.xml
Processing file: ../ch06/xml/item-bourgOM.xml
Search phrase found: 'beautiful'
Search phrase found: 'Sitka-topped'
Search phrase found: 'Indian Rosewood'
Search phrase found: 'huge sound'
Search phrase found: 'great action'
Search phrase found: 'fossilized ivory'
Search phrase found: 'ebony'
Search phrase found: 'great guitar'

This is perfect: all of the bolded and italicized phrases are now ready to be added to a search facility. (Sorry; you'll have to write that yourself!)

6.3.2.2. TreeWalker

The TreeWalker interface is almost exactly the same as the NodeIterator interface; the only difference is that you get a tree view instead of a list view. This is primarily useful if you want to deal with only a certain type of node within a tree; for instance, the tree with only elements or without any comments. By using the constant filter value (such as NodeFilter.SHOW_ELEMENT) and a filter implementation (like one that passes on FILTER_SKIP for all comments), you can essentially get a view of a DOM tree without extraneous information. The TreeWalker interface provides all the basic node operations, such as firstChild( ), parentNode( ), nextSibling( ), and of course getCurrentNode( ), which tells you where you are currently walking.

I'm not going to give an example here. By now, you should see that this is identical to dealing with a standard DOM tree, except that you can filter out unwanted items by using the NodeFilter constants. This is a great, simple way to limit your view of XML documents to only information you are interested in seeing. Use it well; it's a real asset, as is NodeIterator! You can also check out the complete specification online at http://www.w3.org/TR/DOM-Level-2-Traversal-Range/.

6.3.3. Range

The DOM Level 2 Range module is one of the least commonly used modules, probably due to a lack of understanding of DOM Range rather than any lack of usefulness. This module provides a way to deal with a set of content within a document. Once you've defined that range of content, you can insert into it, copy it, delete parts of it, and manipulate it in various ways. The most important thing to start with is realizing that "range" in this sense refers to a number of pieces of a DOM tree grouped together. It does not refer to a set of allowed values, where a high and low or start and end are defined. Therefore, DOM Range has nothing at all to do with validation of data values. Get that, and you're already ahead of the pack.

Like traversal, working with Range involves a new DOM package: org.w3c.dom.ranges. There are actually only two interfaces and one exception within this class, so it won't take you long to get your bearings. First is the analog to Document (and DocumentTraversal): that's org.w3c.dom.ranges.DocumentRange. Like the DocumentTraversal class, Xerces' Document implementation class implements Range. And also like DocumentTraversal, it has very few interesting methods; in fact, only one:

public Range createRange( );

All other range operations operate upon the Range class (rather, an implementation of the interface; but you get the idea). Once you've got an instance of the Range interface, you can set the starting and ending points, and edit away. As an example, let's go back to the UpdateItemServlet . I mentioned that it's a bit of a hassle to try and remove all the children of the description element and then set the new description text; that's because there is no way to tell if a single Text node is within the description, or if many elements and text nodes, as well as nested nodes, exist within a description that is primarily HTML. I showed you how to simply remove the old description element and create a new one. However, DOM Range makes this unnecessary. Take a look at this modification to the doPost( ) method of that servlet:

            // Load document
            try {
                DOMParser parser = new DOMParser( );
                parser.parse(xmlFile.toURL().toString( ));
                doc = parser.getDocument( );

                Element root = doc.getDocumentElement( );

                // Name of item
                NodeList nameElements =
                    root.getElementsByTagNameNS(docNS, "name");
                Element nameElement = (Element)nameElements.item(0);
                Text nameText = (Text)nameElement.getFirstChild( );
                nameText.setData(name);

                // Description of item
                NodeList descriptionElements =
                    root.getElementsByTagNameNS(docNS, "description");
                Element descriptionElement = (Element)descriptionElements.item(0);

                // Remove and recreate description
                Range range = ((DocumentRange)doc).createRange( );
                range.setStartBefore(descriptionElement.getFirstChild( ));
                range.setEndAfter(descriptionElement.getLastChild( ));
                range.deleteContents( );
                Text descriptionText = doc.createTextNode(description);
                descriptionElement.appendChild(descriptionText);

                range.detach( );
            } catch (SAXException e) {
                // Print error
                PrintWriter out = res.getWriter( );
                res.setContentType("text/html");
                out.println("<HTML><BODY>Error in reading XML: " +
                    e.getMessage( ) + ".</BODY></HTML>");
                out.close( );
                return;
            }

To remove all the content, I first create a new Range, using the DocumentRange cast. You'll need to add import statements for the DocumentRange and Range classes to your servlet, too (they are both in the org.w3c.dom.ranges package).

NOTE: In the first part of the DOM Level 2 Modules section, I showed you how to check which modules a parser implementation supports. I realize that Xerces reported that it did not support Range. However, running this code with Xerces 1.3.0, 1.3.1, and 1.4 all worked without a hitch. Strange, isn't it?

Once the range is ready, set the starting and ending points. Since I want all content within the description element, I start before the first child of that Element node (using setStartBefore( )), and end after its last child (using setEndAfter( )). There are other, similar methods for this task, setStartAfter( ) and setEndBefore( ). Once that's done, it's simple to call deleteContents( ). Just like that, not a bit of content is left. Then the servlet creates the new textual description and appends it. Finally, I let the JVM know that it can release any resources associated with the Range by calling detach( ). While this step is commonly overlooked, it can really help with lengthy bits of code that use the extra resources.

Another option is to use extractContents( ) instead of deleteContents( ). This method removes the content, then returns the content that has been removed. You could insert this as an archived element, for example:

// Remove and recreate description
Range range = ((DocumentRange)doc).createRange( );
range.setStartBefore(descriptionElement.getFirstChild( ));
range.setEndAfter(descriptionElement.getLastChild( ));
Node oldContents = range.extractContents( );
Text descriptionText = doc.createTextNode(description);
descriptionElement.appendChild(descriptionText);

// Set this as content to some other, archival, element
archivalElement.appendChild(oldContents);

Don't try this in your servlet; there is no archivalElement in this code, and it is just for demonstration purposes. However, it should be starting to sink in that the DOM Level 2 Range module can really help you in editing documents' contents. It also provides yet another way to get a handle on content when you aren't sure of the structure of that content ahead of time.

There's a lot more to ranges in DOM; check this out on your own, along with all of the DOM modules covered in this chapter. However, you should now have enough of an understanding of the basics to get you going. Most importantly, realize that at any point in an active Range instance, you can simply invoke range.insertNode(Node newNode) and add new content, wherever you are in a document! It is this robust editing quality of ranges that make them so attractive. The next time you need to delete, copy, extract, or add content to a structure that you know little about, think about using ranges. The specification gives you information on all this and more, and is located online at http://www.w3.org/TR/DOM-Level-2-Traversal-Range/.

6.3.4. Events, Views, and Style

Aside from the HTML module, which I'll talk about next, there are three other DOM Level 2 modules: Events, Views, and Style. I'm not going to cover these three in depth in this book, largely because I believe that they are more useful for client programming. So far, I've focused on server-side programming, and I'm going to keep in that vein throughout the rest of the book. These three modules are most often used on client software such as IDEs, web pages, and the like. Still, I want to briefly touch on each so you'll still be on top of the DOM heap at the next alpha-geek soirée.

6.3.4.1. Events

The Events module provides just what you are probably expecting: a means of "listening" to a DOM document. The relevant classes are in the org.w3c.dom.events package, and the class that gets things going is DocumentEvent. No surprise here; compliant parsers (like Xerces) implement this interface in the same class that implements org.w3c.dom.Document. The interface defines only one method:

public Event createEvent(String eventType);

The string passed in is the type of event; valid values in DOM Level 2 are "UIEvent", "MutationEvent", and "MouseEvent". Each of these has a corresponding class: UIEvent, MutationEvent, and MouseEvent. You'll note, in looking at the Xerces Javadoc, that they provide only the MutationEvent interface, which is the only event type Xerces supports. When an event is "fired" off, it can be handled (or "caught") by an EventListener.

This is where the DOM core support comes in; a parser supporting DOM events should have the org.w3c.dom.Node interface implementing the org.w3c.dom.events.EventTarget interface. So every node can be the target of an event. This means that you have the following method available on those nodes:

public void addEventListener(String type, EventListener listener,
                             boolean capture);

Here's the process. You create a new EventListener (which is a custom class you would write) implementation. You need to implement only a single method:

public void handleEvent(Event event);

Register that listener on any and all nodes you want to work with. Code in here typically does some useful task, like emailing users that their information has been changed (in some XML file), revalidating the XML (think XML editors), or asking users if they are sure they want to perform the action.

At the same time, you'll want your code to trigger a new Event on certain actions, like the user clicking on a node in an IDE and entering new text, or deleting a selected element. When the Event is triggered, it is passed to the available EventListener instances, starting with the active node and moving up. This is where your listener's code executes, if the event types are the same. Additionally, you can have the event stop propagating at that point (once you've handled it), or bubble up the event chain and possibly be handled by other registered listeners.

So there you have it; events in only a page! And you thought specifications were hard to read. Seriously, this is some useful stuff, and if you are working with client-side code, or software that will be deployed standalone on user's desktops (like that XML editor I keep talking about), this should be a part of your DOM toolkit. Check out the full specification online at http://www.w3.org/TR/DOM-Level-2-Events/.

6.3.4.2. Views

Next on the list is DOM Level 2 Views. The reason I don't cover views in much detail is that, really, there is very little to be said. From every reading I can make of the (one-page!) specification, it's simply a basis for future work, perhaps in vertical markets. The specification defines only two interfaces, both in the org.w3c.dom.views package. Here's the first:

package org.w3c.dom.views;

public interface AbstractView {
    public DocumentView getDocument( );

}

And here's the second:

package org.w3c.dom.views;

public interface DocumentView {
    public AbstractView getDefaultView( );

}

Seems a bit cyclical, doesn't it? A single source document (a DOM tree) can have multiple views associated with it. In this case, view refers to a presentation, like a styled document (after XSL or CSS has been applied), or perhaps a version with Shockwave and one without. By implementing the AbstractView interface, you can define your own customized versions of displaying a DOM tree. For example, consider this example subinterface:

package javaxml2;

import org.w3c.dom.views.AbstractView;

public interface StyledView implements AbstractView {

    public void setStylesheet(String stylesheetURI);

    public String getStylesheetURI( );
}

I've left out the method implementations, but you can see how this could be used to provide stylized views of a DOM tree. Additionally, a compliant parser implementation would have the org.w3c.dom.Document implementation implement DocumentView, which allows you to query a document for its default view. It's expected that in a later version of the specification you will be able to register multiple views for a document, and more closely tie a view or views to a document.

Look for this to be fleshed out more as browsers like Netscape, Mozilla, and Internet Explorer provide these sorts of views of XML. Additionally, you can read the short specification and know as much as I do by checking it out online at http://www.w3.org/TR/DOM-Level-2-Views/.

6.3.4.3. Style

Finally, there is the Style module, also referred to as simply CSS (Cascading Style Sheets). You can check this specification out at http://www.w3.org/TR/DOM-Level-2-Style/. This provides a binding for CSS stylesheets to be represented by DOM constructs. Everything of interest is in the org.w3c.dom.stylesheets and org.w3c.dom.css packages. The former contains generic base classes, and the latter provides specific applications to Cascading Style Sheets. Both are primarily used for showing a client a styled document.

You use this module exactly like you use the core DOM interfaces: you get a Style-compliant parser, parse a stylesheet, and use the CSS language bindings. This is particularly handy when you want to parse a CSS stylesheet and apply it to a DOM document. You're working from the same basic set of concepts, if that makes sense to you (and it should; when you can do two things with an API instead of one, that's generally good!). Again, I only briefly touch on the Style module, because it's accessible with the Javadoc in its entirety. The classes are aptly named (CSSValueList, Rect, CSSDOMImplementation), and are close enough to their XML DOM counterparts that I'm confident you'll have no problem using them if you need to.

6.3.5. HTML

For HTML, DOM provides a set of interfaces that model the various HTML elements. For example, you can use the HTMLDocument class, the HTMLAnchorElement, and the HTMLSelectElement (all in the org.w3c.dom.html package) to represent their analogs in HTML (<HTML>, <A>, and <SELECT> in this case). All of these provide convenience methods like setTitle( ) (on HTMLDocument), setHref( ) (on HTMLAnchorElement), and getOptions( ) (on HTMLSelectElement). All of these extend core DOM structures like Document and Element, and so can be used as any other DOM Node could.

However, it turns out that the HTML bindings are rarely used (at least directly). It's not because they aren't useful; instead, many tools have already been written to provide this sort of access through even more user-friendly tools. XMLC, a project within the Enhydra application server framework, is one such example (located online at http://xmlc.enhydra.org), and Cocoon, covered in Chapter 10, "Web Publishing Frameworks", is another. These allow developers to work with HTML and web pages in a way that does not necessarily require even basic DOM knowledge, making it more accessible to web designers and newer Java developers. The end result of using these tools is that the HTML DOM bindings are rarely needed. But if you know about them, you can use them if you need to. Additionally, you can use standard DOM functionality on well-formed HTML documents (XHTML), treating elements as Element nodes and attributes as Attr nodes. Even without the HTML bindings, you can use DOM to work with HTML. Piece of cake.

6.3.6. Odds and Ends

What's left in DOM Level 2 besides these modules and namespace-awareness? Very little, and you've probably already used most of it. The createDocument( ) and createDocumentType( ) methods are new to the DOMImplementation class, and you've used both of them. Additionally, the getSystemId( ) and getPublicId( ) methods used in the DOMSerializer class on the DocumentType interface are also DOM Level 2 additions. Other than that, there isn't much; a few new DOMException error code constants, and that's about it. You can see the complete list of changes online at http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/changes.html. The rest of the changes are the additional modules, one of which I'll cover next.

Previous | Home | Next
6.2. Namespaces | Book Index | 6.4. DOM Level 3
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DOM Level 3 (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

6.4. DOM Level 3

Before closing the book on DOM and looking at common gotchas, I will spend a little time letting you know what's coming in DOM Level 3, which is underway right now. In fact, I expect this specification to be finalized early in 2002, not long from the time you are probably reading this book. The items I point out here aren't all of the changes and additions in DOM Level 3, but they are the ones that I think are of general interest to most DOM developers (that's you now, if you were wondering). Many of these are things that DOM programmers have been requesting for several years, so now you can look forward to them as well.

6.4.1. The XML Declaration

The first change in the DOM that I want to point out seems pretty trivial at first glance: exposure of the XML declaration. Remember those? Here's an example:

<?xml version="1.0" standalone="yes" encoding="UTF-8"?>

There are three important pieces of information here that are not currently available in DOM: the version, the state of the standalone attribute, and the specified encoding. Additionally, the DOM tree itself has an encoding; this may or may not match up to the XML encoding attribute. For example, the associated encoding for "UTF-8" in Java turns out to be "UTF8", and there should be a way to distinguish between the two. All of these problems are solved in DOM Level 3 by the addition of four attributes to the Document interface. These are version (a String), standalone (a boolean), encoding (another String), and actualEncoding (String again). The accessor and mutator methods to modify these attributes are pretty straightforward:

public String getVersion( );
public void setVersion(String version);

public boolean getStandalone( );
public void setStandalone(boolean standalone);

public String getEncoding( );
public void setEncoding(String encoding);

public String getActualEncoding( );
public void setActualEncoding(String actualEncoding);

Most importantly, you'll finally be able to access the information in the XML declaration. This is a real boon to those writing XML editors and the like that need this information. It also helps developers working with internationalization and XML, as they can ascertain a document's encoding (encoding), create a DOM tree with its encoding (actualEncoding), and then translate as needed.

6.4.2. Node Comparisons

In Levels 1 and 2 of DOM, the only way to compare two nodes is to do it manually. Developers end up writing utility methods that use instanceof to determine the type of Node, and then compare all the available method values to each other. In other words, it's a pain. DOM Level 3 offers several comparison methods that alleviate this pain. I'll give you the proposed signatures, and then tell you about each. They are all additions to the org.w3c.dom.Node interface, and look like this:

// See if the input Node is the same object as this Node
public boolean isSameNode(Node input);

// Tests for equality in structure (not object equality)
public boolean equalsNode(Node input, boolean deep);

/** Constants for document order */
public static final int DOCUMENT_ORDER_PRECEDING = 1;
public static final int DOCUMENT_ORDER_FOLLOWING = 2;
public static final int DOCUMENT_ORDER_SAME      = 3;
public static final int DOCUMENT_ORDER_UNORDERED = 4;

// Determine the document order of input in relation to this Node
public int compareDocumentOrder(Node input) throws DOMException;

/** Constants for tree position */
public static final int TREE_POSITION_PRECEDING  = 1;
public static final int TREE_POSITION_FOLLOWING  = 2;
public static final int TREE_POSITION_ANCESTOR   = 3;
public static final int TREE_POSITION_DESCENDANT = 4;
public static final int TREE_POSITION_SAME       = 5;
public static final int TREE_POSITION_UNORDERED  = 6;

// Determine the tree position of input in relation to this Node
public int compareTreePosition(Node input) throws DOMException;

The first method, isSameNode( ), allows for object comparison. This doesn't determine whether the two nodes have the same structure or data, but whether they are the same object in the JVM. The second method, equalsNode( ), is probably going to be more commonly used in your applications. It tests for Node equality in terms of data and type (obviously, an Attr will never be equal to a DocumentType). It provides a parameter, deep, to allow comparison of just the Node itself or of all its child Nodes as well.

The next two methods, compareDocumentOrder( ) and compareTreePosition( ), allow for relational positioning of the current Node and an input Node. For both, there are several constants defined to be used as return values. A node can be before the current one in the document, after it, in the same position, or unordered. The unordered value occurs when comparing an attribute to an element, or in any other case where the term "document order" has no contextual meaning. And finally, a DOMException occurs when the two nodes being queried are not in the same DOM Document object. The final new method, compareTreePosition( ), provides the same sort of comparison, but adds the ability to determine ancestry. Two additional constants, TREE_POSITION_ANCESTOR and TREE_POSITION_DESCENDANT, allow for this. The first denotes that the input Node is up the hierarchy from the reference Node (the one the method is invoked upon); the second indicates that the input Node is down the hierarchy from the reference Node.

With these four methods, you can isolate any DOM structure and determine how it relates to another. This addition to DOM Level 3 should serve you well, and you can count on using all of the comparison methods in your coding. Keep an eye on both the constant names and values, though, as they may change over the evolution of the specification.

6.4.3. Bootstrapping

The last addition in DOM Level 3 I want to cover is arguably the most important: the ability to bootstrap. I mentioned earlier that in creating DOM structures, you are forced to use vendor-specific code (unless you're using JAXP, which I'll cover in Chapter 9, "JAXP"). This is a bad thing, of course, as it knocks out vendor-independence. For the sake of discussion, I'll repeat a code fragment that creates a DOM Document object using a DOMImplementation here:

import org.w3c.dom.Document;
import org.w3c.dom.DOMImplementation;

import org.apache.xerces.dom.DOMImplementationImpl;

// Class declaration and other Java constructs

DOMImplementation domImpl = DOMImplementationImpl.getDOMImplementation( );
Document doc = domImpl.createDocument( );
// And so on...

The problem is that there is no way to get a DOMImplementation without importing and using a vendor's implementation class. The solution is to use a factory that provides DOMImplementation instances. Of course, the factory is actually providing a vendor's implementation of DOMImplementation (I know, I know, it's a bit confusing). Vendors can set system properties or provide their own versions of this factory so that it returns the implementation class they want. The resulting code to create DOM trees then looks like this:

import org.w3c.dom.Document;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.DOMImplementationFactory;

// Class declaration and other Java constructs

DOMImplementation domImpl =
    DOMImplementationFactory.getDOMImplementation( );
Document doc = domImpl.createDocument( );
// And so on...

The class being added is DOMImplementationFactory, and should solve most of your vendor-independence issues once it's in place. Look for this as the flagship of DOM Level 3, as it's one of the most requested features for current levels of DOM.

Previous | Home | Next
6.3. DOM Level 2 Modules | Book Index | 6.5. Gotcha!
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Gotcha! (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

6.5. Gotcha!

DOM has a set of troublesome spots just like SAX, and just like the APIs we'll cover in the next few chapters. I will point some of those out to you, and hopefully save you a few hours of debugging time along the way. Enjoy; these happen to be problems that I've run into and struggled against for quite a while before getting things figured out.

6.5.1. The Dreaded WRONG DOCUMENT Exception

The number one problem that I see among DOM developers is what I refer to as "the dreaded WRONG DOCUMENT exception." This exception occurs when you try to mix nodes from different documents. It most often shows up when you try to move a node from one document to another, which turns out to be a common task.

The problem arises because of the factory approach I mentioned earlier. Because each element, attribute, processing instruction, and so on is created from a Document instance, it is not safe to assume that those nodes are compatible with other Document instances; two instances of Document may be from different vendors with different supported features, and trying to mix and match nodes from one with nodes from the other can result in implementation-dependent problems. As a result, to use a node from a different document requires passing that node into the target document's insertNode( ) method. The result of this method is a new Node, which is compatible with the target document. In other words, this code is going to cause problems:

Element otherDocElement = otherDoc.getDocumentElement( );
Element thisDocElement = thisDoc.getDocumentElement( );

// Here's the problem - mixing nodes from different documents
thisDocElement.appendChild(otherDocElement);

This exception will result:

org.apache.xerces.dom.DOMExceptionImpl: DOM005 Wrong document
 at org.apache.xerces.dom.ChildAndParentNode.internalInsertBefore(
     ChildAndParentNode.java:314)
 at org.apache.xerces.dom.ChildAndParentNode.insertBefore(
     ChildAndParentNode.java:296)
 at org.apache.xerces.dom.NodeImpl.appendChild(NodeImpl.java:213)
 at MoveNode.main(MoveNode.java:30)

To avoid this, you must first import the desired node into the new document:

Element otherDocElement = otherDoc.getDocumentElement( );
Element thisDocElement = thisDoc.getDocumentElement( );

// Import the node into the right document
Element readyToUseElement =
(Element)thisDoc.importNode(otherDocElement);

// Now this works
thisDocElement.appendChild(readyToUseElement);

Note that the result of importNode( ) is a Node, so it must be cast to the correct interface (Element in this case). Save yourself some time and effort and commit this to memory; write it on a notecard and tuck it under your pillow. Trust me, this is about the most annoying exception known to man!

6.5.2. Creating, Appending, and Inserting

Fixing the problem I just described often leads to another problem. A common error I've seen is when developers remember to import a node, and then forget to append it! In other words, code crops up looking like this:

Element otherDocElement = otherDoc.getDocumentElement( );
Element thisDocElement = thisDoc.getDocumentElement( );

// Import the node into the right document
Element readyToUseElement = (Element)thisDoc.importNode(otherDocElement);

// The node never gets appended!!

In this case, you have an element that belongs to the target document, but that never gets appended, or prepended, to anything within the document. The result is another tough-to-find bug, in that the document owns the element but the element is not in the actual DOM tree. Output ends up being completely devoid of the imported node, which can be quite frustrating. Watch out!

Previous | Home | Next
6.4. DOM Level 3 | Book Index | 6.6. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

6.6. What's Next?

Well, you should be starting to feel like you're getting the hang of this XML thing. In the next chapter, I'll continue on the API trail by introducing you to JDOM, another API for accessing XML from Java. JDOM is similar to DOM (but is not DOM) in that it provides you a tree model of XML. I'll show you how it works, highlight when to use it, and cover the differences between the various XML APIs we've looked at so far. Don't get cocky yet; there's plenty more to learn!

Previous | Home | Next
6.5. Gotcha! | Book Index | 7. JDOM
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JDOM (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 7. JDOM

Contents:

The Basics
PropsToXML
XMLProperties
Is JDOM a Standard?
Gotcha!
What's Next?

JDOM provides a means of accessing an XML document within Java through a tree structure, and in that respect is somewhat similar to the DOM. However, it was built specifically for Java (remember the discussion on language bindings for the DOM?), so is in many ways more intuitive to a Java developer than DOM. I'll describe these aspects of JDOM throughout the chapter, as well as talk about specific cases to use SAX, DOM, or JDOM. And for the complete set of details on JDOM, you should check out the web site at http://www.jdom.org.

Additionally, and importantly, JDOM is an open source API. And because the API is still finalizing on a 1.0 version, it also remains flexible.[7] You have the ability to suggest and implement changes yourself. If you find that you like JDOM, except for one little annoying thing, you can help us investigate solutions to your problem. In this chapter, I'll cover JDOM's current status, particularly with regard to standardization, and the basics on using the API, and I'll give you some working examples.

[7]Because JDOM 1.0 is not final, some things may change between the publication of this book and your download. I'll try and keep a running list of changes on the JDOM web site (http://www.jdom.org) and work with O'Reilly to get these changes and updates available as quickly as possible.

Full Disclosure

In the interests of full disclosure, I should say that I am one of the co-creators of JDOM; my partner in crime on this particular endeavor is Jason Hunter, the noted author of Java Servlet Programming (O'Reilly). Jason and I had some issues with DOM, and during a long discussion at the 2000 O'Reilly Enterprise Java Conference, came up with JDOM. I also owe a great deal of credit to James Davidson (Sun Microsystems, servlet 2.2 specification lead, Ant author, etc.) and Pier Fumagalli (Apache/Jakarta/Cocoon superhero). Plus, the hundreds of good friends on the JDOM mailing lists.

All that to say that I'm partial to JDOM. So, if you sense some favoritism creeping through this chapter, I apologize; I use SAX, DOM, and JDOM often, but I happen to like one more than the others, because in my personal development, it has helped me out. Anyway, consider yourself forewarned!

7.1. The Basics

Chapter 5, "DOM" and Chapter 6, "Advanced DOM" should have given you a pretty good understanding of dealing with XML tree representations. So when I say that JDOM also provides a tree-based representation of an XML document, that gives you a starting point for understanding how JDOM behaves. To help you see how the classes in JDOM match up to XML structures, take a look at Figure 7-1, which shows a UML model of JDOM's core classes.

Figure 7-1. UML model of core JDOM classes

As you can see, the names of the classes tell the story. At the core of the JDOM structure is the Document object; it is both the representation of an XML document, and a container for all the other JDOM structures. Element represents an XML element, Attribute an attribute, and so on down the line. If you've immersed yourself in DOM, though, you might think there are some things missing from JDOM. For example, where's the Text class? As you recall, DOM follows a very strict tree model, and element content is actually considered a child node (or nodes) of an element node itself. In JDOM, this was seen as inconvenient in many cases, and the API provides getText( ) methods on the Element class. This allows the content of an element to be obtained from the element itself, and therefore there is no Text class. This was felt to provide a more intuitive approach for Java developers unfamiliar with XML, DOM, or some of the vagaries of trees.

7.1.1. Java Collections Support

Another important item to take note of is that you don't see any list classes like SAX's Attributes class or DOM's NodeList and NamedNodeMap classes. This is a nod to Java developers; it was felt that using Java Collections (java.util.List, java.util.Map, etc.) would provide a familiar and simple API for XML usage. DOM must serve across languages (remember Java language bindings in Chapter 5, "DOM"?), and can't take advantage of language-specific things like Java Collections. For example, when invoking the getAttributes( ) method on the Element class, you get back a List; you can of course operate upon this List just as you would any other Java List, without looking up new methods or syntax.

7.1.2. Concrete Classes and Factories

Another basic tenet of JDOM that is different from DOM, and not so visible, is that JDOM is an API of concrete classes. In other words, Element, Attribute, ProcessingInstruction, Comment, and the rest are all classes that can be directly instantiated using the new keyword. The advantage here is that factories are not needed, as factories can oftentimes be intrusive into code. Creating a new JDOM document would be done like this:

Element rootElement = new Element("root");
Document document = new Document(rootElement);

That simple. On the other hand, not using factories can also be seen as a disadvantage. While you can subclass JDOM classes, you would have to explicitly use those subclasses in your code:

element.addContent(new FooterElement("Copyright 2001"));

Here, FooterElement is a subclass of org.jdom.Element, and does some custom processing (it could, for example, build up several elements that display a page footer). Because it subclasses Element, it can be added to the element variable through the normal means, the addContent( ) method. However, there is no means to define an element subclass and specify that it should always be used for element instantiation, like this:

// This code does not work!!
JDOMFactory factory = new JDOMFactory( );
factory.setDocumentClass("javaxml2.BrettsDocumentClass");
factory.setElementClass("javaxml2.BrettsElementClass");

Element rootElement = JDOMFactory.createElement("root");
Document document = JDOMFactory.createDocument(rootElement);

The idea is that once the factory has been created, specific subclasses of JDOM structures can be specified as the class to use for those structures. Then, every time (for example) an Element is created through the factory, the javaxml2.BrettsElementClass is used instead of the default org.jdom.Element class.

Support for this as an option is growing, if not as a standard means of working with JDOM. That means that in the open source world, it's possible this functionality might be in place by the time you read this, or by the time JDOM is finalized in a 1.0 form. Stay tuned to http://www.jdom.org for the latest on these developments.

7.1.3. Input and Output

A final important aspect of JDOM is its input and output model. First, you should realize that JDOM is not a parser; it is an XML document representation in Java. In other words, like DOM and SAX, it is simply a set of classes that can be used to manipulate the data that a parser provides. As a result, JDOM must rely on a parser for reading raw XML.[8] It can also accept SAX events or a DOM tree as input, as well as JDBC ResultSet instances and more. To facilitate this, JDOM provides a package specifically for input, org.jdom.input. This package provides builder classes; the two you'll use most often are SAXBuilder and DOMBuilder . These build the core JDOM structure, a JDOM Document, from a set of SAX events or a DOM tree. As JDOM standardizes (see Section 7.4, "Is JDOM a Standard?" at the end of this chapter), it's also expected that direct support for JDOM will materialize in parser efforts like Apache Xerces and Sun's Crimson.

[8]By default, this parser is Xerces, which is included with JDOM. However, you can use any other XML parser with JDOM.

For dealing with input streams, files or documents on disk, or building from existing XML not in a DOM tree, SAXBuilder is the best solution. It's fast and efficient, just like SAX. Using the builder is a piece of cake:

SAXBuilder builder = new SAXBuilder( );
Document doc = builder.build(new FileInputStream("contents.xml"));

I'll detail this further in the code in the chapter, but you can see that it doesn't take much to get access to XML. If you already have your document in a DOM structure, you'll want to use DOMBuilder, which performs a fast conversion from one API to the other:

DOMBuilder builder = new DOMBuilder( );
Document doc = builder.build(myDomDocumentObject);

It's fairly self-explanatory. This essentially converts from an org.w3c.dom.Document to an org.jdom.Document. The process of converting from a JDOM document back to one of these structures is essentially the same, in reverse; the org.jdom.output package is used for these tasks. To move from JDOM structures to DOM ones, DOMOutputter is used:

DOMOutputter outputter = new DOMOutputter( );
org.w3c.dom.Document domDoc = outputter.output(myJDOMDocumentObject);

Taking a JDOM Document and firing off SAX events works in the same way:

SAXOutputter outputter = new SAXOutputter( );
outputter.setContentHandler(myContentHandler);
outputter.setErrorHandler(myErrorHandler);
outputter.output(myJDOMDocumentObject);

This works just like dealing with normal SAX events, where you register content handlers, error handlers, and the rest, and then fire events to those handlers from the JDOM Document object supplied to the output( ) method.

The final outputter, and the one you'll probably work with more than any other, is org.jdom.output.XMLOutputter. This outputs XML to a stream or writer, which wraps a network connection, a file, or any other structure you want to push XML to. This also is effectively a production-ready version of the DOMSerializer class from Chapter 5, "DOM", except of course it works with JDOM, not DOM. Using the XMLOutputter works like this:

XMLOutputter outputter = new XMLOutputter( );
outputter.output(jdomDocumentObject, new FileOutputStream("results.xml"));

So there you have it; the input and output of JDOM all in a few paragraphs. One last thing to note, as illustrated in Figure 7-2: it is very easy to "loop" things because all the input and output of JDOM is actually part of the API. In other words, you can use a file as input, work with it in JDOM, output it to SAX, DOM, or a file, and then consume that as input, restarting the loop. This is particularly helpful in messaging-based applications, or in cases where JDOM is used as a component between other XML supplying and consuming components.

Figure 7-2. Input and output loops in JDOM

This isn't a comprehensive look at JDOM, but it gives you enough information to get started, and I'd rather show you things within the context of working code anyway! So, let's take a look at a utility program that can convert Java properties files to XML.

Previous | Home | Next
6.6. What's Next? | Book Index | 7.2. PropsToXML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


PropsToXML (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

7.2. PropsToXML

To put some real code to the task of learning JDOM, let me introduce the PropsToXML class. This class is a utility that takes a standard Java properties file and converts it to an XML equivalent. Many developers out there have requested a means of doing exactly this; it often allows legacy applications using properties files to easily convert to using XML without the overhead of manually converting the configuration files.

7.2.1. Java Properties Files

If you have never worked with Java properties files, they are essentially files with name-value pairs that can be read easily with some Java classes (for instance, the java.util.Properties class). These files often look similar to Example 7-1, and in fact I'll use this example properties file throughout the rest of the chapter. Incidentally, it's from the Enhydra application server.

Example 7-1. A typical Java properties file

#
# Properties added to System properties
#

# sax parser implementing class
org.xml.sax.parser="org.apache.xerces.parsers.SAXParser"

#
# Properties used to start the server
#

# Class used to start the server
org.enhydra.initialclass=org.enhydra.multiServer.bootstrap.Bootstrap

# initial arguments passed to the server (replace command line args)
org.enhydra.initialargs="./bootstrap.conf"

# Classpath for the parent top enhydra classloader
org.enhydra.classpath="."

# separator for the classpath above
org.enhydra.classpath.separator=":"

No big deal here, right? Well, using an instance of the Java Properties class, you can load these properties into the object (using the load(InputStream inputStream) method) and then deal with them like a Hashtable. In fact, the Properties class extends the Hashtable class in Java; nice, huh? The problem is that many people write these files like the example with names separated by a period ( .) to form a sort of hierarchical structure. In the example, you would have a top level (the properties file itself), then the org node, and under it the xml and enhydra nodes, and under the enhydra node several nodes, some with values. You'd expect a structure like the one shown in Figure 7-3, in other words.

Figure 7-3. Expected structure of properties shown in Example 7-1

While this sounds good, Java provides no means of accessing the name-value pairs in this manner; it does not give the period any special value, but instead treats it as just another character. So while you can do this:

String classpathValue = Properties.getProperty("org.enhydra.classpath");

You cannot do this:

List enhydraProperties = Properties.getProperties("org.enhydra");

You would expect (or at least I do!) that the latter would work, and provide you all the subproperties with the structure org.enhydra (org.enhydra.classpath, org.enhydra.initialargs, etc.). Unfortunately, that's not part of the Properties class. For this reason, many developers have had to write their own little wrapper methods around this object, which of course is nonstandard and a bit of a pain. Wouldn't it be nice if this information could be modeled in XML, where operations like the second example are simple? That's exactly what I want to write code to do, and I'll use JDOM to demonstrate that API.

7.2.2. Converting to XML

As in previous chapters, it's easiest to start with a skeleton for the class and build out. For the PropsToXML class, I want to allow a properties file to be supplied for input, and the name of a file for the XML output. The class reads in the properties file, converts it to an XML document using JDOM, and outputs it to the specified filename. Example 7-2 starts the ball rolling.

Example 7-2. The skeleton of the PropsToXML class

package javaxml2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

public class PropsToXML {

    /**
     * <p> This will take the supplied properties file, and
     *   convert that file to an XML representation, which is
     *   then output to the supplied XML document filename. </p>
     *
     * @param propertiesFilename file to read in as Java properties.
     * @param xmlFilename file to output XML representation to.
     * @throws <code>IOException</code> - when errors occur.
     */
    public void convert(String propertiesFilename, String xmlFilename)
        throws IOException {

        // Get Java Properties object
        FileInputStream input = new FileInputStream(propertiesFilename);
        Properties props = new Properties( );
        props.load(input);

        // Convert to XML
        convertToXML(props, xmlFilename);
    }

    /**
     * <p> This will handle the detail of conversion from a Java
     *  <code>Properties</code> object to an XML document. </p>
     *
     * @param props <code>Properties</code> object to use as input.
     * @param xmlFilename file to output XML to.
     * @throws <code>IOException</code> - when errors occur.
     */
    private void convertToXML(Properties props, String xmlFilename)
        throws IOException {

        // JDOM conversion code goes here
    }

    /**
     * <p> Provide a static entry point for running. </p>
     */
    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println("Usage: java javaxml2.PropsToXML " +
                "[properties file] [XML file for output]");
            System.exit(0);
        }

        try {
            PropsToXML propsToXML = new PropsToXML( );
            propsToXML.convert(args[0], args[1]);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

The only new part of this code is the Java Properties object, which I've mentioned briefly. The supplied properties filename is used in the load( ) method, and that object is delegated on to a method that will use JDOM, which I'll focus on next.

7.2.3. Creating XML with JDOM

Once the code has the properties in a (more) usable form, it's time to start using JDOM. The first task is to create a JDOM Document. For that to occur, you need to create a root element for the document, using JDOM's Element class. Since an XML document can't exist without a root element, an instance of the Element class is required as input for the Document class constructor.

Creating an Element requires only the passing of the element's name. There are alternate versions that take in namespace information, and I'll discuss those a little later. For now, it's easiest to use the root element's name, and since this needs to be a top-level, arbitrary name (to contain all the property nestings), I use "properties" in the code. Once this element is created, it's used to create a new JDOM Document.

Then, it's on to dealing with the properties in the supplied file. The list of property names is obtained as a Java Enumeration through the Properties object's propertyNames( ) method. Once that name is available, it can be used to obtain the property value by using the getProperty( ) method. At this point, you've got the root element of the new XML document, the property name to add, and the value for that property. And then, like any other good program, you iterate through all of the other properties until finished. At each step, this information is supplied to a new method, createXMLRepresentation( ) . This performs the logic for handling conversion of a single property into a set of XML elements. Add this code, as shown here, to your source file:

    private void convertToXML(Properties props, String xmlFilename)
        throws IOException {

        // Create a new JDOM Document with a root element "properties"
        Element root = new Element("properties");
        Document doc = new Document(root);

        // Get the property names
        Enumeration propertyNames = props.propertyNames( );
        while (propertyNames.hasMoreElements( )) {
            String propertyName = (String)propertyNames.nextElement( );
            String propertyValue = props.getProperty(propertyName);
            createXMLRepresentation(root, propertyName, propertyValue);
        }

        // Output document to supplied filename
        XMLOutputter outputter = new XMLOutputter("  ", true);
        FileOutputStream output = new FileOutputStream(xmlFilename);
        outputter.output(doc, output);
    }

Don't worry about the last few lines that output the JDOM Document yet. I'll deal with this in the next section, but first I want to cover the createXML-Representation( ) method, which contains the logic for dealing with a single property, and creating XML from it.

The easiest (and logically, the first) step in moving from a property to XML is to take the name of the property and create an Element with that name. You've already seen how to do this; simply pass the name of the element to its constructor. Once the element is created, assign the value of the property as the textual content of the element. This can be done easily enough through the setText( ) method, which of course takes a String. Once the element is ready for use, it can be added as a child of the root element through the addContent( ) method. In fact, any legal JDOM construct can be passed to an element's addContent( ) method, as it is overloaded to accept these various types. These include instances of a JDOM Entity, Comment, ProcessingInstruction, and more. But I'll get to those later; for now, add the following method into your source file:

    /**
     * <p> This will convert a single property and its value to
     *  an XML element and textual value. </p>
     *
     * @param root JDOM root <code>Element</code> to add children to.
     * @param propertyName name to base element creation on.
     * @param propertyValue value to use for property.
     */
    private void createXMLRepresentation(Element root,
                                         String propertyName,
                                         String propertyValue) {

        Element element = new Element(propertyName);
        element.setText(propertyValue);
        root.addContent(element);
    }

At this point, you can actually compile the source file, and then use the resulting PropsToXML class. Supply a properties file (you can type in or download the enhydra.properties file shown earlier in this chapter), as well as an output filename, as shown here:[9]

[9]If you're not familiar with *NIX, the backslash at the end of each line ( \) simply allows for continuation of the command on the next line; Windows users should enter the entire command on one line.

/javaxml2/build $ java javaxml2.PropsToXML \
                  /javaxml2/ch07/properties/enhydra.properties \
                  enhydraProps.xml

This whirs along for a fraction of a second, and then generates an enhydraProps.xml file. Open this up; it should look like Example 7-3.[10]

[10]Note that the line wraps in the example are for publishing purposes only; in your document, each property with opening tag, text, and closing tag should be on its own line.

Example 7-3. First version of the enhydraProps.xml document

<?xml version="1.0" encoding="UTF-8"?>
<properties>
  <org.enhydra.classpath.separator>":"</org.enhydra.classpath.separator>
  <org.enhydra.initialargs>"./bootstrap.conf"</org.enhydra.initialargs>
  <org.enhydra.initialclass>org.enhydra.multiServer.bootstrap.Bootstrap
</org.enhydra.initialclass>
  <org.enhydra.classpath>"."</org.enhydra.classpath>
  <org.xml.sax.parser>"org.apache.xerces.parsers.SAXParser"
</org.xml.sax.parser>
</properties>

In about 50 lines of code, you've gone from Java properties to XML. However, this XML document isn't much better than the properties file; there is still no way to relate the org.enhydra.initialArgs property to the org.enhydra.classpath property. Our job isn't done yet.

Instead of using the property name as the element name, the code needs to take the property name and split it on the period delimiters. For each of these "sub-names," an element needs to be created and added to the element stack. Then the process can repeat. For the property name org.xml.sax, the following XML structure should result:

<org>
  <xml>
    <sax>[property Value]</sax>
  </xml>
</org>

At each step, using the Element constructor and the addContent( ) method does the trick; and once the name is completely deconstructed, the setText( ) method can be used to set the last element's textual value. The best way is to create a new Element, called current, and use it is a "pointer" (there aren't any pointers in Java -- it's just a term); it will always point at the element that content should be added to. At each step, the code also needs to see if the element to be added already exists. For example, the first property, org.xml.sax, creates an org element. When the next property is added (org.enhydra.classpath), the org element does not need to be created again.

To facilitate this, the getChild( ) method is used. This method takes the name of the child element to retrieve, and is available to all instances of the Element class. If the child specified exists, that element is returned. However, if no child exists, a null value is returned, and it is on this null value that our code can key. In other words, if the return value is an element, that becomes the current element, and no new element needs to be created (it already exists). However, if the return from the getChild( ) call is null, a new element must be created with the current sub-name, added as content to the current element, and then the current pointer is moved down the tree. Finally, once the iteration is over, the textual value of the property can be added to the leaf element, which turns out to be (nicely) the element that the current pointer references. Add this code to your source file:

    private void createXMLRepresentation(Element root,
                                         String propertyName,
                                         String propertyValue) {

        /*
        Element element = new Element(propertyName);
        element.setText(propertyValue);
        root.addContent(element);
        */

        int split;
        String name = propertyName;
        Element current = root;
        Element test = null;

        while ((split = name.indexOf(".")) != -1) {
            String subName = name.substring(0, split);
            name = name.substring(split+1);

            // Check for existing element
            if ((test = current.getChild(subName)) == null) {
                Element subElement = new Element(subName);
                current.addContent(subElement);
                current = subElement;
            } else {
                current = test;
            }
        }

        // When out of loop, what's left is the final element's name
        Element last = new Element(name);
        last.setText(propertyValue);
        current.addContent(last);
    }

With this addition in place, recompile the program and run it again. This time, your output should be a lot nicer, as shown in Example 7-4.

Example 7-4. Updated output from PropsToXML

<?xml version="1.0" encoding="UTF-8"?>
<properties>
  <org>
    <enhydra>
      <classpath>
        <separator>":"</separator>
      </classpath>
      <initialargs>"./bootstrap.conf"</initialargs>
      <initialclass>org.enhydra.multiServer.bootstrap.Bootstrap</initialclass>
      <classpath>"."</classpath>
    </enhydra>
    <xml>
      <sax>
        <parser>"org.apache.xerces.parsers.SAXParser"</parser>
      </sax>
    </xml>
  </org>
</properties>

And, just as quickly as you've started in on JDOM, you've got the hang of it. However, you might notice that the XML document violates one of the rules of thumb for document design introduced in Chapter 2, "Nuts and Bolts" (in the section detailing usage of elements versus usage of attributes). You see, each property value has a single textual value. That arguably makes the property values suitable as attributes of the last element on the stack, rather than content. Proving that rules are meant to be broken, I prefer them as content in this case, but that's neither here nor there.

For no other reason than demonstration purposes, let's look at converting the property values to attributes rather than textual content. This turns out to be quite easy, and can be done in one of two ways. The first is to create an instance of the JDOM Attribute class. The constructor for that class takes the name of the attribute and its value. Then, the resulting instance can be added to the leaf element with that element's setAttribute( ) method. That approach is shown here:

        // When out of loop, what's left is the final element's name
        Element last = new Element(name);
        /* last.setText(propertyValue);  */
        Attribute attribute = new Attribute("value", propertyValue);
        current.setAttribute(attribute);
        current.addContent(last);
WARNING: If you want to compile the file with these changes, be sure you add an import statement for the Attribute class:

import org.jdom.Attribute;

A slightly easier way is to use one of the convenience methods that JDOM offers. Since adding attributes is such a common task, the Element class provides an overloaded version of setAttribute( ) that takes a name and value, and internally creates an Attribute object. In this case, that approach is a little clearer:

        // When out of loop, what's left is the final element's name
        Element last = new Element(name);
        /* last.setText(propertyValue); */
        last.setAttribute("value", propertyValue);
        current.addContent(last);

This works just as well, but also avoids having to use an extra import statement. You can compile this change in and run the sample program. The new output should match Example 7-5.

Example 7-5. Output of PropsToXML using attributes

<?xml version="1.0" encoding="UTF-8"?>
<properties>
  <org>
    <enhydra>
      <classpath>
        <separator value="&quot;:&quot;" />
      </classpath>
      <initialargs value="&quot;./bootstrap.conf&quot;" />
      <initialclass value="org.enhydra.multiServer.bootstrap.Bootstrap" />
      <classpath value="&quot;.&quot;" />
    </enhydra>
    <xml>
      <sax>
        <parser value="&quot;org.apache.xerces.parsers.SAXParser&quot;" />
      </sax>
    </xml>
  </org>
</properties>

Each property value is now an attribute of the innermost element. Notice that JDOM converts the quotation marks within the attribute values, which are disallowed, to entity references so the document as output is well-formed. However, this makes the output a little less clean, so you may want to switch your code back to using textual data within elements, rather than attributes.

7.2.4. Outputting XML with JDOM

Before we continue, I want to spend a little time talking about the output portion of the code that I skimmed over earlier in the chapter. It's highlighted again here:

    private void convertToXML(Properties props, String xmlFilename)
        throws IOException {

        // Create a new JDOM Document with a root element "properties"
        Element root = new Element("properties");
        Document doc = new Document(root);

        // Get the property names
        Enumeration propertyNames = props.propertyNames( );
        while (propertyNames.hasMoreElements( )) {
            String propertyName = (String)propertyNames.nextElement( );
            String propertyValue = props.getProperty(propertyName);
            createXMLRepresentation(root, propertyName, propertyValue);
        }

        // Output document to supplied filename
        XMLOutputter outputter = new XMLOutputter("  ", true);
        FileOutputStream output = new FileOutputStream(xmlFilename);
        outputter.output(doc, output);
    }

You already know that XMLOutputter is the class to use for handling output to a file, stream, or other static representation. However, I supplied some arguments to the constructor in the code sample; without any arguments, the outputter would perform direct output. There would be no change to the XML used as input. When reading in XML, this most often results in no line breaks and no indentation. The resultant document would have the entire document, except for the XML declaration, on a single line. I'd show you this, but it wouldn't fit on the page, and tends to cause confusion. The outputter has several constructors though:

public XMLOutputter( );

public XMLOutputter(String indent);

public XMLOutputter(String indent, boolean newlines);

public XMLOutputter(String indent, boolean newlines, String encoding);

public XMLOutputter(XMLOutputter that);

Most of these are self-explanatory. The indent parameter allows specification of how many spaces to use for indentation; I used two spaces (" ") in the sample code. The boolean value for newlines determines if line breaks are used (this was on in the sample). If needed, an encoding parameter can be specified, which becomes the value for encoding in the XML declaration:

<?xml version="1.0" encoding="UTF-8"?>

Additionally, there are mutator methods for all of these properties (setIndent( ), setEncoding( ), etc.) in the class. There are also versions of the output( ) method (the one used in the example code) that take either an OutputStream or a Writer. And there are versions that take the various JDOM constructs as input, so you could output an entire Document, or just an Element, Comment, ProcessingInstruction, or anything else:

// Create an outputter with 4 space indentation and new lines
XMLOutputter outputter = new XMLOutputter("    ", true);

// Output different JDOM constructs
outputter.output(myDocument, myOutputStream);
outputter.output(myElement, myWriter);
outputter.output(myComment, myOutputStream);
// etc...

In other words, XMLOutputter serves all of your XML output needs. Of course, you can also use DOMOutputter and SAXOutputter, which I'll cover in detail in the next chapter.

Previous | Home | Next
7. JDOM | Book Index | 7.3. XMLProperties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XMLProperties (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

7.3. XMLProperties

Let's take things to the next logical step, and look at reading XML. Continuing with the example of converting a properties file to XML, you are now probably wondering how to access the information in your XML file. Luckily, there's a solution for that, too! In this section, for the sake of explaining how JDOM reads XML, I want to introduce a new utility class, XMLProperties. This class is essentially an XML-aware version of the Java Properties class; in fact, it extends that class. This class allows access to an XML document through the typical property-access methods like getProperty( ) and properties( ); in other words, it allows Java-style access (using the Properties class) to XML-style storage. In my opinion, this is the best combination you can get.

To accomplish this task, you can start by creating an XMLProperties class that extends the java.util.Properties class. With this approach, making things work becomes simply a matter of overriding the load( ), save( ), and store( ) methods. The first of these, load( ) , reads in an XML document and loads the properties within that document into the superclass object.

WARNING: Don't mistake this class for an all-purpose XML-to-properties converter; it only will read in XML that is in the format detailed earlier in this chapter. In other words, properties are elements with either textual or attribute values but not both; I'll cover both approaches, but you'll have to choose one or the other. Don't try to take all your XML documents, read them in, and expect things to work as planned!

The second method, save( ) , is actually deprecated in Java 2, as it doesn't expose any error information; still, it needs to be overridden for Java 1.1 users. To facilitate this, the implementation in XMLProperties simply calls store( ) . And store( ) handles the task of writing the properties information out to an XML document. Example 7-6 is a good start at this, and provides a skeleton within which to work.

Example 7-6. The skeleton of the XMLProperties class

package javaxml2;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.jdom.Attribute;
import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

public class XMLProperties extends Properties {

    public void load(Reader reader)
        throws IOException {

        // Read XML document into a Properties object
    }

    public void load(InputStream inputStream)
        throws IOException {

        load(new InputStreamReader(inputStream));
    }

    public void load(File xmlDocument)
        throws IOException {

        load(new FileReader(xmlDocument));
    }

    public void save(OutputStream out, String header) {
        try {
            store(out, header);
        } catch (IOException ignored) {
            // Deprecated version doesn't pass errors
        }
    }

    public void store(Writer writer, String header)
        throws IOException {

        // Convert properties to XML and output
    }

    public void store(OutputStream out, String header)
        throws IOException {

        store(new OutputStreamWriter(out), header);
    }

    public void store(File xmlDocument, String header)
        throws IOException {

        store(new FileWriter(xmlDocument), header);
    }
}

Take note that I overloaded the load( ) and store( ) methods; while the Properties class only has versions that take an InputStream and OutputStream (respectively), I'm a firm believer in providing users options. The extra versions, which take Files and Readers/Writers, make it easier for users to interact, and add a marginal amount of code to the class. Additionally, these overloaded methods can all delegate to existing methods, which leaves the code ready for loading and storing implementation.

7.3.1. Storing XML

I'll deal with storing XML first, mainly because the code is already written. The logic to take a Properties object and output it as XML is the purpose of the PropsToXML class, and I'll simply reuse some of that code here to make things work nicely:

    public void store(Writer writer, String header)
        throws IOException {

        // Create a new JDOM Document with a root element "properties"
        Element root = new Element("properties");
        Document doc = new Document(root);

        // Get the property names
        Enumeration propertyNames = propertyNames( );
        while (propertyNames.hasMoreElements( )) {
            String propertyName = (String)propertyNames.nextElement( );
            String propertyValue = getProperty(propertyName);
            createXMLRepresentation(root, propertyName, propertyValue);
        }

        // Output document to supplied filename
        XMLOutputter outputter = new XMLOutputter("  ", true);
        outputter.output(doc, writer);
    }

    private void createXMLRepresentation(Element root,
                                         String propertyName,
                                         String propertyValue) {

        int split;
        String name = propertyName;
        Element current = root;
        Element test = null;

        while ((split = name.indexOf(".")) != -1) {
            String subName = name.substring(0, split);
            name = name.substring(split+1);

            // Check for existing element
            if ((test = current.getChild(subName)) == null) {
                Element subElement = new Element(subName);
                current.addContent(subElement);
                current = subElement;
            } else {
                current = test;
            }
        }

        // When out of loop, what's left is the final element's name
        Element last = new Element(name);
        last.setText(propertyValue);
        /** Uncomment this for Attribute usage */
        /*
        last.setAttribute("value", propertyValue);
        */
        current.addContent(last);
    }

Not much needs comment. There are a few lines of code highlighted to illustrate some changes, though. The first two changes ensure that the superclass is used to obtain the property names and values, rather than the Properties object that was passed into the version of this method in PropsToXML. The third change moves from using a string filename to the supplied Writer for output. With those few modifications, you're all set to compile the XMLProperties source file.

There is one item missing, though. Note that the store( ) method allows specification of a header variable; in a standard Java properties file, this is added as a comment to the head of the file. To keep things parallel, the XMLProperties class can be modified to do the same thing. You will need to use the Comment class to do this. The following code additions put this change into effect:

    public void store(Writer writer, String header)
        throws IOException {

        // Create a new JDOM Document with a root element "properties"
        Element root = new Element("properties");
        Document doc = new Document(root);

        // Add in header information
        Comment comment = new Comment(header);
        doc.addContent(comment);

        // Get the property names
        Enumeration propertyNames = propertyNames( );
        while (propertyNames.hasMoreElements( )) {
            String propertyName = (String)propertyNames.nextElement( );
            String propertyValue = getProperty(propertyName);
            createXMLRepresentation(root, propertyName, propertyValue);
        }

        // Output document to supplied filename
        XMLOutputter outputter = new XMLOutputter("  ", true);
        outputter.output(doc, writer);
    }

The addContent( ) method of the Document object is overloaded to take both Comment and ProcessingInstruction objects, and appends the content to the file. It's used here to add in the header parameter as a comment to the XML document being written to.

7.3.2. Loading XML

There's not much left to do here; basically, the class writes out to XML, provides access to XML (through the methods already existing on the Properties class), and now simply needs to read in XML. This is a fairly simple task; it boils down to more recursion. I'll show you the code modifications needed, and then walk you through them. Enter the code shown here into your XMLProperties.java source file:

    public void load(Reader reader)
        throws IOException {

        try {
            // Load XML into JDOM Document
            SAXBuilder builder = new SAXBuilder( );
            Document doc = builder.build(reader);

            // Turn into properties objects
            loadFromElements(doc.getRootElement().getChildren( ),
                new StringBuffer(""));

        } catch (JDOMException e) {
            throw new IOException(e.getMessage( ));
        }
    }

    private void loadFromElements(List elements, StringBuffer baseName) {
        // Iterate through each element
        for (Iterator i = elements.iterator(); i.hasNext( ); ) {
            Element current = (Element)i.next( );
            String name = current.getName( );
            String text = current.getTextTrim( );

            // Don't add "." if no baseName
            if (baseName.length( ) > 0) {
                baseName.append(".");
            }
            baseName.append(name);

            // See if we have an element value
            if ((text == null) || (text.equals(""))) {
                // If no text, recurse on children
                loadFromElements(current.getChildren( ),
                                 baseName);
            } else {
                // If text, this is a property
                setProperty(baseName.toString( ),
                            text);
            }

            // On unwind from recursion, remove last name
            if (baseName.length() == name.length( )) {
                baseName.setLength(0);
            } else {
                baseName.setLength(baseName.length( ) -
                    (name.length( ) + 1));
            }
        }
    }

The implementation of the load( ) method (which all overloaded versions delegate to) uses SAXBuilder to read in the supplied XML document. I discussed this earlier in the chapter, and I'll look at it in even more detail in the next; for now, it's enough to realize that it simply reads XML into a JDOM Document object.

The name for a property consists of the names of all the elements leading to the property value, with a period separating each name. Here's a sample property in XML:

<properties>
  <org>
    <enhydra>
      <classpath>"."</classpath>
    </enhydra>
  </org>
</properties>

The property name can be obtained by taking the element names leading to the value (excluding the properties element, which was used as a root-level container): org, enhydra, and classpath. Throw a period between each, and you get org.enhydra.classpath, which is the property name in question. To accomplish this, I coded up the loadFromElements( ) method. This takes in a list of elements, iterates through them, and deals with each element individually. If the element has a textual value, that value is added to the superclass object's properties. If it has child elements instead, then the children are obtained, and recursion begins again on the new list of children. At each step of recursion, the name of the element being dealt with is appended to the baseName variable, which keeps track of the property names. Winding through recursion, baseName would be org, then org.enhydra, then org.enhydra.classpath. And, as recursion unwinds, the baseName variable is shortened to remove the last element name. Let's look at the JDOM method calls that make it possible.

First, you'll notice several invocations of the getChildren( ) method on instances of the Element class. This method returns all child elements of the current element as a Java List. There are versions of this method that also take in the name of an element to search for, and return either all elements with that name (getChildren(String name)), or just the first child element with that name (getChild(String name)). There are also namespace-aware versions of the method, which will be covered in the next chapter. To start the recursion process, the root element is obtained from the JDOM Document object through the getRootElement( ) method, and then its children are used to seed recursion. Once in the loadFromElements( ) method, standard Java classes are used to move through the list of elements (such as java.util.Iterator). To check for textual content, the getTextTrim( ) method is used. This method returns the textual content of an element, and returns the element without surrounding whitespace.[11] Thus, the content " textual content " (note the surrounding whitespace) would be returned as "textual content". While this seems somewhat trivial, consider this more realistic example of XML:

[11]It also removes more than one space between words. The textual content "lots of spaces" would be returned through getTextTrim( ) as "lots of spaces".

<chapter>
  <title>
    Advanced SAX
  </title>
</chapter>

The actual textual content of the title element turns out to be several spaces, followed by a line feed, followed by more space, the characters "Advanced SAX", more space, another line feed, and even more space. In other words, probably not what you expected. The returned string data from a call to getTextTrim( ) would simply be "Advanced SAX", which is what you want in most cases anyway. However, if you do want the complete content (often used for reproducing the input document exactly as it came in), you can use the getText( ) method, which returns the element's content unchanged. If there is no content, the return value from this method is an empty string (""), which makes for an easy comparison, as shown in the example code. And that's about it: a few simple method calls and the code is reading XML with JDOM. Let's see this class in action.

7.3.3. Taking a Test Drive

Once you've got everything in place in the XMLProperties class, compile it. To test it out, you can enter in or download Example 7-7, which is a class that uses the XMLProperties class to load an XML document, print some information out about it, and then write the properties back out as XML.

Example 7-7. Testing the XMLProperties class

package javaxml2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Enumeration;

public class TestXMLProperties {

    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println("Usage: java javaxml2.TestXMLProperties " +
                "[XML input document] [XML output document]");
            System.exit(0);
        }

        try {
            // Create and load properties
            System.out.println("Reading XML properties from " + args[0]);
            XMLProperties props = new XMLProperties( );
            props.load(new FileInputStream(args[0]));

            // Print out properties and values
            System.out.println("\n\n---- Property Values ----");
            Enumeration names = props.propertyNames( );
            while (names.hasMoreElements( )) {
                String name = (String)names.nextElement( );
                String value = props.getProperty(name);
                System.out.println("Property Name: " + name +
                                   " has value " + value);
            }

            // Store properties
            System.out.println("\n\nWriting XML properies to " + args[1]);
            props.store(new FileOutputStream(args[1]),
                "Testing XMLProperties class");
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

This doesn't do much; it reads in properties, uses them to print out all the property names and values, and then writes those properties back out -- but all in XML. You can run this program on the XML file generated by the PropsToXML class I showed you earlier in the chapter.

WARNING: The version of XMLProperties used here deals with property values as textual content of elements (the first version of PropsToXML shown), not as attribute values (the second version of PropsToXML). You'll need to use that earlier version of PropsToXML, or back out your changes, if you are going to use it to generate XML for input to the TestXMLProperties class. Otherwise, you won't pick up any property values with this code.

Supply the test program with the XML input file and the name of the output file:

C:\javaxml2\build>java javaxml2.TestXMLProperties enhydraProps.xml output.xml
Reading XML properties from enhydraProps.xml

---- Property Values ----
Property Name: org.enhydra.classpath.separator has value ":"
Property Name: org.enhydra.initialargs has value "./bootstrap.conf"
Property Name: org.enhydra.initialclass has value
  org.enhydra.multiServer.bootstrap.Bootstrap
Property Name: org.enhydra.classpath has value "."
Property Name: org.xml.sax.parser has value
  "org.apache.xerces.parsers.SAXParser"

Writing XML properties to output.xml

And there you have it: XML data formatting, properties behavior.

7.3.4. Backtracking

Before wrapping up on the code, there are a few items I want to address. First, take a look at the XML file generated by TestXMLProperties , the result of invoking store( ) on the properties. It should look similar to Example 7-8 if you used the XML version of enhydra.properties detailed earlier in this chapter.

Example 7-8. Output from TestXMLProperties

<?xml version="1.0" encoding="UTF-8"?>
<properties>
  <org>
    <enhydra>
      <classpath>
        <separator>":"</separator>
      </classpath>
      <initialargs>"./bootstrap.conf"</initialargs>
      <initialclass>org.enhydra.multiServer.bootstrap.Bootstrap</initialclass>
      <classpath>"."</classpath>
    </enhydra>
    <xml>
      <sax>
        <parser>"org.apache.xerces.parsers.SAXParser"</parser>
      </sax>
    </xml>
  </org>
</properties>
<!--Testing XMLProperties class-->

Notice anything wrong? The header comment is in the wrong place. Take another look at the code that added in that comment, from the store( ) method:

        // Create a new JDOM Document with a root element "properties"
        Element root = new Element("properties");
        Document doc = new Document(root);

        // Add in header information
        Comment comment = new Comment(header);
        doc.addContent(comment);

The root element appears before the comment because it is added to the Document object first. However, the Document object can't be created without supplying a root element -- a bit of a chicken-or-egg situation. To work with this, you need to use a new method, getContent( ) . This method returns a List, but that List contains all the content of the Document, including comments, the root element, and processing instructions. Then, you can prepend the comment to this list, as shown here, using methods of the List class:

        // Add in header information
        Comment comment = new Comment(header);
        doc.getContent( ).add(0, comment);

With this change in place, your output will look as it should:

<?xml version="1.0" encoding="UTF-8"?>
<!--Testing XMLProperties class-->
<properties>
  <org>
    <enhydra>
      <classpath>
        <separator>":"</separator>
      </classpath>
      <initialargs>"./bootstrap.conf"</initialargs>
      <initialclass>org.enhydra.multiServer.bootstrap.Bootstrap</initialclass>
      <classpath>"."</classpath>
    </enhydra>
    <xml>
      <sax>
        <parser>"org.apache.xerces.parsers.SAXParser"</parser>
      </sax>
    </xml>
  </org>
</properties>

The getContent( ) method is also available on the Element class, and returns all content of the element, regardless of type (elements, processing instructions, comments, entities, and Strings for textual content).

Also important are the modifications necessary for XMLProperties to use attributes for property values, instead of element content. You've already seen the code change needed in storage of properties (in fact, the change is commented out in the source code, so you don't need to write anything new). As for loading, the change involves checking for an attribute instead of an element's textual content. This can be done with the getAttributeValue(String name) method, which returns the value of the named attribute, or null if no value exists. The change is shown here:

    private void loadFromElements(List elements, StringBuffer baseName) {
        // Iterate through each element
        for (Iterator i = elements.iterator(); i.hasNext( ); ) {
            Element current = (Element)i.next( );
            String name = current.getName( );
            // String text = current.getTextTrim( );
            String text = current.getAttributeValue("value");

            // Don't add "." if no baseName
            if (baseName.length( ) > 0) {
                baseName.append(".");
            }
            baseName.append(name);

            // See if we have an attribute value
            if ((text == null) || (text.equals(""))) {
                // If no text, recurse on children
                loadFromElements(current.getChildren( ),
                                 baseName);
            } else {
                // If text, this is a property
                setProperty(baseName.toString( ),
                            text);
            }

            // On unwind from recursion, remove last name
            if (baseName.length() == name.length( )) {
                baseName.setLength(0);
            } else {
                baseName.setLength(baseName.length( ) -
                    (name.length( ) + 1));
            }
        }
    }

Compile in the changes, and you're set to deal with attribute values instead of element content. Leave the code in the state you prefer it (as I mentioned earlier, I actually like the values as element content), so if you want textual element content, be sure to back out these changes after seeing how they affect output. Whichever you prefer, hopefully you are starting to know your way around JDOM. And just like SAX and DOM, I highly recommend bookmarking the Javadoc (either locally or online) as a quick reference for those methods you just can't quite remember. In any case, before wrapping up, let's talk a little about a common issue with regard to JDOM: standardization.

Previous | Home | Next
7.2. PropsToXML | Book Index | 7.4. Is JDOM a Standard?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Is JDOM a Standard? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

7.4. Is JDOM a Standard?

More than any other question about JDOM, I am asked whether JDOM is a standard. This is a common question, especially among either those who want to use JDOM (and need justification) or those who don't like JDOM (and need justification). I address some of those issues in this section, so that whatever camp you are in, you know the details about JDOM and its standardization process.

7.4.1. JDOM as a JSR

First and foremost, JDOM is now an official JSR, which is a Java Specification Request. In other words, it is going through the formal standardization process, sponsored by Sun and governed by the JCP (the Java Community Process). You can read all about the JSR and JCP processes at http://java.sun.com/aboutJava/communityprocess/. As for JDOM, it is now officially JSR-102 and can be found online at Sun's web site, located at http://java.sun.com/aboutJava/communityprocess/jsr/jsr_102_jdom.html.

Once JDOM moves through the JCP, probably in late 2001, several things will happen. First, it will receive a much more elevated status in terms of standards; although the JCP and Sun aren't perfect, they do offer a lot of credence. The JCP has support and members within IBM, BEA, Compaq, HP, Apache, and more. Additionally, it will become very easy to move JDOM into other Java standards. For example, there is interest from Sun in making JDOM part of the next version of JAXP, either 1.2 or 2.0 (I talk more about JAXP in Chapter 9, "JAXP"). Finally, future versions of the JDK are slated to have XML as part of their core; in years to come, JDOM may be in every download of Java.

7.4.2. SAX and DOM as Standards

Keep in mind that JDOM isn't getting some sort of elevated status; DOM and SAX are already both a part of JAXP, and so are actually ahead of JDOM in that regard. However, it's worth making some comments about the "standardization" of DOM and SAX. First, SAX came out of the public domain, and remains today a de facto standard. Developed primarily on the XML-dev mailing list, no standards body ratified or accepted SAX until it was already in heavy use. While I am by no means criticizing SAX, I am wary of folks who claim that JDOM shouldn't be used because it wasn't developed by a standards body.

On the other hand, DOM was developed by the W3C, and is a formal standard. For that reason, it has a staunch following. DOM is a great solution for many applications. Again, though, the W3C is simply one standards body; the JCP is another, the IETF is yet another, and so on. I'm not arguing the merits of any particular group; I just caution you about accepting any standard (JDOM or otherwise) if it doesn't meet your application's needs. Arguments about "standardization" take a backseat to usability. If you like DOM and it serves your needs, then use it. The same goes for SAX and JDOM. What I would prefer that everybody do, though, is stop trying to make decisions for everyone else (and I know I'm defending my API, but I get this sort of thing all the time!). Hopefully, this book takes you deeply enough into all three APIs to help you make an educated decision.

Previous | Home | Next
7.3. XMLProperties | Book Index | 7.5. Gotcha!
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Gotcha! (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

7.5. Gotcha!

Not to disappoint, I want to warn you of some common JDOM pitfalls. I hope this will save you a little time in your JDOM programming.

7.5.1. JDOM isn't DOM

First and foremost, you should realize that JDOM isn't DOM. It doesn't wrap DOM, and doesn't provide extensions to DOM. In other words, the two have no technical relation to each other. Realizing this basic truth will save you a lot of time and effort; there are many articles out there today that talk about getting the DOM interfaces to use JDOM, or avoiding JDOM because it hides some of DOM's methods. These statements confuse more people than almost anything else. You don't need to have the DOM interfaces, and DOM calls (like appendChild( ) or createDocument( )) simply won't work on JDOM. Sorry, wrong API!

7.5.2. Null Return Values

Another interesting facet of JDOM, and one that has raised some controversy, is the return values from methods that retrieve element content. For example, the various getChild( ) methods on the Element class may return a null value. I mentioned this, and demonstrated it, in the PropsToXML example code. The gotcha occurs when instead of checking if an element exists (as was the case in the example code), you assume that an element already exists. This is most common when some other application or component sends you XML, and your code expects it to conform to a certain format (be it a DTD, XML Schema, or simply an agreed-upon standard). For example, take a look at the following code:

Document doc = otherComponent.getDocument( );
String price = doc.getRootElement( ).getChild("item")
                                   .getChild("price")
                                   .getTextTrim( );

The problem in this code is that if there is no item element under the root, or no price element under that, a null value is returned from the getChild( ) method invocations. Suddenly, this innocuous-looking code begins to emit NullPointerExceptions, which are quite painful to track down. You can handle this situation in one of two ways. The first is to check for null values at each step of the way:

Document doc = otherComponent.getDocument( );
Element root = doc.getRootElement( );
Element item = root.getChild("item");
if (item != null) {
    Element price = item.getChild("price");
    if (price != null) {
        String price = price.getTextTrim( );
    } else {
        // Handle exceptional condition
    }
} else {
    // Handle exceptional condition
}

The second option is to wrap the entire code fragment in a try/catch block:

Document doc = otherComponent.getDocument( );
try {
    String price = doc.getRootElement( ).getChild("item")
                                       .getChild("price")
                                       .getTextTrim( );
} catch (NullPointerException e) {
    // Handle exceptional condition
}

While either approach works, I recommend the first; it allows finer-grained error handling, as it is possible to determine exactly which test failed, and therefore exactly what problem occurred. The second code fragment informs you only that somewhere a problem occurred. In any case, careful testing of return values can save you some rather annoying NullPointerExceptions.

7.5.3. DOMBuilder

Last but not least, you should be very careful when working with the DOMBuilder class. It's not how you use the class, but when you use it. As I mentioned, this class works for input in a similar fashion to SAXBuilder. And like its SAX sister class, it has build( ) methods that take in input forms like a Java File or InputStream. However, building a JDOM Document from a file, URL, or I/O stream is always slower than using SAXBuilder; that's because SAX is used to build a DOM tree in DOMBuilder, and then that DOM tree is converted to JDOM. Of course, this is much slower than leaving out the intermediary step (creating a DOM tree), and simply going straight from SAX to JDOM.

So, any time you see code like this:

DOMBuilder builder = new DOMBuilder( );

// Building from a file
Document doc = builder.build(new File("input.xml"));

// Building from a URL
Document doc = builder.build(
    new URL("http://newInstance.com/javaxml2/copyright.xml"));

// Building from an I/O stream
Document doc = builder.build(new FileInputStream("input.xml"));

You should run screaming! Seriously, DOMBuilder has its place: it's great for taking existing DOM structures and going to JDOM. But for raw, speedy input, it's simply an inferior choice in terms of performance. Save yourself some headaches and commit this fact to memory now!

Previous | Home | Next
7.4. Is JDOM a Standard? | Book Index | 7.6. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

7.6. What's Next?

An advanced JDOM chapter follows. In that chapter, I'll cover some of the finer points of the API, like namespaces, the DOM adapters, how JDOM deals with lists internally, and anything else that might interest those of you who really want to get into the API. It should give you ample knowledge to use JDOM, along with DOM and SAX, in your applications.

Previous | Home | Next
7.5. Gotcha! | Book Index | 8. Advanced JDOM
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Advanced JDOM (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 8. Advanced JDOM

Contents:

Helpful JDOM Internals
JDOM and Factories
Wrappers and Decorators
Gotcha!
What's Next?

Continuing with JDOM, this chapter introduces some more advanced concepts. In the last chapter, you saw how to read and write XML using JDOM, and also got a good taste of what classes are available in the JDOM distribution. In this chapter, I drill down a little deeper to see what's going on. You'll get to see some of the classes that JDOM uses that aren't exposed in common operations, and you'll start to understand how JDOM is put together. Once you've gotten that basic understanding down, I'll move on to show you how JDOM can utilize factories and your own custom JDOM implementation classes, albeit in a totally different way than DOM. That will take you right into a fairly advanced example using wrappers and decorators, another pattern for adding functionality to the core set of JDOM classes without needing an interface-based API.

8.1. Helpful JDOM Internals

The first topic I cover is the architecture of JDOM. In Chapter 7, "JDOM", I showed you a simple UML-type model of the core JDOM classes. However, if you look closely, there are probably some things in the classes that you haven't worked with, or didn't expect. I'm going to cover those particular items in this section, showing how you can get down and dirty with JDOM.

NOTE: JDOM beta 7 was released literally days before this chapter was written. In that release, the Text class was being whiteboarded, but had not been integrated in the JDOM internals. However, this process is happening very quickly, most likely before this book gets into your hands. Even if that is not the case, it will be integrated soon after, and the issues discussed here will then apply. If you have problems with the code snippets in this section, check the version of JDOM you are using, and always try to get the newest possible release.

8.1.1. The Text Class

One class you may have been a bit surprised to see in JDOM is the Text class. If you read the last chapter, you probably caught that one large difference between DOM and JDOM is that JDOM (at least seemingly) directly exposes the textual content of an element, whereas in DOM you get the child Text node and then extract its value. What actually happens, though, is that JDOM models character-based content much like DOM does architecturally; each piece of character content is stored within a JDOM Text instance. However, when you invoke getText( ) (or getTextTrim( ) or getTextNormalize( )) on a JDOM Element instance, the instance automatically returns the value(s) in its child Text nodes:

// Get textual content
String textualContent = element.getText( );

// Get textual content, with surrounding whitespace trimmed
String trimmedContent = element.getText().trim( );
// or...
String trimmedContent = element.getTextTrim( );

// Get textual content, normalized (all interior whitespace compressed to single
//   space. For example, "   this   would  be  " would be "this would be"
String normalizedContent = element.getTextNormalize( );

As a result, it commonly seems that no Text class is actually being used. The same methodology applies when invoking setText( ) on an element; the text is created as the content of a new Text instance, and that new instance is added as a child of the element. Again, the rationale is that the process of reading and writing the textual content of an XML element is such a common occurrence that it should be as simple and quick as possible.

At the same time, as I pointed out in earlier chapters, a strict tree model makes navigation over content very simple; instanceof and recursion become easy solutions for tree explorations. Therefore, an explicit Text class, present as a child (or children) of Element instances, makes this task much easier. Further, the Text class allows extension, while raw java.lang.String classes are not extensible. For all of these reasons (and several more you can dig into on the jdom-interest mailing lists), the Text class is being added to JDOM. Even though not as readily apparent as in other APIs, it is available for these iteration-type cases. To accommodate this, if you invoke getContent( ) on an Element instance, you will get all of the content within that element. This could include Comments, ProcessingInstructions, EntityRefs, CDATA sections, and textual content. In this case, the textual content is returned as one or more Text instances rather than directly as Strings, allowing processing like this:

public void processElement(Element element) {
    List mixedContent = element.getContent( );
    for (Iterator i = mixedContent.iterator(); i.hasNext( ); ) {
        Object o = i.next( );
        if (o instanceof Text) {
            processText((Text)o);
        } else if (o instanceof CDATA) {
            processCDATA((CDATA)o);
        } else if (o instanceof Comment) {
            processComment((Comment)o);
        } else if (o instanceof ProcessingInstruction) {
            processProcessingInstruction((ProcessingInstruction)o);
        } else if (o instanceof EntityRef) {
            processEntityRef((EntityRef)o);
        } else if (o instanceof Element) {
            processElement((Element)o);
        }
    }
}

public void processComment(Comment comment) {
    // Do something with comments
}

public void processProcessingInstruction(ProcessingInstruction pi) {
    // Do something with PIs
}

public void processEntityRef(EntityRef entityRef) {
    // Do something with entity references
}

public void processText(Text text) {
    // Do something with text
}

public void processCDATA(CDATA cdata) {
    // Do something with CDATA
}

This sets up a fairly simple recursive processing of a JDOM tree. You could kick it off with simply:

// Get a JDOM Document through a builder
Document doc = builder.build(input);

// Start recursion
processElement(doc.getRootElement( ));

You would handle Comment and ProcessingInstruction instances at the document level, but you get the idea here. You can choose to use the Text class when it makes sense, and not worry about it when it doesn't.

8.1.2. The EntityRef Class

Next up on the JDOM internals list is the EntityRef class. This is another class that you may not have to use much in common cases, but is helpful to know for special coding needs. This class represents an XML entity reference in JDOM, such as the OReillyCopyright entity reference in the contents.xml document I have been using in examples:

<ora:copyright>&OReillyCopyright;</ora:copyright>

This class allows for setting and retrieval of a name, public ID, and system ID, just as is possible when defining the reference in an XML DTD or schema. It can appear anywhere in a JDOM content tree, like the Elements and Text nodes. However, like Text nodes, an EntityRef class is often a bit of a pain in the normal case. For example, in the contents.xml document, modeled in JDOM, you're usually going to be more interested in the textual value of the reference (the resolved content) rather than the reference itself. In other words, when you invoke getContent( ) on the copyright Element in a JDOM tree, you'd like to get "Copyright O'Reilly, 2000" or whatever other textual value is referred to by the entity reference. This is much more useful (again, in the most common cases) than getting a no-content indicator (an empty string), and then having to check for the existence of an EntityRef. For this reason, by default, all entity references are expanded when using the JDOM builders (SAXBuilder and DOMBuilder) to generate JDOM from existing XML. You will rarely see EntityRefs in this default case, because you don't want to mess with them. However, if you find you need to leave entity references unexpanded and represented by EntityRefs, you can use the setExpandEntities( ) method on the builder classes:

// Create new builder
SAXBuilder builder = new SAXBuilder( );

// Do not expand entity references (default is to expand these)
builder.setExpandEnitites(false);

// Build the tree with EntityRef objects (if needed, of course)
Document doc = builder.build(inputStream);

In this case, you may have EntityRef instances in the tree (if you were using the contents.xml document, for example). And you can always create EntityRefs directly and place them in the JDOM tree:

// Create new entity reference
EntityRef ref = new EntityRef("TrueNorthGuitarsTagline");
ref.setSystemID("tngTagline.xml");

// Insert into the tree
tagLineElement.addContent(ref);

When serializing this tree, you get XML like this:

<guitar>
  <tagLine>&TrueNorthGuitarsTagline;</tagLine>
</guitar>

And when reading the document back in using a builder, the resulting JDOM Document would depend on the expandEntities flag. If it is set to false, you'd get the original EntityRef back again with the correct name and system ID. With this value set to false (the default), you'd get the resolved content. A second serialization might result in:

<guitar>
  <tagLine>two hands, one heart</tagLine>
</guitar>

While this may seem like a lot of fuss over something simple, it's important to realize that whether or not entities are expanded can change the input and output XML you are working with. Always keep track of how the builder flags are set, and what you want your JDOM tree and XML output to look like.

8.1.3. The Namespace Class

I want to briefly cover one more JDOM class, the Namespace class. This class acts as both an instance variable and a factory within the JDOM architecture. When you need to create a new namespace, either for an element or for searching, you use the static getNamespace( ) methods on this class:

// Create namespace with prefix
Namespace schemaNamespace =
    Namespace.getNamespace("xsd", "http://www.w3.org/XMLSchema/2001");

// Create namespace without prefix
Namespace javaxml2Namespace =
    Namespace.getNamespace("http://www.oreilly.com/javaxml2");

As you can see, there is a version for creating namespaces with prefixes and one for creating namespaces without prefixes (default namespaces). Either version can be used, then supplied to the various JDOM methods:

// Create element with namespace
Element schema = new Element("schema", schemaNamespace);

// Search for children in the specified namespace
List chapterElements = contentElement.getChildren("chapter", javaxml2Namespace);

// Declare a new namespace on this element
catalogElement.addNamespaceDeclaration(
    Namespace.getNamespace("tng", "http://www.truenorthguitars.com"));

These are all fairly self-explanatory. Also, when XML serialization is performed with the various outputters (SAXOutputter, DOMOutputter, and XMLOutputter), the namespace declarations are automatically handled and added to the resulting XML.

One final note: in JDOM, namespace comparison is based solely on URI. In other words, two Namespace objects are equal if their URIs are equal, regardless of prefix. This is in keeping with the letter and spirit of the XML Namespace specification, which indicates that two elements are in the same namespace if their URIs are identical, regardless of prefix. Look at this XML document fragment:

<guitar xmlns="http://www.truenorthguitars.com">
  <ni:owner xmlns:ni="http://www.newInstance.com">
    <ni:name>Brett McLaughlin</ni:name>
    <tng:model xmlns:tng="http://www.truenorthguitars.com>Model 1</tng:model>
    <backWood>Madagascar Rosewood</backWood>
  </ni:owner>
</guitar>

Even though they have varying prefixes, the elements guitar, model, and backWood are all in the same namespace. This holds true in the JDOM Namespace model, as well. In fact, the Namespace class's equals( ) method will return equal based solely on URIs, regardless of prefix.

I've touched on only three of the JDOM classes, but these are the classes that are tricky and most commonly asked about. The rest of the API was covered in the previous chapter, and reinforced in the next sections of this chapter. You should be able to easily deal with textual content, entity references, and namespaces in JDOM now, converting between Strings and Text nodes, resolved content and EntityRefs, and multiple-prefixed namespaces with ease. With that understanding, you're ready to move on to some more complex examples and cases.

Previous | Home | Next
7.6. What's Next? | Book Index | 8.2. JDOM and Factories
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JDOM and Factories (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

8.2. JDOM and Factories

Moving right along, recall the discussion from the last chapter on JDOM and factories. I mentioned that you would never see code like this (at least with the current versions) in JDOM applications:

// This code does not work!!
JDOMFactory factory = new JDOMFactory( );
factory.setDocumentClass("javaxml2.BrettsDocumentClass");
factory.setElementClass("javaxml2.BrettsElementClass");

Element rootElement = JDOMFactory.createElement("root");
Document document = JDOMFactory.createDocument(rootElement);

Well, that remains true. However, I glossed over some pretty important aspects of that discussion, and want to pick it up again here. As I mentioned in Chapter 7, "JDOM", being able to have some form of factories allows greater flexibility in how your XML is modeled in Java. Take a look at the simple subclass of JDOM's Element class shown in Example 8-1.

Example 8-1. Subclassing the JDOM Element class

package javaxml2;

import org.jdom.Element;
import org.jdom.Namespace;

public class ORAElement extends Element {

    private static final Namespace ORA_NAMESPACE =
        Namespace.getNamespace("ora", "http://www.oreilly.com");

    public ORAElement(String name) {
        super(name, ORA_NAMESPACE);
    }

    public ORAElement(String name, Namespace ns) {
        super(name, ORA_NAMESPACE);
    }

    public ORAElement(String name, String uri) {
        super(name, ORA_NAMESPACE);
    }

    public ORAElement(String name, String prefix, String uri) {
        super(name, ORA_NAMESPACE);
    }
}

This is about as simple a subclass as you could come up with; it is somewhat similar to the NamespaceFilter class from Chapter 4, "Advanced SAX ". It disregards whatever namespace is actually supplied to the element (even if there isn't a namespace supplied!), and sets the element's namespace defined by the URI http://www.oreilly.com with the prefix ora.[12] This is a simple case, but it gives you an idea of what is possible, and serves as a good example for this section.

[12]It is slightly different from NamespaceFilter in that it changes all elements to a new namespace, rather than just those elements with a particular namespace.

8.2.1. Creating a Factory

Once you've got a custom subclass, the next step is actually using it. As I already mentioned, JDOM considers having to create all objects with factories a bit over-the-top. Simple element creation in JDOM works like this:

// Create a new Element
Element element = new Element("guitar");

Things remain equally simple with a custom subclass:

// Create a new Element, typed as an ORAElement
Element oraElement = new ORAElement("guitar");

The element is dropped into the O'Reilly namespace because of the custom subclass. Additionally, this method is more self-documenting than using a factory. It's clear at any point exactly what classes are being used to create objects. Compare that to this code fragment:

// Create an element: what type is created?
Element someElement = doc.createElement("guitar");

It's not clear if the object created is an Element instance, an ORAElement instance, or something else entirely. For these reasons, the custom class approach serves JDOM well. For object creation, you can simply instantiate your custom subclass directly. However, the need for factories arises when you are building a document:

// Build from an input source
SAXBuilder builder = new SAXBuilder( );
Document doc = builder.build(someInputStream);

Obviously, here you were not able to specify custom classes through the building process. I suppose you could be really bold and modify the SAXBuilder class (and the related org.jdom.input.SAXHandler class), but that's a little ridiculous. So, to facilitate this, the JDOMFactory interface, in the org.jdom.input package, was introduced. This interface defines methods for every type of object creation (see Appendix A, "API Reference" for the complete set of methods). For example, there are four methods for element creation, which match up to the four constructors for the Element class:

public Element element(String name);
public Element element(String name, Namespace ns);
public Element element(String name, String uri);
public Element element(String name, String prefix, String uri);

You will find similar methods for Document, Attribute, CDATA, and all the rest. By default, JDOM uses the org.jdom.input.DefaultJDOMFactory, which simply returns all of the core JDOM classes within these methods. However, you can easily subclass this implementation and provide your own factory methods. Look at Example 8-2, which defines a custom factory.

Example 8-2. A custom JDOMFactory implementation

package javaxml2;

import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.DefaultJDOMFactory;

class CustomJDOMFactory extends DefaultJDOMFactory {

    public Element element(String name) {
        return new ORAElement(name);
    }

    public Element element(String name, Namespace ns) {
        return new ORAElement(name, ns);
    }

    public Element element(String name, String uri) {
        return new ORAElement(name, uri);
    }

    public Element element(String name, String prefix, String uri) {
        return new ORAElement(name, prefix, uri);
    }
}

This is a simple implementation; it doesn't need to be very complex. It overrides each of the element( ) methods and returns an instance of the custom subclass, ORAElement, instead of the default JDOM Element class. At this point, any builder that uses this factory will end up with ORAElement instances in the created JDOM Document object, rather than the default Element instances you would normally see. All that's left is to let the build process know about this custom factory.

8.2.2. Building with Custom Classes

Once you have a valid implementation of JDOMFactory, let your builders know to use it by invoking the setFactory( ) method and passing in a factory instance. This method is available on both of the current JDOM builders, SAXBuilder and DOMBuilder. To see it in action, check out Example 8-3. This simple class takes in an XML document and builds it using the ORAElement class and CustomJDOMFactory from Example 8-1 and Example 8-2. It then writes the document back out to a supplied output filename, so you can see the effect of the custom classes.

Example 8-3. Building with custom classes using a custom factory

package javaxml2;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.input.JDOMFactory;
import org.jdom.output.XMLOutputter;

public class ElementChanger {

    public void change(String inputFilename, String outputFilename)
        throws IOException, JDOMException {

        // Create builder and set up factory
        SAXBuilder builder = new SAXBuilder( );
        JDOMFactory factory = new CustomJDOMFactory( );
        builder.setFactory(factory);

        // Build document
        Document doc = builder.build(inputFilename);

        // Output document
        XMLOutputter outputter = new XMLOutputter( );
        outputter.output(doc, new FileWriter(new File(outputFilename)));
    }

    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println("Usage: javaxml2.ElementChanger " +
                "[XML Input Filename] [XML Output Filename]");
            return;
        }

        try {
            ElementChanger changer = new ElementChanger( );
            changer.change(args[0], args[1]);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

I ran this on the contents.xml file used throughout the first several chapters:

bmclaugh@GANDALF
$ java javaxml2.ElementChanger contents.xml newContents.xml

This hummed along for a second, and then gave me a new document (newContents.xml). A portion of that new document is shown in Example 8-4.

Example 8-4. Output fragment from contents.xml after ElementChanger

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book SYSTEM "DTD/JavaXML.dtd">
<!-- Java and XML Contents -->
<ora:book xmlns:ora="http://www.oreilly.com">
  <ora:title ora:series="Java">Java and XML</ora:title>

  <!-- Chapter List -->
  <ora:contents>
    <ora:chapter title="Introduction" number="1">
      <ora:topic name="XML Matters" />
      <ora:topic name="What's Important" />
      <ora:topic name="The Essentials" />
      <ora:topic name="What's Next?" />
    </ora:chapter>
    <ora:chapter title="Nuts and Bolts" number="2">
      <ora:topic name="The Basics" />
      <ora:topic name="Constraints" />
      <ora:topic name="Transformations" />
      <ora:topic name="And More..." />
      <ora:topic name="What's Next?" />
    </ora:chapter>
    <ora:chapter title="SAX" number="3">
      <ora:topic name="Getting Prepared" />
      <ora:topic name="SAX Readers" />
      <ora:topic name="Content Handlers" />
      <ora:topic name="Gotcha!" />
      <ora:topic name="What's Next?" />
    </ora:chapter>
    <ora:chapter title="Advanced SAX" number="4">
      <ora:topic name="Properties and Features" />
      <ora:topic name="More Handlers" />
      <ora:topic name="Filters and Writers" />
      <ora:topic name="Even More Handlers" />
      <ora:topic name="Gotcha!" />
      <ora:topic name="What's Next?" />
    </ora:chapter>
    <!-- Other chapters -->
</ora:book>

Each element is now in the O'Reilly namespace, prefixed and referencing the URI specified in the ORAElement class.

Obviously, you can take this subclassing to a much higher degree of complexity. Common examples include adding specific attributes or even child elements to every element that comes through. Many developers have existing business interfaces, and define custom JDOM classes that extend the core JDOM classes and also implement these business-specific interfaces. Other developers have built "lightweight" subclasses that discard namespace information and maintain only the bare essentials, keeping documents small (albeit not XML-compliant in some cases). The only limitations are your own ideas in subclassing. Just remember to set up your own factory before building documents, so your new functionality is included.

Previous | Home | Next
8. Advanced JDOM | Book Index | 8.3. Wrappers and Decorators
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Wrappers and Decorators (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

8.3. Wrappers and Decorators

One of the most common requests that comes up about JDOM is related to interfaces. Many, many users have asked for interfaces in JDOM, and that request has been consistently denied. The reasoning is simple: no set of common methods could be arrived at for all JDOM constructs. There has been a reluctance to use the DOM approach, which provides a set of common methods for most constructs. For example, getChildren( ) is on the common DOM org.w3c.dom.Node interface; however, it returns null when it doesn't apply, such as to a Text node. The JDOM approach has been to only provide methods on a basic interface common to all JDOM classes, and no methods fulfilling this requirement have been found. Additionally, for every request to add interfaces, there has been a request to leave the API as is.

However, there are patterns that allow interface-type functionality to be used with JDOM without changing the API drastically (in fact, without changing it at all!). In this section, I want to talk about the most effective of those patterns, which involves using wrappers or decorators. I'm not going to dive into a lot of design pattern material in this book, but suffice it to say that a wrapper or decorator (I use the two interchangeably in this chapter) is on the exterior of existing classes, rather than on the interior, as a core JDOM interface would be. In other words, existing behavior is wrapped. In this section, I show you how this pattern allows you to customize JDOM (or any other API) in any way you please.

NOTE: By now, you should be fairly advanced in Java and XML. For that reason, I'm going to move through the example code in this section with a minimal amount of comment. You should be able to figure out what's going on pretty easily, and I'd rather get in more code than more talk.

8.3.1. JDOMNode

To get started, I've defined a JDOMNode interface in Example 8-5. This interface defines very simple behavior that I want accessible for all JDOM nodes, and that I want without having to perform type-casting.

Example 8-5. A node decorator interface

package javaxml2;

import java.util.List;
import java.util.Iterator;

// JDOM imports
import org.jdom.Document;

public interface JDOMNode {

    public Object getNode( );

    public String getNodeName( );

    public JDOMNode getParentNode( );

    public String getQName( );

    public Iterator iterator( );

    public String toString( );
}

The only method that may look odd is iterator( ); it will return a Java Iterator over a node's children, or return an empty list Iterator if there are no children (such as for attributes or text nodes). It's worth noting that I could have just as easily chosen to use the DOM org.w3c.dom.Node interface (if I wanted DOM and JDOM interoperability at a class level), or a different interface specific to my business needs. The sky is the limit on this core interface.

8.3.2. Implementing Classes

The next, more interesting step is to provide implementations of this interface that decorate existing JDOM constructs. These provide wrapping for the concrete classes already in JDOM, and most of the methods on the JDOMNode interface simply are passed through to the underlying (decorated) object. First up is Example 8-6, which decorates a JDOM Element.

Example 8-6. Decorator for JDOM Elements

package javaxml2;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

// JDOM imports
import org.jdom.Element;

public class ElementNode implements JDOMNode {

    /** the decorated Element */
    protected Element decorated;

    public ElementNode(Element element) {
        this.decorated = element;
    }

    public Object getNode( ) {
        return decorated;
    }

    public String getNodeName( ) {
        if (decorated != null) {
            return decorated.getName( );
        }
        return "";
    }

    public JDOMNode getParentNode( ) {
        if (decorated.getParent( ) != null) {
            return new ElementNode(decorated.getParent( ));
        }
        return null;
    }

    public String getQName( ) {
        if (decorated.getNamespacePrefix( ).equals("")) {
            return decorated.getName( );
        } else {
            return new StringBuffer(decorated.getNamespacePrefix( ))
                           .append(":")
                           .append(decorated.getName()).toString( );
        }
    }

    public Iterator iterator( ) {
        List list = decorated.getAttributes( );
        ArrayList content = new ArrayList(list);

        // put the element's content in the list in order
        Iterator i = decorated.getMixedContent().iterator( );
        while (i.hasNext( )) {
            content.add(i.next( ));
        }
        return content.iterator( );
    }

    public String toString( ) {
        return decorated.toString( );
    }
}

There's nothing too remarkable here, so let's keep going. In Example 8-7, I've defined a similar class, AttributeNode, which decorates a JDOM Attribute and implements my core JDOMNode class. Notice the several no-op (no-operation) methods for things like getting the children of the attribute; this closely models the DOM approach. Again, keep in mind that these classes could just as easily implement any other interface (think org.w3c.dom.Attr in this case) without needing changes within the core JDOM API.

Example 8-7. Decorator for JDOM Attributes

package javaxml2;

import java.util.Iterator;
import java.util.Collections;

// JDOM imports
import org.jdom.Attribute;

public class AttributeNode implements JDOMNode {

    /** The decorated attribute */
    protected Attribute decorated;

    public AttributeNode(Attribute attribute) {
        this.decorated = attribute;
    }

    public Object getNode( ) {
        return decorated;
    }

    public String getNodeName( ) {
        if (decorated != null) {
            return decorated.getName( );
        }
        return "";
    }

    public JDOMNode getParentNode( ) {
        if (decorated.getParent( ) != null) {
            return new ElementNode(decorated.getParent( ));
        }
        return null;
    }

    public String getQName( ) {
        if (decorated.getNamespacePrefix( ).equals("")) {
            return decorated.getName( );
        } else {
            return new StringBuffer(decorated.getNamespacePrefix( ))
                           .append(":")
                           .append(decorated.getName()).toString( );
        }
    }

    public Iterator iterator( ) {
        return Collections.EMPTY_LIST.iterator( );
    }

    public String toString( ) {
        return decorated.toString( );
    }
}

Finally, I'll decorate JDOM's textual content (see Example 8-8). At the time of this writing, the JDOM Text class I talked about in the first of this chapter hadn't quite been integrated into its final form in the JDOM source tree. As a result, I'm actually wrapping a Java String in the TextNode class. When the Text node makes it in, this needs to be updated to wrap that type, which is a simple operation.

Example 8-8. Decorator for JDOM textual content

package javaxml2;

import java.util.Collections;
import java.util.Iterator;

// JDOM imports
import org.jdom.Element;

public class TextNode implements JDOMNode {

    /** The decorated String */
    protected String decorated;

    /** The manually set parent of this string content */
    private Element parent = null;

    public TextNode(String string) {
        decorated = string;
    }

    public Object getNode( ) {
        return decorated;
    }

    public String getNodeName( ) {
        return "";
    }

    public JDOMNode getParentNode( ) {
        if (parent == null) {
            throw new RuntimeException(
                "The parent of this String content has not been set!");
        }
        return new ElementNode(parent);
    }

    public String getQName( ) {
        // text nodes have no name
        return "";
    }

    public Iterator iterator( ) {
        return Collections.EMPTY_LIST.iterator( );
    }

    public TextNode setParent(Element parent) {
        this.parent = parent;
        return this;
    }

    public String toString( ) {
        return decorated;
    }
}

I'm not going to provide decorators for all the other JDOM types because you should be getting the picture by now. Note that I could also have provided a single JDOMNode implementation, ConcreteNode or something like that, that wrapped the various JDOM types all in one class. However, that would require quite a bit of special casing code that isn't suitable here. Instead, there is a one-to-one mapping between JDOM core classes and JDOMNode implementations.

8.3.3. Providing Support for XPath

Now that you've got some interface-based JDOM nodes, I will extend things a little further. This is a common business scenario, in which you need to provide specific functionality on top of an existing API. For a practical example, I tackle XPath. For any JDOMNode implementation, I'd like to be able to get the XPath expression representing that node. To allow for that functionality, I have written another wrapper class, shown in Example 8-9. This class, XPathDisplayNode , wraps an existing node (of any type, because of the interface-based logic), and provides a single public XPath method, getXPath( ) . This method returns an XPath expression for the wrapped node as a Java string of characters.

Example 8-9. Wrapper for XPath support

package javaxml2;

import java.util.Vector;
import java.util.List;
import java.util.Iterator;
import java.util.Stack;

// JDOM imports
import org.jdom.Attribute;
import org.jdom.Element;
import org.jdom.Namespace;

public class XPathDisplayNode {

    /** The JDOMNode this xpath is based on */
    JDOMNode node;

    public XPathDisplayNode(JDOMNode node) {
        this.node = node;
    }

    private String getElementXPath(JDOMNode currentNode) {
        StringBuffer buf = new StringBuffer("/")
            .append(currentNode.getQName( ));
        Element current = (Element)currentNode.getNode( );
        Element parent = current.getParent( );

        // See if we're at the root element
        if (parent == null ) {
            return buf.toString( );
        }

        // Check for other siblings of the same name and namespace
        Namespace ns = current.getNamespace( );
        List siblings = parent.getChildren(current.getName( ), ns);

        int total = 0;
        Iterator i = siblings.iterator( );
        while (i.hasNext( )) {
            total++;
            if (current == i.next( )) {
                break;
            }
        }

        // No selector needed if this is the only element
        if ((total == 1) && (!i.hasNext( ))) {
            return buf.toString( );
        }

        return buf.append("[")
                  .append(String.valueOf(total))
                  .append("]").toString( );
    }

    public String getXPath( ) {
        // Handle elements
        if (node.getNode( ) instanceof Element) {
            JDOMNode parent = node.getParentNode( );

            // If this is null, we're at the root
            if (parent == null) {
                return "/" + node.getQName( );
            }

            // Otherwise, build a path back to the root
            Stack stack = new Stack( );
            stack.add(node);
            do {
                stack.add(parent);
                parent = parent.getParentNode( );
            } while (parent != null);

            // Build the path
            StringBuffer xpath = new StringBuffer( );
            while (!stack.isEmpty( )) {
                xpath.append(getElementXPath((JDOMNode)stack.pop( )));
            }
            return xpath.toString( );
        }

        // Handle attributes
        if (node.getNode( ) instanceof Attribute) {
            Attribute attribute = (Attribute)node.getNode( );
            JDOMNode parent = node.getParentNode( );
            StringBuffer xpath = new StringBuffer("//")
                .append(parent.getQName( ))
                .append("[@")
                .append(node.getQName( ))
                .append("='")
                .append(attribute.getValue( ))
                .append("']");

            return xpath.toString( );
        }

        // Handle text
        if (node.getNode( ) instanceof String) {
            StringBuffer xpath = new StringBuffer(
                new XPathDisplayNode(node.getParentNode()).getXPath( ))
                    .append("[child::text( )]");
            return xpath.toString( );
        }

        // Other node types could follow here
        return "Node type not supported yet.";
    }
}

In this class, I provided special casing for each node type; in other words, I didn't implement an XPathElementNode, XPathAttributeNode, and so on. That's because the similarities in generating this XPath statement are much greater than the advantages of splitting out the code for each type. Of course, this is just the opposite of providing a type-specific node decorator for each JDOM type. You'll want to always try and figure out the difference in your applications, which results in much cleaner code (and often less code, as well).

I'm going to leave the details of working through the process followed in this code up to you. For any node, the XPath expression is calculated and assembled manually, and you should be able to follow the logic pretty easily. That expression is then returned to the calling program, which I cover next.

8.3.4. Endgame

Once you have all your various node types as well as the XPath wrapper, it's time to do something useful. In this case, I want to provide a document viewer, similar to the SAXTreeViewer class from Chapter 3, "SAX", for a JDOM tree. However, I'd also like to provide the XPath expression for each item in that tree down in the status bar. Example 8-10 shows how to do this, using the nodes and wrappers discussed in this section.

Example 8-10. The SimpleXPathViewer class

package javaxml2;

import java.awt.*;
import java.io.File;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.util.Iterator;

// JDOM imports
import org.jdom.*;
import org.jdom.input.SAXBuilder;

public class SimpleXPathViewer extends JFrame {

    /** The event handler inner class */
    EventHandler eventHandler = new EventHandler( );

    /** A text field for displaying the XPath for the selectected node */
    private JTextField statusText;

    /** The JTree used to display the nodes of the xml document */
    private JTree jdomTree;

    /** The selection model used to determine which node was clicked */
    private DefaultTreeSelectionModel selectionModel;

    /** The filename containing the xml file to view */
    private String filename;

    /** Temporary hack to get around the lack of a text node */
    private static Element lastElement;

    class EventHandler implements TreeSelectionListener {

        public void valueChanged(TreeSelectionEvent e) {
            TreePath path= selectionModel.getLeadSelectionPath( );

            // If you are just collapsing the tree, you may not have a new path
            if (path != null) {
                JDOMNode selection=
                    (JDOMNode)((DefaultMutableTreeNode)path.getLastPathComponent( ))
                        .getUserObject( );
                buildXPath(selection);
            }
        };
    };

    public SimpleXPathViewer(String fileName) throws Exception {
        super( );
        this.filename = fileName;
        setSize(600, 450);
        initialize( );
    }

    private void initialize( ) throws Exception {
        setTitle("Simple XPath Viewer");

        // Setup the UI
        initConnections( );

        // Load the JDOM Document
        Document doc = loadDocument(filename);

        // Create the initial JDOMNode from the Factory method
        JDOMNode root = createNode(doc.getRootElement( ));

        // Create the root node of the JTree and build it from the JDOM Document
        DefaultMutableTreeNode treeNode =
            new DefaultMutableTreeNode("Document: " + filename);
        buildTree(root, treeNode);

        // Add the node to the tree's model
        ((DefaultTreeModel)jdomTree.getModel( )).setRoot(treeNode);
    }

    private void initConnections( ) {
        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        // Setup the JTree and a pane to display it in
        jdomTree = new JTree( );
        jdomTree.setName("JDOM Tree");
        jdomTree.addTreeSelectionListener(eventHandler);
        selectionModel = (DefaultTreeSelectionModel)jdomTree.getSelectionModel( );
        getContentPane( ).add(new JScrollPane(jdomTree), BorderLayout.CENTER);

        // Setup a text box for use in a status bar
        statusText = new JTextField("Click on an element to view xpath");
        JPanel statusBarPane= new JPanel( );
        statusBarPane.setLayout(new BorderLayout( ));
        statusBarPane.add(statusText, BorderLayout.CENTER );
        getContentPane( ).add(statusBarPane, BorderLayout.SOUTH);
    }

    private Document loadDocument(String filename) throws JDOMException {
        SAXBuilder builder = new SAXBuilder( );
        builder.setIgnoringElementContentWhitespace(true);
        return builder.build(new File(filename));
    }

    private JDOMNode createNode(Object node) {
        if (node instanceof Element) {
            lastElement = (Element)node;
            return new ElementNode((Element)node);
        }

        if (node instanceof Attribute) {
            return new AttributeNode((Attribute)node);
        }

        if (node instanceof String) {
            return new TextNode((String)node).setParent(lastElement);
        }

        // All other nodes are not implemented
        return null;
    }

    private void buildTree(JDOMNode node, DefaultMutableTreeNode treeNode) {
        // If this is a whitespace node or unhandled node, ignore it
        if ((node == null) || (node.toString().trim( ).equals(""))) {
            return;
        }

        DefaultMutableTreeNode newTreeNode = new DefaultMutableTreeNode(node);

        // Walk over the children of the node
        Iterator i = node.iterator( );
        while (i.hasNext( )) {
            // Create JDOMNodes on the children and add to the tree
            JDOMNode newNode = createNode(i.next( ));
            buildTree(newNode, newTreeNode);
        }

        // After all the children have been added, connect to the tree
        treeNode.add(newTreeNode);
    }

    private void buildXPath(JDOMNode node) {
        statusText.setText(new XPathDisplayNode(node).getXPath( ));
    }

    public static void main(java.lang.String[] args) {
        try {
            if (args.length != 1) {
                System.out.println("Usage: java javaxml2.SimpleXPathViewer " +
                    "[XML Document filename]");
                return;
            }

            /* Create the frame */
            SimpleXPathViewer viewer= new SimpleXPathViewer(args[0]);

            /* Add a windowListener for the windowClosedEvent */
            viewer.addWindowListener(new java.awt.event.WindowAdapter( ) {
                    public void windowClosed(java.awt.event.WindowEvent e) {
                        System.exit(0);
                    };
                });
            viewer.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

As usual, I am skipping the Swing details. You can see that once the document is loaded using SAXBuilder, though, the root element of that document is obtained (in the initialize( ) method). This element is used to create an instance of JDOMNode through the createNode( ) utility function. The function simply converts between JDOM types and JDOMNode implementations, and took about 15 seconds to code up. Use a similar method in your own programs that use decorators and wrappers.

Once I've got JDOMNode implementations, it's simple to walk the tree, creating visual objects for each node encountered. Additionally, for each node, I've set the status text of the window to the XPath expression for that node. You can compile all of these examples, and run them using this command:

C:\javaxml2\build>java javaxml2.SimpleXPathViewer
                       c:\javaxml2\ch08\xml\contents.xml

Be sure that JDOM and your XML parser are in your classpath. The result is the Swing UI shown in Figure 8-1. Notice how the status bar reflects the XPath expression for the currently selected node. Play around with this -- seeing four or five screenshots in a book isn't nearly as useful as your exploration of the tool.

Figure 8-1. Viewing contents.xml and XPaths

And that's it! I know I've gone quickly, but the concepts involved are simple. You can think about how decorators and wrappers might help you with the interface-like functionality you need in your applications. Also check out the JDOM web site at http://www.jdom.org for contributions that may include stock wrappers (like this one, or a DOM set of decorators).

Finally, I'd like to thank Philip Nelson, who did the lion's share of the work on the decorator code shown here. Philip has really explored using decorators with JDOM, and was a great help in this section.

Previous | Home | Next
8.2. JDOM and Factories | Book Index | 8.4. Gotcha!
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Gotcha! (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

8.4. Gotcha!

As with the other chapters on APIs, I will address a few more tricky items that relate to the topics in this chapter. These are common problems that can cause you to beat your head against the wall, so try and avoid them.

8.4.1. More on Subclassing

Since I talked about factories and custom classes in this chapter, it's worth pointing out a few important things about subclassing that can be gotcha items. When you extend a class, and in particular the JDOM classes, you need to ensure that your custom behavior is going to be activated as you want it to. In other words, ensure that there is no path from an application through your subclass and to the superclass that isn't a path you are willing to live with. In almost every case, this involves ensuring that you override each constructor of the superclass. You'll notice that in Example 8-1, the ORAElement class, I overrode all four of the Element class's constructors. This ensured that any application using ORAElement would have to create the object with one of these constructors. While that might seem like a trivial detail, imagine if I had left out the constructor that took in a name and URI for the element. This step effectively reduces the number of ways to construct the object by one. That might seem trivial, but it's not!

Continuing with this hypothetical, you implement a CustomJDOMFactory class, like the one shown in Example 8-2, and override the various element( ) methods. However, you would probably forget to override element(String name, String uri), since you already forgot to override that constructor in your subclass. Suddenly, you've got a problem. Every time an element is requested by name and URI (which is quite often in the SAXBuilder process, by the way), you are going to get a plain, vanilla Element instance. However, the other element creation methods all return instances of ORAElement. Just like that, because of one lousy constructor, your document is going to have two element implementations, almost certainly not what you wanted. It is crucial to inspect every means of object creation in your subclasses, and generally make sure you override every constructor that is public in the superclass.

8.4.2. Creating Invalid XML

Another tricky case to watch out for when subclassing is inadvertently creating invalid XML. Using JDOM, it's more or less impossible to create XML that is not well-formed, but consider the ORAElement subclass again. This subclass added the ora prefix to every element, which alone could cause it to fail validation. This is probably not a big deal, but you do need to comment out or remove the DOCTYPE declaration to avoid problems when reading the document back in.

Even more importantly, you can get some unexpected results if you aren't careful. Look at this fragment of the XML generated using the ORAElement subclass, which only shows the last little bit of the serialized document:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book SYSTEM "DTD/JavaXML.dtd">
<!-- Java and XML Contents -->
<ora:book xmlns:ora="http://www.oreilly.com">
  <ora:title ora:series="Java">Java and XML</ora:title>

  <!-- Other content -->

  <ora:copyright>

<ora:copyright>
  <ora:year value="2001" />
  <ora:content>All Rights Reserved, O'Reilly &amp; Associates</ora:content>
</ora:copyright>
</ora:copyright>
</ora:book>

Notice that there are now two ora:copyright elements! What happened is that an existing element was in place in the O'Reilly namespace (the original ora:copyright element). However, the copyright element nested within that, with no namespace, was also assigned the ora prefix and O'Reilly namespace through the ORAElement class. The result is two elements with the same name and namespace, but differing content models. This makes validation very tricky, and is probably not what you intended. These are simple examples, but in more complex documents with more complex subclasses, you'll need to watch carefully what results you are generating, particularly with respect to a DTD, XML Schema, or other form of document constraints.

Previous | Home | Next
8.3. Wrappers and Decorators | Book Index | 8.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

8.5. What's Next?

I'm winding down on the lower-level APIs, and I'll finish up the discussion in the next chapter by covering JAXP, Sun's Java API for XML Processing. JAXP requires use of SAX and DOM, and so logically looks at those APIs. It also serves as a halfway point in the book, and probably a good time to take a short break when you're done with the chapter! I'll cover both JAXP 1.0 and 1.1, now heavily in use, explaining how they work with the APIs you already know and why JAXP can really help out in your application programming. So get ready for another hammer in the old toolbox, and turn the page.

Previous | Home | Next
8.4. Gotcha! | Book Index | 9. JAXP
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JAXP (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 9. JAXP

Contents:

API or Abstraction
JAXP 1.0
JAXP 1.1
Gotcha!
What's Next?

When Sun released the Java API for XML Parsing, generally referred to as JAXP, they managed to launch a series of contradictions into the Java world. In one swoop, they released the most important API that wasn't an API to Java developers, and caused great confusion with the simplest API. People switched to a new parser without knowing they had switched to a new parser. There is a lot of confusion surrounding JAXP, not only about how to use it, but even about what it is.

In this chapter, I'll first address some of the confusion about what JAXP is and is not.[13] Then you'll get a look at JAXP 1.0, which is still used heavily. Once you get the basics, we will move on to JAXP 1.1, the latest version (not quite released as of the writing of this chapter, but almost certainly available by publication time). That will give you a leg up on the new features in the latest version, and in particular the TrAX API included in JAXP 1.1. Buckle up, and be prepared to finally understand the mystery behind JAXP.

[13]If this chapter feels a little bit like déjà vu, you may have read an earlier version of this text at IBM DeveloperWorks. There were originally two articles published at http://www.ibm.com/developer that explored JAXP. This chapter is an updated and slightly modified version of those articles.

9.1. API or Abstraction

Before diving into code, it's important to cover some basic concepts. Strictly speaking, JAXP is an API, but it is more accurately called an abstraction layer. It does not provide a new means of parsing XML, add to SAX, DOM, or JDOM, or provide new functionality in handling Java and XML. Instead, it makes it easier to deal with some difficult tasks with DOM and SAX. It also makes it possible to handle vendor-specific tasks encountered when using the DOM and SAX APIs, which in turn allows those APIs to be used in a vendor-neutral way.

While I'll go through these features individually, the thing you really need to get a handle on is that JAXP does not provide parsing functionality! Without SAX, DOM, or another XML parsing API, you cannot parse XML. I have seen many requests for a comparison of DOM, SAX, or JDOM to JAXP. Making these comparisons is impossible because the first three APIs serve a completely different purpose than JAXP. SAX, DOM, and JDOM all parse XML. JAXP provides a means to get to these APIs and the results of parsing a document. It doesn't offer a new way to parse the document itself. This is a critical distinction to make if you're going to use JAXP correctly. It will also most likely put you miles ahead of many of your fellow XML developers.

If you're still dubious, download the JAXP 1.0 distribution from Sun's web site at http://java.sun.com/xml and you'll get an idea of how basic JAXP is. In the included jar (jaxp.jar), you will find only six classes! How hard could this API be? All of the classes (part of the javax.xml.parsers package) sit on top of an existing parser. And two of these classes are for error handling. JAXP is simpler than people think.

9.1.1. Sun's JAXP and Parser

Part of the trouble stems from the fact that Sun's parser is included with the JAXP download. The parser classes are all in the parser.jar archive as part of the com.sun.xml.parser package and related subpackages. This parser (now code-named Crimson) is not part of JAXP. It is part of the JAXP distribution, but it is not part of the JAXP API. Confusing? A little bit. Think about it this way: JDOM downloads include the Apache Xerces parser. That parser isn't part of JDOM but is used by JDOM, so it's included to ensure that JDOM is usable out of the box. The same principle applies for JAXP, but it isn't as clearly publicized: JAXP comes with Sun's parser so it can be used immediately. However, many people refer to the classes included in Sun's parser as part of the JAXP API itself. For example, a common question on newsgroups is, "How can I use the XMLDocument class that comes with JAXP? What is its purpose?" The answer is somewhat complicated.

First, the com.sun.xml.tree.XMLDocument class is not part of JAXP. It is part of Sun's parser. So the question is misleading from the start. Second, the whole point of JAXP is to provide vendor-independence when dealing with parsers. The same code, using JAXP, could be used with Sun's XML parser, Apache's Xerces XML parser, and Oracle's XML parser. Using a Sun-specific class, then, is a bad idea. It violates the entire point of using JAXP. Are you starting to see how this subject has gotten muddied up? The parser and the API in the JAXP distribution (at least the one from Sun) have been lumped together, and developers mistake classes and features from one as part of the other, and vice versa.

9.1.2. The Old and the New

There is another confusing issue related to JAXP. JAXP 1.0 supports only SAX 1.0 and DOM Level 1. It is generally Sun's policy not to ship any API or product based on a working draft, beta, or other nonfinal version of underlying APIs. When JAXP 1.0 was finalized, Sun settled on SAX 1.0, as SAX 2.0 was still in beta, and DOM Level 1, as Level 2 was still in candidate recommendation. There were a lot of users who layered JAXP on top of existing parsers (like Apache Xerces, for example) that had SAX 2.0 and DOM Level 2 support, and suddenly lost functionality. The result was a lot of questions about how to use features that simply couldn't be used with JAXP. It was also right about this time that SAX 2.0 went from beta to final, and really threw things into a mess. However, that hasn't stopped many who didn't need these later versions of DOM and SAX from putting JAXP 1.0 into production, and so I'd be remiss in not covering both the old version (1.0), as well as the new version (1.1), which does support SAX 2.0 and DOM Level 2. The rest of this chapter is split into two parts: the first dealing with JAXP 1.0, and the second with 1.1. Since 1.1 builds on what 1.0 provided in terms of functionality, you should read both sections regardless of the version of the API you're using.

Previous | Home | Next
8.5. What's Next? | Book Index | 9.2. JAXP 1.0
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JAXP 1.0 (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

9.2. JAXP 1.0

It all begins (and began) with JAXP 1.0. This first version of Sun's API provided, basically, a thin layer over existing APIs that allowed for vendor-neutral parsing of code. For SAX, this isn't a huge deal; now that you are a SAX expert, you are smart enough to use the XMLReaderFactory class instead of directly instantiating a vendor's parser class. Of course, as you're also a DOM expert, you know that it's a pain to deal with DOM in a vendor-neutral way, so JAXP helps out quite a bit in this regard. Additionally, JAXP provided some methods for working with validation and namespaces, another vendor-specific task that can now be handled (in most cases) in a much better way.

9.2.1. Starting with SAX

Before getting into how JAXP works with SAX, I will fill you in on some SAX 1.0 details. Remember the org.xml.sax.helpers.DefaultHandler class I showed you in Chapter 4, "Advanced SAX " that implemented all the core SAX 2.0 handlers? There was a similar class in SAX 1.0 called org.xml.sax.HandlerBase ; this class implemented the SAX 1.0 handlers (which were slightly different in that version). As long as you understand this, you'll be all set to deal with JAXP 1.0.

To use JAXP with a SAX-compliant parser, your only task is to extend the HandlerBase class and implement the callbacks desired for your application. That's it, no different than doing the same for DefaultHandler in SAX 2.0. An instance of your extension class then becomes the core argument for most of the JAXP methods that deal with SAX.

Here's the typical SAX rundown:

  • Create a SAXParser instance using a specific vendor's parser implementation.

  • Register callback implementations (by using a class that extends HandlerBase).

  • Start parsing and relax as your callback implementations are fired off.

The SAX component of JAXP provides a simple means to do all of this. Without JAXP, a SAX parser instance either must be instantiated directly from a vendor class (such as org.apache.xerces.parsers.SAXParser), or it must use a SAX helper class called ParserFactory (the SAX 1.0 version of SAX 2.0's XMLReaderFactory).

JAXP provides a better alternative. It allows you to use the vendor class as a parser through a Java system property. Of course, when you download a distribution from Sun, you get a JAXP implementation that uses Sun's parser by default. The same JAXP interfaces, but with an implementation built on Apache Xerces, can be downloaded from the Apache XML web site at http://xml.apache.org, and they use Apache Xerces by default. Therefore (in either case), changing the parser you are using requires that you change a classpath setting or system property value, but it does not require code recompilation. And this is the magic, the abstraction, that JAXP is all about.

WARNING: Where you download the JAXP classes from is important. Even though you can still set system properties to change the parser class, the default parser (when no system properties are present) depends on the implementation -- which depends on the location that JAXP comes from. The version from Apache XML uses Xerces by default, while Sun's version uses Crimson by default. If you get these mixed up, you may end up with the wrong parser in your classpath, and get ClassNotFound exceptions.

9.2.1.1. A look at the SAXParserFactory class

The JAXP SAXParserFactory class (in the javax.xml.parsers class, like all the JAXP classes) is the key to changing parser implementations easily. You must create a new instance of this class (which I will describe how to do in a moment). After the factory is created, it provides a method to obtain a SAX-capable parser. Behind the scenes, the JAXP implementation takes care of the vendor-dependent code, keeping your code unpolluted. This factory provides some other nice features, as well.

In addition to the basic job of creating instances of SAX parsers, the factory allows configuration options to be set. These options affect all parser instances obtained through the factory. The two options available in JAXP 1.0 are setting namespace awareness (setNamespaceAware (boolean awareness)), and turning on validation (setValidating (boolean validating)). Remember that after these options are set, they affect all instances obtained from the factory after the method invocation.

Once you have set up the factory, invoking the newSAXParser( ) method returns a ready-to-use instance of the JAXP SAXParser class. This class wraps an underlying SAX parser (an instance of the SAX class org.xml.sax.Parser). It also protects you from using any vendor-specific additions to the parser class. (Remember our earlier discussion about the xmlDocument class?) This class allows actual parsing behavior to be kicked off. Example 9-1 shows how a SAX factory can be created, configured, and used.

Example 9-1. Using the SAXParserFactory class

package javaxml2;

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

// JAXP
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

// SAX
import org.xml.sax.AttributeList;
import org.xml.sax.HandlerBase;
import org.xml.sax.SAXException;

public class TestSAXParsing {

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.err.println(
                    "Usage: java TestSAXParsing [XML Document filename]");
                System.exit(1);
            }

            // Get SAX Parser Factory
            SAXParserFactory factory = SAXParserFactory.newInstance( );

            // Turn on validation, and turn off namespaces
            factory.setValidating(true);
            factory.setNamespaceAware(false);

            SAXParser parser = factory.newSAXParser( );
            parser.parse(new File(args[0]), new MyHandler( ));

        } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not " +
                               "support the requested features.");
        } catch (FactoryConfigurationError e) {
            System.out.println(
                "Error occurred obtaining SAX Parser Factory.");
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

class MyHandler extends HandlerBase {
    // SAX callback implementations from DocumentHandler, ErrorHandler,
    //   DTDHandler, and EntityResolver
}

Notice in this code that two JAXP-specific problems can occur in using the factory: the inability to obtain or configure a SAX factory, and the inability to configure a SAX parser. The first of these problems, which is represented by a FactoryConfigurationError, usually occurs when the parser specified in a JAXP implementation or system property cannot be loaded. The second problem, ParserConfigurationException, occurs when a requested feature is not available in the parser being used. Both are easy to deal with and shouldn't pose as any difficulty.

A SAXParser is obtained once you get the factory, turn off namespaces, and turn on validation; then parsing begins. Notice that the parse( ) method of the SAX parser takes an instance of the SAX HandlerBase class that I mentioned earlier (I left the implementation of this class out of the code listing, but you can download the complete source file for TestSAXParsing.java at the book's web site). You also pass in the file (as a Java File) to parse, obviously. However, the SAXParser class contains much more than just this single method.

9.2.1.2. Working with the SAXParser class

Once you have an instance of the SAXParser class, you can do more with it than just passing it a File to parse. Because of the way components in large applications communicate these days, it is not always safe to assume that the creator of an object instance is its user. In other words, one component may create the SAXParser instance, while another component (perhaps coded by another developer) may need to use that same instance. For this reason, methods are provided to determine the settings of a parser instance. The two methods that provide this functionality are isValidating( ) , which informs the caller if the parser will perform validation, and isNamespaceAware( ) , which returns an indication if the parser can process namespaces in an XML document. While these methods can give you information about what the parser can do, you do not have the means to change these features. You must do this at the parser factory level.

Additionally, there is a variety of ways to request parsing of a document. Instead of just accepting a File and a SAX HandlerBase instance, the SAXParser's parse( ) method can also accept a SAX InputSource, a Java InputStream, or a URL in String form, all with a HandlerBase instance as the second argument. Different types of input documents can be treated to different means of parsing.

Finally, the underlying SAX parser (an instance of org.xml.sax.Parser) can be obtained and used directly through the SAXParser's getParser( ) method. Once this underlying instance is obtained, the usual SAX methods are available. Example 9-2 shows examples of the various uses of the SAXParser class, the core class in JAXP for SAX parsing.

Example 9-2. Using the JAXP SAXParser class

    // Get a SAX Parser instance
    SAXParser saxParser = saxFactory.newSAXParser( );

    // Find out if validation is supported
    boolean isValidating = saxParser.isValidating( );

    // Find out if namespaces is supported
    boolean isNamespaceAware = saxParser.isNamespaceAware( );

    // ------- Parse, in a variety of ways ----------------- //

    // Use a file and a SAX HandlerBase instance
    saxParser.parse(new File(args[0]), myHandlerBaseInstance);

    // Use a SAX InputSource and a SAX HandlerBase instance
    saxParser.parse(mySaxInputSource, myHandlerBaseInstance);

    // Use an InputStream and a SAX HandlerBase instance
    saxParser.parse(myInputStream, myHandlerBaseInstance);

    // Use a URI and a SAX HandlerBase instance
    saxParser.parse("http://www.newInstance.com/xml/doc.xml",
                    myHandlerBaseInstance);

    // Get the underlying (wrapped) SAX parser
    org.xml.sax.Parser parser = saxParser.getParser( );

    // Use the underlying parser
    parser.setContentHandler(myContentHandlerInstance);
    parser.setErrorHandler(myErrorHandlerInstance);
    parser.parse(new org.xml.sax.InputSource(args[0]));

Up to now, I've talked a lot about SAX, but I haven't unveiled anything remarkable or even that surprising. The fact is, the functionality of JAXP is fairly minor, particularly when SAX is involved. This is fine with me (and should be with you), because minimal functionality means your code is more portable and can be used by other developers, either freely (through open source) or commercially, with any SAX-compliant XML parser. That's it. There's nothing more to using SAX with JAXP. If you already know SAX, you're 98 percent of the way there. You just need to learn two new classes and a couple of Java exceptions, and you're ready to roll. If you've never used SAX, it's easy enough to start now.

9.2.2. Dealing with DOM

The process of using JAXP with DOM is nearly identical to using JAXP with SAX; all you do is change two class names and a method's return type, and you are pretty much there. If you understand how SAX works and understand what DOM is, you won't have any problem. Of course, you've got Chapter 5, "DOM" and Chapter 6, "Advanced DOM" to refer back to, so you're all set. Since JAXP doesn't have to fire SAX callbacks when working with DOM, it is responsible only for returning a DOM Document object from a parsing.

9.2.2.1. A look at the DOM parser factory

With a basic understanding of DOM and the differences between DOM and SAX, there is little else to say. The code in Example 9-3 will look remarkably similar to the SAX code in Example 9-1. First, an instance of DocumentBuilderFactory is obtained (in the same way that a SAXParserFactory instance was in SAX). Then the factory is configured to handle validation and namespaces (in the same way that it was in SAX). Next, a DocumentBuilder, the DOM analog to SAXParser, is retrieved from the factory. Parsing can then occur, and the resulting DOM Document object is handed off to an instance of the DOMSerializer class (from Chapter 5, "DOM").

Example 9-3. Using the DocumentBuilderFactory class

package javaxml2;

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

// JAXP
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

// DOM
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TestDOMParsing {

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.err.println (
                    "Usage: java TestDOMParsing [filename]");
                System.exit(1);
            }

            // Get Document Builder Factory
            DocumentBuilderFactory factory =
                DocumentBuilderFactory.newInstance( );

            // Turn on validation, and turn off namespaces
            factory.setValidating(true);
            factory.setNamespaceAware(false);

            DocumentBuilder builder = factory.newDocumentBuilder( );
            Document doc = builder.parse(new File(args[0]));

            // Serialize the DOM tree
            DOMSerializer serializer = new DOMSerializer( );
            serializer.serialize(doc, System.out);

        } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not " +
                "support the requested features.");
        } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining Document " +
                "Builder Factory.");
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

Two problems can arise from this code: a FactoryConfigurationError and a ParserConfigurationException. The cause of each is the same as it was in SAX. Either there's a problem present in the implementation classes (FactoryCon-figurationError), or the parser provided doesn't support the requested features (ParserConfigurationException). The only difference between DOM and SAX is that with DOM, you substitute DocumentBuilderFactory for SAXParserFactory, and DocumentBuilder for SAXParser.

9.2.2.2. Working with the DOM parser

Once you have a DOM factory, you can obtain a DocumentBuilder instance from it. The methods available to a DocumentBuilder instance are very similar to those available to its SAX counterpart. The major difference is that variations of the parse( ) method do not take an instance of the SAX HandlerBase class. Instead they return a DOM Document instance representing the XML document that was parsed. The only other difference is that two methods are provided for SAX-like functionality: setErrorHandler( ), which takes a SAX ErrorHandler implementation to handle problems that may arise in parsing, and setEntityResolver( ), which takes a SAX EntityResolver implementation to handle entity resolution. Example 9-4 shows examples of these methods in action.

Example 9-4. Using the JAXP DocumentBuilder

    // Get a DocumentBuilder instance
    DocumentBuilder builder = builderFactory.newDocumentBuilder( );

    // Find out if validation is supported
    boolean isValidating = builder.isValidating( );

    // Find out if namespaces is supported
    boolean isNamespaceAware = builder.isNamespaceAware( );

    // Set a SAX ErrorHandler
    builder.setErrorHandler(myErrorHandlerImpl);

    // Set a SAX EntityResolver
    builder.setEntityResolver(myEntityResolverImpl);

    // ------------ Parse, in a variety of ways ------------------- //

    // Use a file
    Document doc = builder.parse(new File(args[0]));

    // Use a SAX InputSource
    Document doc = builder.parse(mySaxInputSource);

    // Use an InputStream
    Document doc = builder.parse(myInputStream, myHandlerBaseInstance);

    // Use a URI
    Document doc = builder.parse("http://www.newInstance.com/xml/doc.xml");

It really is that straightforward to take what you've learned about SAX and apply it to DOM. So make your bar bets with friends and coworkers on how using JAXP is a piece of cake; you'll win every time.

9.2.3. Changing the Parser

The last topic I need to address in dealing with JAXP is the ability to easily change out the parser used by the factory classes. Changing the parser used by JAXP actually means changing the parser factory, because all SAXParser and DocumentBuilder instances come from these factories. Since the factories determine which parser is loaded, it's the factories that must change. The implementation of SAXParserFactory to be used can be changed by setting the Java system property javax.xml.parsers.SAXParserFactory. If this property isn't defined, then the default implementation (whatever parser your vendor specified) is returned. The same principle applies for the DocumentBuilderFactory implementation you use. In this case, the javax.xml.parsers.DocumentBuilderFactory system property is queried. And as simple as that, we have gone through it all! This is the whole scope of JAXP 1.0: provide hooks into SAX, provide hooks into DOM, and allow the parser to easily be changed out.

Previous | Home | Next
9. JAXP | Book Index | 9.3. JAXP 1.1
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JAXP 1.1 (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

9.3. JAXP 1.1

Late in 2000, the expert group for JAXP 1.1 formed, and work got underway to move JAXP 1.0 to a better, more effective solution for parsing and handling XML documents. As I write this chapter, JAXP 1.1 has just become downloadable in a final form from Sun's web site at http://java.sun.com/xml. Many of the changes to the API center around parsing, which makes sense, given that the "P" in JAXP stands for "parsing." But the most significant changes in JAXP 1.1 center around XML transformations, which I cover in the last part of this chapter. In terms of additions to 1.0 functionality, the changes are fairly minor. The biggest addition is support for SAX 2.0, which went final in May of 2000, and DOM Level 2, which was finalized in November of 2000. Remember that JAXP 1.0 supported only SAX 1.0 and DOM Level 1. This lack of updated standards has been one of the biggest criticisms of JAXP 1.0, and is probably why the 1.1 version has appeared so quickly.

In addition to updating JAXP to the newest versions of SAX and DOM, several small changes have been made in the API feature list. Almost all of these changes are the result of feedback from the various companies and individuals on the JAXP expert group. These changes also all deal with configuring the parsers returned from JAXP's two factories, SAXParserFactory and DocumentBuilderFactory. I cover these now, as well as the update in standards support for SAX and DOM, and then we look at the new TrAX API that is part of JAXP 1.1.

9.3.1. Updating the Standards

The most anticipated change from JAXP 1.0 to 1.1 is the updated support for the SAX and DOM standards. Of critical note is that SAX 2.0 handles namespaces, while SAX 1.0 did not.[14] This namespace support enables the use of numerous other XML vocabularies, such as XML Schema, XLink, and XPointer. While it was possible to use these vocabularies in SAX 1.0, the burden was on the developer to split an element's local (or qualified) name from its namespace, and keep track of namespaces throughout the document. SAX 2.0 provides this information to the developer, dramatically simplifying these programming tasks. The same goes for DOM Level 2: namespace support, as well as a wealth of other methods on the DOM classes, is available.

[14]Careful readers will note that JAXP 1.0 offered namespace processing through the setNamespaceAware( ) methods on SAXParserFactory and DocumentBuilderFactory. The JAXP code had to do this task "by hand" instead of relying on the SAX or DOM APIs. With SAX 2.0 and DOM Level 2, this process is standardized, and therefore much more reliable, as well as cleaner, than the JAXP 1.0 implementation. It's a good thing.

The good news is that these changes are generally transparent to the developer using JAXP. In other words, standards updates happen somewhat "automatically," without user intervention. Simply specifying a SAX 2.0-compliant parser to the SAXParserFactory and a DOM Level 2-compliant parser to the DocumentBuilderFactory class takes care of the update in functionality.

9.3.1.1. The road to SAX 2.0

There are a few significant changes related to these standards updates, particularly with regard to SAX. In SAX 1.0, the parser interface implemented by vendors and XML parser projects was org.xml.sax.Parser. The JAXP class SAXParser, then, provided a method to get this underlying implementation class through the getParser( ) method. The signature for that method looks like this:

public interface SAXParser {

    public org.xml.sax.Parser getParser( );

    // Other methods
}

However, in the change from SAX 1.0 to 2.0, the Parser interface was deprecated and replaced with a new interface, org.xml.sax.XMLReader (the one that you are familiar with from earlier chapters). This made the getParser( ) method useless for obtaining an instance of the SAX 2.0 XMLReader class. To support this new interface, a new method has been added to the JAXP SAXParser class. Not surprisingly, this method is named getXMLReader( ) and looks like:

public interface SAXParser {

    public org.xml.sax.XMLReader getXMLReader( );

    public org.xml.sax.Parser getParser( );

    // Other methods
}

In the same way, JAXP 1.0 used the parse( ) method by supplying an instance of the HandlerBase class (or a subclass, to be more accurate). Of course in SAX 2.0, the HandlerBase class has been replaced by DefaultHandler . To accommodate this change, all of the parse( ) methods on the SAXParser class have been complemented with versions of the same method that take an instance of the DefaultHandler class to support SAX 2.0. To help you see this difference, take a look at Example 9-5, which shows a good chunk of the SAXParser interface.

Example 9-5. The parse( ) methods of the SAXParser interface

public interface SAXParser {

    // The SAX 1.0 parse methods
    public void parse(File file, HandlerBase handlerBase);
    public void parse(InputSource inputSource, HandlerBase handlerBase);
    public void parse(InputStream inputStream, HandlerBase handlerBase);
    public void parse(InputStream inputStream, HandlerBase handlerBase,
                      String systemID);
    public void parse(String uri, HandlerBase handlerBase);

    // The SAX 2.0 parse methods
    public void parse(File file, DefaultHandler defaultHandler);
    public void parse(InputSource inputSource,
                      DefaultHandler defaultHandler);
    public void parse(InputStream inputStream,
                      DefaultHandler defaultHandler);
    public void parse(InputStream inputStream,
                      DefaultHandler defaultHandler,
                      String systemID);
    public void parse(String uri, DefaultHandler defaultHandler);

    // Other methods

}

All these methods for parsing may seem a bit confusing, but it's only tricky if you're working with both versions of SAX. If you are using SAX 1.0, you'll be working with the Parser interface and HandlerBase class, and it will be obvious which methods to use. Similarly, when using SAX 2.0, it will be obvious that the methods that accept DefaultHandler instances and return XMLReader instances should be used. So take all this as a reference and don't worry too much about it! There are some other changes to the SAX portion of the API, as well.

9.3.1.2. Changes in SAX classes

To complete the discussion of the changes to existing JAXP functionality, I need to go over a few new methods that are available to JAXP SAX users. First, the SAXParserFactory class has a new method, setFeature( ) . As you recall from JAXP 1.0, the SAXParserFactory class allows configuration of SAXParser instances returned from the factory. In addition to the methods already available in 1.0 (setValidating( ) and setNamespaceAware( )), this new method allows SAX 2.0 features to be requested for new parser instances. For example, a user may request the http://apache.org/xml/features/validation/schema feature, which allows XML Schema validation to be turned on or off. This can now be performed directly on a SAXParserFactory, as shown here:

    SAXParserFactory myFactory = SAXParserFactory.newInstance( );

    // Turn on XML Schema validation
    myFactory.setFeature(
        "http://apache.org/xml/features/validation/schema", true);

    // Now get an instance of the parser with schema validation enabled
    SAXParser parser = myFactory.newSAXParser( );

A getFeature( ) method is provided to complement the setFeature( ) method and allow querying of particular features. This method returns a simple boolean value.

In addition to providing a means to set SAX features (with true or false values), JAXP 1.1 supports the setting of SAX properties (with object values). For example, using an instance of a SAX parser, you could set the property http://xml.org/sax/properties/lexical-handler, assigning that property an implementation of a SAX LexicalHandler interface. Because properties like this lexical one are parser-specific instead of factory-specific (as features were), a setProperty( ) method is provided on the JAXP SAXParser class rather than on the SAXParserFactory class. And as with features, a getProperty( ) complement is provided to return the value associated with a specific property, also on the SAXParser class.

9.3.1.3. Updates to the DOM

A number of new methods are available for the DOM portion of JAXP, as well. These methods have been added to existing JAXP classes to support both DOM Level 2 options, as well as common configuration situations that have arisen in the last year. I won't cover all of these options and the corresponding methods here since many are used only in very unusual situations and won't be needed in most of your applications. I encourage you to check these out in the latest JAXP specification. With the coverage of standards updates, SAX changes, and additional DOM methods, you're ready to read about the most substantial change in JAXP 1.1: the TrAX API.

9.3.2. The TrAX API

So far, I've covered the changes to XML parsing in JAXP. Now I can turn to XML transformations in JAXP 1.1. Perhaps the most exciting development in the newest version of Sun's API is that JAXP 1.1 allows vendor-neutral XML document transformations. While this vendor-neutrality may cloud the definition of JAXP as simply a parsing API, it is a much-needed facility since XSL processors currently employ different methods and means for enabling user and developer interaction. In fact, XSL processors have even greater variance across providers than their XML parser counterparts.

Originally, the JAXP expert group sought to provide a simple Transform class with a few methods to allow specification of a stylesheet and subsequent document transformations. This first effort turned out to be rather shaky, but I'm happy to report that we (the JAXP expert group) are going much further in our continued efforts. Scott Boag and Michael Kay, two of the XSL processor gurus (working on Apache Xalan and SAXON, respectively), have worked with many others to develop TrAX, which supports a much wider array of options and features, and provides complete support for almost all XML transformations -- all under the JAXP umbrella. The result is the addition of the javax.xml.transform package, and a few subpackages, to the JAXP API.

Like the parsing portion of JAXP, performing XML transformations requires three basic steps:

  • Obtain a Transformer factory

  • Retrieve a Transformer

  • Perform operations (transformations)

9.3.2.1. Working with the factory

For the transformation portion of JAXP, the factory you will work with is represented by the class javax.xml.transform.TransformerFactory . This class is analogous to the SAXParserFactory and DocumentBuilderFactory classes that I already covered in both the JAXP 1.0 and 1.1 sections. Of course, simply obtaining a factory instance to work with is a piece of cake:

TransformerFactory factory = TransformerFactory.newInstance( );

Nothing special here, just basic factory design principles at work, in conjunction with a singleton pattern.

Once the factory is available, various options can be set upon the factory. Those options will affect all instances of Transformer (which is covered in a minute) created by that factory. You can also obtain instances of javax.xml.transform.Templates through the TransformerFactory. Templates are an advanced JAXP/TrAX concept, and covered at the end of the chapter.

The first of the options you can work with are attributes. These are not XML attributes, but are similar to the properties used in SAX. Attributes allow options to be passed to the underlying XSL processor, which may be Apache Xalan, SAXON, or Oracle's XSL processor (or, theoretically, any TrAX-compliant processor). They are largely vendor-dependent, though. Like the parsing side of JAXP, a setAttribute( ) method is provided as well as a counterpart, getAttribute( ) . Also like setProperty( ), the mutator method (setAttribute( )) takes an attribute name and Object value. And like getProperty( ), the accessor method (getAttribute( )) takes an attribute name and returns the associated Object value.

Setting an ErrorListener is the second option available. Defined in the javax.xml.transform.ErrorListener interface, an ErrorListener allows problems in transformation to be caught and handled programmatically. If this sounds like org.xml.sax.ErrorHandler, it is very similar. Example 9-6 shows this interface.

Example 9-6. The ErrorListener interface

package javax.xml.transform;

public interface ErrorListener {
    public void warning(TransformerException exception)
        throws TransformerException;
    public void error(TransformerException exception)
        throws TransformerException;
    public void fatalError(TransformerException exception)
        throws TransformerException;
}

Creating an implementation of this interface, filling the three callback methods, and using the setErrorListener( ) method on the TransformerFactory instance you are working with sets you up to deal with any errors that occur during transformation.

Finally, a method is provided to set and retrieve the URI resolver for the instances generated by the factory. The interface defined in javax.xml.transform.URIResolver also behaves similarly to a SAX counterpart, org.xml.sax.EntityResolver. The interface has a single method, shown in Example 9-7.

Example 9-7. The URIResolver interface

package javax.xml.transform;

public interface URIResolver {
    public Source resolve(String href, String base)
        throws TransformerException;
}

This interface, when implemented, allows URIs found in XSL constructs like xsl:import and xsl:include to be handled. Returning a Source (which I'll cover in a moment), you can instruct your transformer to search for the specified document in various locations when a particular URI is encountered. For example, when an include of the URI http://www.oreilly.com/oreilly.xsl is encountered, you might instead return the local document alternateOreilly.xsl and prevent the need for network access. Implementations of the URIResolver interface can be set using the TransformerFactory's setURIResolver( ) method, and retrieved using the getURIResolver( ) method.

Finally, once you set the options of your choice, you can obtain an instance, or instances, of a Transformer through the newTransformer( ) method of the factory, as shown here:

    // Get the factory
    TransformerFactory factory = TransformerFactory.newInstance( );

    // Configure the factory
    factory.setErrorResolver(myErrorResolver);
    factory.setURIResolver(myURIResolver);

    // Get a Transformer to work with, with the options specified
    Transformer transformer =
        factory.newTransformer(new StreamSource("foundation.xsl"));

As you can see, this method takes the stylesheet as input to use in all transformations for that Transformer instance. In other words, if you wanted to transform a document using stylesheet A and stylesheet B, you would need two Transformer instances, one for each stylesheet. If you wanted to transform multiple documents with the same stylesheet (call it stylesheet C), however, you would need only a single Transformer instance, associated with stylesheet C. Don't worry about the StreamSource class; that's coming next.

9.3.2.2. Transforming XML

Once you have an instance of a Transformer, you can go about actually performing XML transformations. This consists of two basic steps:

  • Set the XSL stylesheet to use

  • Perform the transformation, specifying the XML document and result target

As I have demonstrated, the first step is really the easiest. A stylesheet can be supplied when obtaining a Transformer instance from the factory. The location of this stylesheet must be specified by providing a javax.xml.transform.Source instance (actually an instance of an implementation of the Source interface) for its location. The Source interface, which you've seen in a few code samples, is the means of locating an input, be it a stylesheet, document, or other information set. TrAX provides the Source interface and three concrete implementations:

  • javax.xml.transform.stream.StreamSource

  • javax.xml.transform.dom.DOMSource

  • javax.xml.transform.sax.SAXSource

The first of these, StreamSource , reads input from some type of I/O device. Constructors are provided for accepting an InputStream, a Reader, or a String system ID as input. Once created, the StreamSource can be passed to the Transformer for use. This will probably be the Source implementation you use most commonly in programs. It's great for reading a document from a network, input stream, user input, or other static representation of XSL stylesheets.

The next Source implementation, DOMSource, provides for reading from an existing DOM tree. It provides a constructor for taking in a DOM org.w3c.dom.Node, and will read from that Node when used. This is ideal for supplying an existing DOM tree to a transformation, perhaps if parsing has already occurred and an XML document is already in memory as a DOM structure, or if you've built a DOM tree programmatically.

SAXSource provides for reading input from SAX producers. This Source implementation takes either a SAX org.xml.sax.InputSource, or an org.xml.sax.XMLReader as input, and uses the events from these sources. This is ideal for situations in which a SAX content handler is already in use, and callbacks are set up and need to be triggered prior to transformations.

Once you've obtained an instance of a Transformer (by providing the stylesheet to use through an appropriate Source), you're ready to perform a transformation. The transform( ) method is used as shown here:

    // Get the factory
    TransformerFactory factory = TransformerFactory.newInstance( );

    // Configure the factory
    factory.setErrorResolver(myErrorResolver);
    factory.setURIResolver(myURIResolver);

    // Get a Transformer to work with, with the options specified
    Transformer transformer =
        factory.newTransformer(new StreamSource("foundation.xsl"));

    // Perform transformation on myDocument, and print out result
    transfomer.transform(new StreamSource("asimov.xml"),
                         new StreamResult("results.xml"));

The transform( ) method takes two arguments: a Source implementation, and a javax.xml.transform.Result implementation. You should already be seeing the symmetry in how this works and have an idea about the functionality within the Result interface. The Source provides the XML document to be transformed, and the Result provides an output target for the transformation. Like Source, there are three concrete implementations of the Result interface provided with TrAX and JAXP:

  • javax.xml.transform.stream.StreamResult

  • javax.xml.transform.dom.DOMResult

  • javax.xml.transform.sax.SAXResult

The StreamResult class takes as a construction mechanism either an OutputStream (like System.out for easy debugging!), a Java File, a String system ID, or a Writer. DOMResult takes a DOM Node to output the transformation to (presumably as a DOM org.w3c.dom.Document), and SAXResult takes a SAX ContentHandler instance to fire callbacks to, resulting from the transformed XML. All are analogous to their Source counterparts.

While the previous example shows transforming from a stream to a stream, any combination of sources and results is possible. Here are a few examples:

    // Perform transformation on jordan.xml, and print out result
    transformer.transform(new StreamSource("jordan.xml"),
                         new StreamResult(System.out));

    // Transform from SAX and output results to a DOM Node
    transformer.transform(new SAXSource(
                              new InputSource(
                                  "http://www.oreilly.com/catalog.xml")),
                           new DOMResult(DocumentBuilder.newDocument( )));

    // Transform from DOM and output to a File
    transformer.transform(new DOMSource(domTree),
                          new StreamResult(
                              new FileOutputStream("results.xml")));

    // Use a custom source and result (JDOM)
    transformer.transform(new org.jdom.trax.JDOMSource(myJdomDocument),
                          new org.jdom.trax.JDOMResult(
                              new org.jdom.Document( )));

TrAX provides tremendous flexibility in moving from various input types to various output types, and in using XSL stylesheets in a variety of formats, such as files, in-memory DOM trees, SAX readers, and so on.

9.3.2.3. Odds and ends

Before closing shop on JAXP, there are a few bits and pieces of TrAX I haven't yet talked about. I won't treat these completely, as they are less commonly used, but I will touch on them briefly. First, TrAX introduces an interface called SourceLocator, also in the javax.xml.transform package. This class functions for transformations exactly as the Locator class did for SAX parsing: it supplies information about where action is occurring. Most commonly used for error reporting, the interface looks like this:

package javax.xml.transform;

public interface SourceLocator {
    public int getColumnNumber( );
    public int getLineNumber( );
    public String getPublicId( );
    public String getSystemId( );
}

I won't comment much on this interface, as it's pretty self-explanatory. However, you should know that in the javax.xml.transform.dom package, there is a subinterface called DOMLocator. This interface adds the getOriginatingNode( ) method, which returns the DOM node being processed. This makes error handling quite easy when working with a DOMSource, and is useful in applications that work with DOM trees.

TrAX also provides a concrete class, javax.xml.transform.OutputKeys, which defines several constants for use in output properties for transformations. These constants can then be used for setting properties on a Transformer or a Templates object. That leads me to the last subject dealing with TrAX.

The Templates interface in TrAX is used when a set of output properties is desired across multiple transformations, or when a set of transformation instructions can be used repeatedly. By supplying a Source to a TransformerFactory's newTemplates( ) method, you get an instance of the Templates object:

// Get a factory
TransformerFactory factory = TransformerFactory.newInstance( );

// Get a Templates object
Templates template = factory.newTemplates(new StreamSource("html.xsl"));

At this point, the template object would be a compiled representation of the transformation detailed in html.xsl (in this example, a stylesheet that converts XML to HTML). By using a Templates object, transformations can be performed from this template across threads, and you also get some optimizations, because instructions are precompiled. Once you have gone that far, you need to generate a Transformer, but from the Templates object, rather than the factory:

// Get a transformer
Transformer transformer = template.newTransformer( );

// Transform
transformer.transform(new DOMSource(orderForm),
                      new StreamResult(res.getOutputStream( )));

Here, there is no need to supply a Source to the newTransformer( ) method, as the transformer is simply a set of (already) compiled instructions. From there, it's business as usual. In this example, a DOM tree that represents an order form is supplied to the transformation, processed using the html.xsl stylesheet, and then sent to a servlet's output stream for display. Pretty slick, huh? As a general rule, if you are going to use a stylesheet more than twice, use a Templates object; it will pay off in performance. Additionally, anytime you are dealing with threads, Templates are the only way to go.

Previous | Home | Next
9.2. JAXP 1.0 | Book Index | 9.4. Gotcha!
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Gotcha! (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

9.4. Gotcha!

The API chapters wouldn't be complete without letting you know about some problems that I frequently run into or that I'm asked about. Hopefully they'll help save you some time, and maybe make your code more bug-proof. Read on, and see where JAXP catches folks these days.

9.4.1. Default Parsers and JAXP Implementations

It's worth saying again: the implementation of JAXP determines the default parser. If you switch the JAXP implementation, you often end up switching the parser that is used, if you haven't set any system properties for JAXP. Your classpath may have to change, or you will get all sorts of ClassNotFoundExceptions.

To avoid this problem completely, you could simply set the relevant JAXP system property to the parser factory you want to use, and regardless of what implementation you choose, you'll get expected behavior. Or better yet, put a jaxp.properties file in the lib directory of your Java installation.[15] This file can be as simple as this:

[15]This option assumes that you have set the JAVA_HOME environment variable to the installation directory of your JDK. It assumes that because it's a good, if not mandatory, practice and will help you out in the long term. JAXP looks, in actuality, for %JAVA_HOME%/lib/jaxp.properties.

javax.xml.parsers.SAXParserFactory	org.apache.xerces.XercesFactory

By changing the factory implementation, you change the parser wrapper that is returned from calls to newSAXParser( ). And lest you try the example file given, the org.apache.xerces.XercesFactory class doesn't exist; it's just for example purposes. It happened to fit within the confines of the code block!

9.4.2. Features on Factories, Properties on Parsers

One common mistake is to mix up factories and properties in the JAXP world. The best way to remember the correct application is to memorize the phrase "features on factories, properties on parsers." You would be amazed at the number of mails I get insisting that the sender has a "corrupt" version of JAXP, because the following code won't compile:

SAXParserFactory factory = SAXParserFactory.newInstance( );
factory.setProperty(
    "http://apache.org/xml/properties/dom/document-class-name",
    "org.apache.xerces.dom.DocumentImpl");

Of course, this is a property, and therefore must be set on a SAXParser instance, not a SAXParserFactory instance. The reverse, of course, holds true for setting features on parsers:

SAXParser parser = factory.newSAXParser( );
parser.setFeature("http://xml.org/sax/features/namespaces", true);

In either case, it is user error, not a strange download problem where all but a few methods came across OK (I generally refer these people to some good books on I/O). This is also a good case of the Javadocs not being used when they should. I'm a firm believer in the value of Javadoc.

Previous | Home | Next
9.3. JAXP 1.1 | Book Index | 9.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

9.5. What's Next?

Because JAXP is an abstraction layer on top of the APIs already discussed in the earlier chapters, there's no need to go into "Advanced JAXP." Additionally, the JAXP concepts are simple enough to not warrant an additional chapter. With this tour through the various "low-level" Java and XML APIs, you should have all the hammers and wrenches needed for your XML programming.

However, there's certainly more to XML than low-level APIs these days. In addition to vertical applications of XML, there are a number of high-level APIs coming out that build on top of the concepts (and APIs) in the first half of this book to provide more convenience to the developer. These more specific concepts and programming tools are the backbone of the second half of this book. I'll begin the discussion in Chapter 10, "Web Publishing Frameworks" by talking about presentation frameworks, something that provides a lot of eye candy on top of XML. Read on, and we'll all be graphic designers for a chapter or so.

Previous | Home | Next
9.4. Gotcha! | Book Index | 10. Web Publishing Frameworks
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Web Publishing Frameworks (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 10. Web Publishing Frameworks

Contents:

Selecting a Framework
Installation
Using a Publishing Framework
XSP
Cocoon 2.0 and Beyond
What's Next?

This chapter begins our examination of specific Java and XML topics. I have covered the basics of using XML from Java, looking at the SAX, DOM, JDOM, and JAXP APIs to manipulate XML, and the fundamentals of using and creating XML itself. Now that you have a grasp on using XML from your code, I want to spend time on specific applications. The next six chapters cover the most significant applications of XML, and, in particular, how those applications are implemented in the Java space. While there are literally thousands of important applications of XML, the topics in these chapters are those continually in the spotlight, with the potential to significantly change the way traditional development processes occur.

The More Things Change, the More They Stay the Same

Readers of the first edition will find that much of the Cocoon discussion in this chapter is the same. Although I promised Cocoon 2 would be out by now and expected to be writing a chapter on it, things haven't progressed as quickly as expected. Stefano Mazzochi, the driving force behind Cocoon, finally got around to finishing school (good choice, Stefano!), and development on Cocoon 2 slowed as a result. Cocoon 1.x is still the current development path, so stick with it for now. I've updated the section on Cocoon 2 to reflect what is coming. Keep an eye out for more Cocoon-related books from O'Reilly in the months to come.

The first hot topic I look at is the XML application that has generated the most excitement in the XML and Java communities: the web publishing framework. Although I have continually emphasized that generating presentation from content is perhaps overhyped compared to the value of the portable data that XML provides, using XML for presentation styling is still very important. This importance increases when looking at web-based applications.

Virtually every major application I can find is either completely web-based or at least has a web frontend. At the same time, users are demanding more functionality, and marketing departments are demanding more flexibility in look and feel. The result has been the rise of the web artist; this new role is different from the webmaster in that little to no Perl, ASP, JavaScript, or other scripting language coding is part of the job description. The web artist's entire day is comprised of HTML and WML creation, modification, and development.[16] The rapid changes in business and market strategy can require a complete application or site overhaul as often as once a week, often forcing the web artist to spend days changing hundreds of HTML pages. While Cascading Style Sheets (CSS) have helped, the difficulty of maintaining consistency across these pages requires a huge amount of time. Even if this less-than-ideal situation were acceptable, no computer developer wants to spend his or her life making markup language changes to web pages.

[16]"HTML and WML" includes the tangential technologies used with the markup language. These complementary technologies, like Flash and Shockwave, are not trivial, so I'm by no means belittling these content authors.

With the advent of server-side Java, the problem has only grown. Servlet developers find themselves spending long hours modifying their out.println( ) statements to output HTML, and often glance hatefully at the marketing department when changes to a site's look require modifications to their code. The entire Java Server Pages (JSP) specification arguably stemmed from this situation; however, JSP is not a solution, as it only shifts the frustration to the content author, who constantly has to avoid making incidental changes to embedded Java code. In addition, JSP does not provide the clean separation between content and presentation it promises. A means to generate pure data content was called for, as well as a means to have that content uniformly styled either at predetermined times (static content generation) or dynamically at runtime (dynamic content generation).

Of course, you may be nodding your head at this familiar problem if you have ever done any web development, and hopefully your mind is wandering into the XSL and XSLT technology space. The problem is that an engine must exist to handle content generation, particularly in the dynamic sense. Having hundreds of XML documents on a site does no good if there is no mechanism to apply transformations on request. Add the need for servlets and other server-side components to output XML that should be consistently styled, and you have defined a small set of requirements for the web publishing framework. In this chapter, I take a look at this framework, how it allows you to avoid long hours of HTML coding, and how it helps you convert all of those "web artists" into XML and XSL gurus, allowing applications to change look and feel as often as desired.

A web publishing framework attempts to address these complicated issues. Just as a web server is responsible for responding to a URL request for a file, a web publishing framework is responsible for responding to a similar request; however, instead of responding with a file, it often will respond with a published version of a file. In this case, a published file refers to a file that may have been transformed with XSLT, massaged at an application level, or converted into another format such as a PDF. The requestor does not see the raw data that may underlie the published result, but also does not have to explicitly request that publication occur. Often, a URI base (such as http://yourHost.com/publish) signifies that a publishing engine that sits on top of the web server should handle requests. As you may suspect, the concept is much simpler than the actual implementation of a framework like this, and finding the correct framework for your needs is not a trivial task.

10.1. Selecting a Framework

You might expect to find a list of hundreds of possible solutions. As you've seen, the Java language offers an easy interface into XML through several APIs. Additionally, Java servlets offer a simple means of handling web requests and responses. However, the list of frameworks is small, and the list of good, stable ones is even smaller. One of the best resources for seeing what products are currently available is XML Software's list at http://xmlsoftware.com/publishing/. This list changes so frequently that it is not worth repeating here. Still, some important criteria for determining what framework is right for you are worth mentioning.

10.1.1. Stability

Don't be surprised if you (still!) have a hard time finding a product whose version tag is greater than 2.x. In fact, you may have to search diligently to even find a second-generation framework. While a higher version number is not a guarantee of stability, it often reflects the amount of time, effort, and review that a framework has undergone. The XML publishing system is such a new beast that the market has been flooded with 1.0 and 1.1 products that simply are not stable enough for practical use.

You can often ascertain the stability of a product by investigating other products from the same vendor. Often a vendor releases an entire suite of tools; if their other tools do not offer SAX 2.0 and DOM Level 2 support, or are all also 1.0 and 1.1 products, you might be wise to pass on the framework until it has matured and conformed to newer XML standards. Try to steer away from platform-specific technologies. If the framework is tied to a platform (such as Windows, or even a specific flavor of Unix), you aren't dealing with a pure Java solution. Remember that a publishing framework must serve clients on any platform; why use a product that can't run on any platform?

10.1.2. Integration with Other XML Tools and APIs

Once you know your framework is stable enough for your needs, make sure it supports a variety of XML parsers and processors. If your framework is tied to a specific parser or processor, you will be limited to one specific implementation of a technology. This is a bad thing. Although frameworks often integrate well with a particular parser vendor, determine if parsers can be interchanged. If you have a favorite processor (or one left to you from previous projects), make sure it can still be used.

Support for SAX and DOM is a must, and many frameworks now support JDOM and JAXP as well. Even if you have a favorite API, the more options you have, the better! Also, try to find a framework whose developers are monitoring the specifications of XML Schema, XLink, XPointer, and other XML vocabularies. This will indicate if you can expect to see revisions of the framework that add support for these XML specifications, an important indication of the framework's longevity. Don't be afraid to ask questions about how quickly new specifications are expected to be integrated into the product, and insist on a firm answer.

10.1.3. Production Presence

The last and perhaps most important question to answer when looking for a web publishing framework is whether it is used in production applications. If you aren't supplied with at least a few reference applications or sites that are using the framework, don't be surprised if there aren't any. Vendors (and developers, in the open source realm) should be happy and proud to let you know where to check out their frameworks in action. Hesitance in this area is a sign that you may be more of a pioneer with a product than you wish to be. For example, Apache Cocoon provides just such a list online, at http://xml.apache.org/cocoon/livesites.html.

10.1.4. Making the Decision

Once you have evaluated these criteria, you will probably have a clear choice. Very few frameworks can positively answer all the questions raised here, not to mention your application-specific concerns. In fact, as of July 2001, less than ten publishing frameworks exist that support the latest versions of SAX (Version 2.0), DOM (Level 2), and JAXP (Version 1.1) are in production use at even one application site, and have at least three significant revisions of code under their belt. These are not listed here because, honestly, in six months they may not exist, or may be radically changed. The world of web publishing frameworks is in such flux that trying to recommend four or five options and assuming they will be in existence months from now has a greater chance of misleading you than helping you.

However, one publishing framework has been consistently successful within the Java and XML community. When considering the open source community in particular, this framework is often the choice of Java developers. The Apache Cocoon project, founded by Stefano Mazzocchi, has been a solid framework since its inception. Developed while most of us were still trying to figure out what XML was, Cocoon is now entering its second generation as an XML publishing framework based completely in Java. It also is part of the Apache XML project, and has default support for Apache Xerces and Apache Xalan. It allows any conformant XML parser to be used, and is based on the immensely popular Java servlet architecture. In addition, there are several production sites using Apache Cocoon (in its 1.x form) that push the boundaries of traditional web application development yet still perform extremely well. For this reason, and again in keeping with the spirit of open source software, I use Apache Cocoon as the framework of choice in this chapter.

In previous chapters, the choice of XML parser and processor was fairly open; in other words, examples would work on different vendor implementations with only small modifications to code. However, the web publishing framework is not standardized, and each framework implements wildly different features and conventions. For this reason, the examples in this chapter using Apache Cocoon are not portable; however, the popularity of the concepts and design patterns used within Cocoon do merit an entire chapter. If you do not choose Cocoon, at least look over the examples. The concepts in web publishing are usable across any vendor implementation, even if the specifics of the code are not.

Previous | Home | Next
9.5. What's Next? | Book Index | 10.2. Installation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Installation (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

10.2. Installation

In other chapters, installation instructions generally involved pointing you at a web site where you could obtain a distribution of the software and letting you add the included jar file to your classpath. Installing a framework such as Cocoon is not quite as simple, and the procedures are documented here. Additionally, Cocoon has instructions online for various other servlet engines; check these out at http://xml.apache.org/cocoon/install.html.

10.2.1. Source Code or Binaries

The first thing you need to do is decide if you want the source code or binaries for Cocoon. This decision actually can be boiled down even further: do you want the very latest features, or the most reliable build? If you are a hardcore developer who wants to dig into Cocoon, you should get a copy of CVS and pull the latest Cocoon source code from the xml.apache.org CVS repository. Rather than detail this process, as it probably involves the minority of you, I'll simply refer you to the CVS Pocket Reference by Gregor Purdy (O'Reilly). This will get you set up, in concert with the instructions online at http://xml.apache.org/cvs.html.

For those interested in trying Cocoon out or actually running it in production, download the latest Cocoon binary from http://xml.apache.org/cocoon/dist. As I write, the latest version, 1.8.2, is available for Windows (Cocoon-1.8.2.zip) and Linux/Unix (Cocoon-1.8.2.tar.gz). Once you download the archive, expand it to a temporary directory that you can work with. The most important thing to note here is the lib/ directory that's created. This directory includes all of the libraries needed to run Cocoon using your servlet engine.

NOTE: If you don't have a lib/ directory, or if it doesn't contain several jar files within it, you may have an older version of Cocoon. It's only in the newer releases (1.8 and up) that the download contains these libraries (which make life significantly easier, by the way!).

10.2.2. Configuring the Servlet Engine

Once you have built Cocoon, configure your servlet engine to use Cocoon and tell it which requests Cocoon should handle. I'll look at setting up Cocoon to work with the Jakarta Tomcat servlet engine here; as this is the reference implementation for the Java Servlet API (Version 2.2), you should be able to mimic these steps for your own servlet engine if you are not using the Tomcat implementation.

The first step is to copy all of the libraries needed for Cocoon at runtime into Tomcat's library directory. This is located at TOMCAT_HOME/lib, where TOMCAT_HOME is the directory of your Tomcat installation. On my Windows machine, this is c:\java\jakarta-tomcat, and on Linux it's /usr/local/jakarta-tomcat. However, this does not mean simply copy everything in Cocoon's lib/ directory over (unless you want to); the required jar files needed at runtime are:

  • bsfengines.jar (Bean Scripting Framework)

  • bsf.jar (Bean Scripting Framework)

  • fop_0_15_0.jar (FOP)

  • sax-bugfix.jar (SAX fixes to error handling)

  • turbine-pool.jar (Turbine)

  • w3c.jar (W3C)

  • xalan_1_2_D02.jar (Xalan)

  • xerces_1_2.jar (Xerces)

Additionally, copy Cocoon's bin/cocoon.jar file into this same directory (TOMCAT_HOME/lib). At that point, you'll have all the libraries needed to run Cocoon.

The latest versions of Tomcat (I'm using 3.2.1) automatically load all libraries in the Tomcat lib/ directory, which means you don't have to mess with the classpath. If you are using a servlet engine that doesn't support this automatic loading, add each jar to the servlet engine's classpath.

Once the required libraries are in place, let the servlet engine know which context to run Cocoon under. This essentially tells the servlet engine where to look for files requested through the Cocoon engine. This is handled by modifying the server.xml file, located in Tomcat's conf/ directory. Add the following directive in at the end of the file, within the ContextManager element:

<Server>
  <!-- Other Server elements -->

  <ContextManager>
    <!-- Other Context directives -->

    <Context path="/cocoon"
             docBase="webapps/cocoon"
             debug="0"
             reloadable="true" >
    </Context>
  </ContextManager>
</Server>

In other words, requests based on the URI /cocoon (such as /cocoon/index.xml) should be mapped to the context within the specified directory (webapps/cocoon). Of course, you'll need to create the directories for the context you've just defined. So add a cocoon and cocoon/WEB-INF directory to Tomcat's webapps directory. You should have a directory structure similar to Figure 10-1.

Figure 10-1. Cocoon context directory structure

With this setup, you'll need to copy a few files from the Cocoon distribution into the context. Copy Cocoon's conf/cocoon.properties and src/WEB-INF/web.xml files into the TOMCAT_HOME/webapps/cocoon/WEB-INF/ directory. Once this is in place, you only need to modify the web.xml file that you just copied. Change the reference in it to point to the cocoon.properties file you just copied over:

<web-app>
 <servlet>
  <servlet-name>org.apache.cocoon.Cocoon</servlet-name>
  <servlet-class>org.apache.cocoon.Cocoon</servlet-class>
  <init-param>
   <param-name>properties</param-name>
   <param-value>WEB-INF/cocoon.properties</param-value>
  </init-param>
 </servlet>

 <servlet-mapping>
  <servlet-name>org.apache.cocoon.Cocoon</servlet-name>
  <url-pattern>*.xml</url-pattern>
 </servlet-mapping>
</web-app>

At this point, you have one last, rather annoying, step to perform. Tomcat automatically loads all the jar files in its lib/ directory, and it does it alphabetically, according to the name of the jar file. The problem is that Cocoon requires a DOM Level 2 implementation (such as the one in Xerces, included with Cocoon in xerces_1_2.jar); however, Tomcat uses a DOM Level 1 implementation, included in parser.jar. Of course, because of the alphabetical listing, parser.jar gets loaded before xerces_1_2.jar, and Cocoon bombs out. To solve this, rename your parser.jar archive something that will get loaded after Xerces; I used z_parser.jar. This step ensures that the classes are still available to Tomcat, but that the DOM Level 2 classes are loaded first and used by Cocoon.

Once you complete these steps, test Cocoon by loading up the Cocoon information URI, which reports details about Cocoon's installation. Access http://[hostname:port]/cocoon/Cocoon.xml. In a default installation, this would be http://localhost:8080/cocoon/Cocoon.xml. Your browser should give you results similar to those in Figure 10-2.

Figure 10-2. Checking the Cocoon installation

Once this is set up, you're ready to put some real content into place. With the setup you already have, all requests that end in .xml and are within the defined Cocoon context will be handled by the Cocoon servlet.

Previous | Home | Next
10. Web Publishing Frameworks | Book Index | 10.3. Using a Publishing Framework
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Using a Publishing Framework (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

10.3. Using a Publishing Framework

Using a good publishing framework like Cocoon doesn't require any special instruction; it is not a complex application that users must learn to adapt to. In fact, all Cocoon's uses are based on simple URLs entered into a standard web browser. Generating dynamic HTML from XML, viewing XML transformed into PDF files, and even generating VRML applications from XML is simply a matter of typing the URL to the desired XML file into your browser and watching Cocoon and the power of XML take action.

10.3.1. Viewing XML Converted to HTML

Now that your framework is in place and is correctly handling requests ending in .xml, we begin to see it publish our XML files. Cocoon comes with several sample XML files and associated XSL stylesheets in the project's samples/ subdirectory. However, you have your own XML and XSL from earlier chapters by now, so let's transform the XML table of contents for this book (contents.xml) with the XSL stylesheet (JavaXML.html.xsl), both from Chapter 2, "Nuts and Bolts". Locate where you saved the XML file, and copy it into Cocoon's document root, webapps/cocoon/. The document refers to the stylesheet XSL/JavaXML.html.xsl. Create the XSL/ directory in your web document root, and copy the stylesheet into that directory. The XML document also references a DTD; you will need to either comment that out, or create a DTD/ directory and copy the JavaXML.dtd file, also from Chapter 2, "Nuts and Bolts", into that directory.

Once you have the XML document and its stylesheet in place, you can access it with the URL http://<hostname>:<port>/cocoon/contents.xml in your web browser. Assuming you followed the earlier instructions to get Cocoon running, the transformed XML should look like Figure 10-3.

Figure 10-3. Cocoon in action on contents.xml

This should be almost trivial; once Cocoon is set up and configured, serving up dynamic content is a piece of cake! The mapping from XML extensions to Cocoon works for any requests within the context in which you set up Cocoon.

10.3.2. Viewing PDFs from XML

In the discussions concerning using XML for presentation, I've focused on XML converted to HTML. However, that's just scratching the surface of formats that XML can be converted to. Not only is a variety of markup languages supported as final document formats, but in addition, Java provides libraries for converting XML to some non-markup-based formats. The most popular and stable library in this category is the Apache XML group's Formatting Objects Processor, FOP. This gives Cocoon or any other publishing framework the ability to turn XML documents into Portable Document Format (PDF) documents, which are generally viewed with Adobe Acrobat (http://www.adobe.com).

The importance of converting a document from XML into a PDF cannot be overstated; particularly for document-driven web sites, such as print media or publishing companies, it could revolutionize web delivery of data. Consider the following XML document, an XML-formatted excerpt from this chapter, shown in Example 10-1.

Example 10-1. XML version of Java and XML

<?xml version="1.0"?>

<?cocoon-process type="xslt"?>
<?xml-stylesheet href="XSL/JavaXML.fo.xsl" type="text/xsl"?>

<book>
 <cover>
  <title>Java and XML</title>
   <author>Brett McLaughlin</author>
 </cover>

 <contents>
  <chapter title="Web Publishing Frameworks" number="10">

   <paragraph> This chapter begins looking at specific Java and XML
topics. So far, I have covered the basics of using XML from Java,
looking at the SAX, DOM, JDOM, and JAXP APIs to manipulate XML and the
fundamentals of using and creating XML itself. Now that you have a grasp
on using XML from your code, I want to spend time on specific
applications. The next six chapters represent the most significant
applications of XML, and, in particular, how those applications are
implemented in the Java space. While there are literally thousands of
important applications of XML, the topics in these chapters are those
that continually seem to be in the spotlight, and that have a significant
potential to change the way traditional development processes occur.
   </paragraph>

    <sidebar title="The More Things Change, the More They Stay the Same">
Readers of the first edition of this book will find that
much of this chapter on Cocoon is the same as the first edition. Although
I promised you that Cocoon 2 would be out by now, and although I expected
to be writing a chapter on Cocoon 2, things haven't progressed as quickly
as expected. Stefano Mazzochi, the driving force behind Cocoon, finally
got around to finishing school (good choice, Stefano!), and so
development on Cocoon 2 has significantly slowed. The result is that
Cocoon 1.x is still the current development path, and you should stick
with it for now. I've updated the section on Cocoon 2 to reflect what is
coming, and you should keep an eye out for more Cocoon-related books from
O'Reilly in the months to come.</sidebar>

   <paragraph> I'll begin this look at hot topics with the one XML
application that seems to have generated the largest amount of excitement
in the XML and Java communities: the web publishing framework. Although
I have continually emphasized that generating presentation from content
is perhaps over-hyped when compared to the value of the portable data
that XML provides, using XML for presentation styling is still very
important. This importance increases when looking at web-based
applications.</paragraph>
  </chapter>

 </contents>
</book>

You saw how an XSL stylesheet allows you to transform this document into HTML. But converting an entire chapter of a book into HTML could result in a gigantic HTML document, and certainly an unreadable format; potential readers wanting online delivery of a book generally prefer a PDF document. On the other hand, generating PDF statically from the chapter means that changes to the chapter must be matched with subsequent PDF file generation. Keeping a single XML document format means the chapter can be easily updated (with any XML editor), formatted into SGML for printing hard copy, transferred to other companies and applications, and included in other books or compendiums. Now add the ability for web users to type in a URL and access the book in PDF format to this robust set of features, and you have a complete publishing system.

Although I don't cover formatting objects and the FOP for Java libraries in detail, you can review the entire formatting objects definition within the XSL specification at the W3C at http://www.w3.org/TR/xsl/. Example 10-2 is an XSL stylesheet that uses formatting objects to specify a transformation from XML to a PDF docu ment, appropriate for the XML version of this chapter.

Example 10-2. XSL stylesheet for PDF transformation

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <xsl:template match="book">
    <xsl:processing-instruction name="cocoon-format">
      type="text/xslfo"
    </xsl:processing-instruction>
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
      <fo:layout-master-set>
      <fo:simple-page-master
        master-name="right"
        margin-top="75pt"
        margin-bottom="25pt"
        margin-left="100pt"
        margin-right="50pt">
        <fo:region-body margin-bottom="50pt"/>
        <fo:region-after extent="25pt"/>
      </fo:simple-page-master>
      <fo:simple-page-master
        master-name="left"
        margin-top="75pt"
        margin-bottom="25pt"
        margin-left="50pt"
        margin-right="100pt">
        <fo:region-body margin-bottom="50pt"/>
        <fo:region-after extent="25pt"/>
      </fo:simple-page-master>
      <fo:page-sequence-master master-name="psmOddEven">
        <fo:repeatable-page-master-alternatives>
          <fo:conditional-page-master-reference
              master-name="right"
              page-position="first"/>
          <fo:conditional-page-master-reference
              master-name="right"
              odd-or-even="even"/>
          <fo:conditional-page-master-reference
              master-name="left"
              odd-or-even="odd"/>
          <!-- recommended fallback procedure -->
          <fo:conditional-page-master-reference
              master-name="right"/>
        </fo:repeatable-page-master-alternatives>
      </fo:page-sequence-master>
      </fo:layout-master-set>

      <fo:page-sequence master-name="psmOddEven">

        <fo:static-content flow-name="xsl-region-after">
          <fo:block text-align-last="center" font-size="10pt">
            <fo:page-number/>
          </fo:block>
        </fo:static-content>

        <fo:flow flow-name="xsl-region-body">
          <xsl:apply-templates/>
        </fo:flow>
      </fo:page-sequence>

    </fo:root>
  </xsl:template>

  <xsl:template match="cover">
    <fo:block font-size="10pt"
              space-before.optimum="10pt">
      <xsl:value-of select="title"/>
      (<xsl:value-of select="author"/>)
    </fo:block>
  </xsl:template>

  <xsl:template match="contents">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="chapter">
    <fo:block font-size="24pt"
              text-align-last="center"
              space-before.optimum="24pt">
      <xsl:value-of select="@number" />.
      <xsl:value-of select="@title" />
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>

  <xsl:template match="paragraph">
    <fo:block font-size="12pt"
              space-before.optimum="12pt"
              text-align="justify">
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>

  <xsl:template match="sidebar">
    <fo:block font-size="14pt"
              font-style="italic"
              color="blue"
              space-before.optimum="16pt"
              text-align="center">
      <xsl:value-of select="@title" />
    </fo:block>
    <fo:block font-size="12pt"
              color="blue"
              space-before.optimum="16pt"
              text-align="justify">
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>
</xsl:stylesheet>

If you create both of these files, saving the chapter as chapterTen.xml, and the XSL stylesheet as JavaXML.fo.xsl within a subdirectory called XSL/, you can see the result of the transformation in a web browser. Make sure you have the Adobe Acrobat Reader and plug-in for your web browser, and then access the XML document just created. Figure 10-4 shows the results.

Figure 10-4. PDF transformation result from chapterTen.xml

10.3.3. Browser-Dependent Styling

In addition to specifically requesting certain types of transformations, such as a conversion to a PDF, Cocoon allows for dynamic processing to occur based on the request. A common example of this is applying different formatting based on the media of the client. In a traditional web environment, this allows an XML document to be transformed differently based on the browser being used. A client using Internet Explorer could be served a different presentation than a client using Netscape; with the recent wars between versions of HTML, DHTML, and JavaScript brewing between Netscape and Microsoft, this is a powerful feature to have available. Cocoon provides built-in support for many common browser types. Locate the cocoon.properties file you referenced earlier, open it, and scroll to the bottom of the file. You will see the following section (this may be slightly different for newer versions):

##########################################
# User Agents (Browsers)                 #
##########################################

# NOTE: numbers indicate the search order. This is VERY VERY IMPORTANT
# since some words may be found in more than one browser description.
# (MSIE is presented as "Mozilla/4.0 (Compatible; MSIE 4.01; ...")
#
# for example, the "explorer=MSIE" tag indicates that the XSL stylesheet
# associated to the media type "explorer" should be mapped to those
# browsers that have the string "MSIE" in their "user-Agent" HTTP header.

browser.0 = explorer=MSIE
browser.1 = pocketexplorer=MSPIE
browser.2 = handweb=HandHTTP
browser.3 = avantgo=AvantGo
browser.4 = imode=DoCoMo
browser.5 = opera=Opera
browser.6 = lynx=Lynx
browser.7 = java=Java
browser.8 = wap=Nokia
browser.9 = wap=UP
browser.10 = wap=Wapalizer
browser.11 = mozilla5=Mozilla/5
browser.12 = mozilla5=Netscape6/
browser.13 = netscape=Mozilla

The keywords after the first equals sign are the items to take note of: explorer, lynx, java, and mozilla5, for example, all differentiate between different user-agents, the codes the browsers send with requests for URLs. As an example of applying stylesheets based on this property, you can create a sample XSL stylesheet to apply when the client accesses the XML table of contents (contents.xml) document with Internet Explorer. Copy the original XML-to-HTML stylesheet, JavaXML.html.xsl, to JavaXML.explorer-html.xsl. Then make the modifications shown in Example 10-3.

Example 10-3. Modified XSL stylesheet for Internet Explorer

<?xml version="1.0"?>

<xsl:stylesheet xmlns:javaxml2="http://www.oreilly.com/javaxml2"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ora="http://www.oreilly.com"
                version="1.0"
>

  <xsl:template match="javaxml2:book">
  <xsl:processing-instruction name="cocoon-format">
    type="text/html"
  </xsl:processing-instruction>
    <html>
      <head>
        <title>
          <xsl:value-of select="javaxml2:title" /> (Explorer Version)
        </title>
      </head>
      <body>
        <xsl:apply-templates select="*[not(self::javaxml2:title)]" />
      </body>
    </html>
  </xsl:template>

  <xsl:template match="javaxml2:contents">
    <center>
     <h2>Table of Contents (Explorer Version)</h2>
     <small>
       Try <a href="http://www.mozilla.org">Mozilla</a> today!
     </small>
    </center>
    <!-- Other XSL directives -->
  </xsl:template>

  <!-- Other XSL template matches -->

</xsl:stylesheet>

While this is a trivial example, dynamic HTML could be inserted for Internet Explorer 5.5, and standard HTML could be used for Netscape Navigator or Mozilla, which have less DHTML support. With this in place, you need to let your XML document know that if the media type (or user-agent) matches up with the explorer type defined in the properties file, a different XSL stylesheet should be used. The additional processing instruction shown in Example 10-4 handles this, and can be added to the contents.xml file.

Example 10-4. Modified contents.xml with media type discernment

<?xml version="1.0"?>
<!DOCTYPE Book SYSTEM "DTD/JavaXML.dtd">
<?xml-stylesheet href="XSL/JavaXML.html.xsl" type="text/xsl"?>
<?xml-stylesheet href="XSL/JavaXML.explorer-html.xsl" type="text/xsl"
                 media="explorer"?>

<?cocoon-process type="xslt"?>

<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>
  <!-- XML content -->
</book>

Accessing the XML in your Netscape browser yields the same results as before; however, if you access the page in Internet Explorer, you will see that the document has been transformed with the alternate stylesheet, and looks like Figure 10-5.

Figure 10-5. contents.xml viewed with Internet Explorer

10.3.4. WAP and XML

One of the real powers in this dynamic application of stylesheets lies in the use of wireless devices. Remember our properties file?

##########################################
# User Agents (Browsers)                 #
##########################################

# NOTE: numbers indicate the search order. This is VERY VERY IMPORTANT
# since some words may be found in more than one browser description.
# (MSIE is presented as "Mozilla/4.0 (Compatible; MSIE 4.01; ...")
#
# for example, the "explorer=MSIE" tag indicates that the XSL stylesheet
# associated to the media type "explorer" should be mapped to those
# browsers that have the string "MSIE" in their "user-Agent" HTTP header.

browser.0 = explorer=MSIE
browser.1 = pocketexplorer=MSPIE
browser.2 = handweb=HandHTTP
browser.3 = avantgo=AvantGo
browser.4 = imode=DoCoMo
browser.5 = opera=Opera
browser.6 = lynx=Lynx
browser.7 = java=Java
browser.8 = wap=Nokia
browser.9 = wap=UP
browser.10 = wap=Wapalizer
browser.11 = mozilla5=Mozilla/5
browser.12 = mozilla5=Netscape6/
browser.13 = netscape=Mozilla

The highlighted entries detect that a wireless agent, such as an Internet-capable phone, is being used to access content. Just as Cocoon detected whether the incoming web browser was Internet Explorer or Netscape, responding with the correct stylesheet, a WAP device can be handled by yet another stylesheet. Add another stylesheet reference in to your contents.xml document:

<?xml version="1.0"?>
<!DOCTYPE Book SYSTEM "DTD/JavaXML.dtd">
<?xml-stylesheet href="XSL/JavaXML.html.xsl" type="text/xsl"?>
<?xml-stylesheet href="XSL/JavaXML.explorer-html.xsl" type="text/xsl"
                 media="explorer"?>
<?xml-stylesheet href="XSL/JavaXML.wml.xsl" type="text/xsl"
                 media="wap"?>

<?cocoon-process type="xslt"?>

<!-- Java and XML Contents -->
<book xmlns="http://www.oreilly.com/javaxml2"
      xmlns:ora="http://www.oreilly.com"
>
  <!-- XML table of contents -->
</book>

Now you need to create this newly referenced stylesheet for WAP devices. The Wireless Markup Language (WML) is typically used when building a stylesheet for a WAP device. WML is a variant on HTML, but has a slightly different method of representing different pages. When a wireless device requests a URL, the returned response must be within a wml element. In that root element, several cards can be defined, each through the WML card element. The device downloads multiple cards at one time (often referred to as a deck) so that it does not have to go back to the server for the additional screens. Example 10-5 shows a simple WML page using these constructs.

Example 10-5. Simple WML page

<wml>
 <card id="index" title="Home Page">
  <p align="left">
   <i>Main Menu</i><br />
   <a href="#title">Title Page</a><br />
   <a href="#myPage">My Page</a><br />
  <p>
 </card>

 <card id="title" title="My Title Page">
  Welcome to my Title Page!<br />
  So happy to see you.
 </card>

 <card id="myPage" title="Hello World">
  <p align="center">
   Hello World!
  </p>
 </card>
</wml>

This simple example serves requests with a menu, and two screens accessed from links within that menu. The complete WML 1.1 specification is available online, along with all other related WAP specifications, at http://www.wapforum.org/what/technical_1_1.htm. You can also pick up a copy of Learning WML and WML Script by Martin Frost (O'Reilly). Additionally, the UP.SDK can be downloaded from http://www.phone.com/products/upsdk.html; this is a software emulation of a wireless device that allows testing of your WML pages. With this software, you can develop an XSL stylesheet to output WML for WAP devices, and test the results by pointing your UP.SDK browser to http://<hostname>:<port>/contents.xml.

Because phone displays are much smaller than computer screens, you want to show only a subset of the information in our XML table of contents. Example 10-6 is an XSL stylesheet that outputs three cards in WML. The first card is a menu with links to the other two cards. The second card generates a table of contents listing from our contents.xml document. The third card is a simple copyright screen. This stylesheet can be saved as JavaXML.wml.xsl in the XSL/ subdirectory of your Cocoon context.

Example 10-6. WML stylesheet

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:javaxml2="http://www.oreilly.com/javaxml2"
                xmlns:ora="http://www.oreilly.com"
                exclude-result-prefixes="javaxml2 ora"
>

 <xsl:template match="javaxml2:book">
  <xsl:processing-instruction name="cocoon-format">
    type="text/wml"
  </xsl:processing-instruction>

  <wml>
   <card id="index" title="{javaxml2:title}">
    <p align="center">
     <i><xsl:value-of select="javaxml2:title"/></i><br />
     <a href="#contents">Contents</a><br/>
     <a href="#copyright">Copyright</a><br/>
    </p>
   </card>

   <xsl:apply-templates select="javaxml2:contents" />

   <card id="copyright" title="Copyright">
    <p align="center">
     Copyright 2000, O&apos;Reilly &amp; Associates
    </p>
   </card>
  </wml>
 </xsl:template>

 <xsl:template match="javaxml2:contents">
  <card id="contents" title="Contents">
   <p align="center">
    <i>Contents</i><br />
    <xsl:for-each select="javaxml2:chapter">
     <xsl:value-of select="@number" />.
     <xsl:value-of select="@title" /><br />
    </xsl:for-each>
   </p>
  </card>
 </xsl:template>

</xsl:stylesheet>

Other than the WML tags, most of this example should look familiar. There is also a processing instruction for Cocoon, with the target specified as cocoon-format. The data sent, type="text/wml", instructs Cocoon to output this stylesheet with a content header specifying that the output is text/wml (instead of the normal text/html or text/plain). There is one other important addition, an attribute added to the root element of the stylesheet:

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:javaxml2="http://www.oreilly.com/javaxml2"
                xmlns:ora="http://www.oreilly.com"
                exclude-result-prefixes="javaxml2 ora"
>

By default, any XML namespace declarations other than the XSL namespace are added to the root element of the transformation output. In this example, the root element of the transformed output, wml, would have the namespace declarations associated with the javaxml2 and ora prefixes added to it:

<wml xmlns:javaxml2="http://www.oreilly.com/javaxml2"
     xmlns:ora="http://www.oreilly.com"
>
  <!-- WML content -->
</wml>

This addition causes a WAP browser to report an error, as xmlns:javaxml2 and xmlns:ora are not allowed attributes for the wml element. WAP browsers are not as forgiving as HTML browsers, and the rest of the WML content would not be shown. However, you must declare the namespace so the XSL stylesheet can handle template matching for the input document, which does use the javaxml-associated namespace. To handle this problem, XSL allows the attribute exclude-result-prefixes to be added to the xsl:stylesheet element. The namespace prefix specified to this attribute will not be added to the transformed output, which is exactly what you want. Your output would now look like this:

<wml>
  <!-- WML content -->
</wml>

This is understood perfectly by a WAP browser. If you've downloaded the UP.SDK browser, you can point it to your XML table of contents, and see the results. Figure 10-6 shows the main menu that results from the transformation using the WML stylesheet when a WAP device requests the contents.xml file through Cocoon.

Figure 10-6. Main menu for Java and XML

WARNING: In the UP.SDK browser versions that I tested, the browser would not resolve the entity reference OReillyCopyright. I had to comment this line out in my XML to make the examples work. You will probably have to do the same, until the simulator fixes this bug.

Figure 10-7 shows the generated table of contents, accessed by clicking the "Link" button when the "Contents" link is indicated in the display.

Figure 10-7. WML table of contents

Visit http://www.openwave.com and http://www.wapforum.org for more information on WML and WAP; both sites have extensive online resources for wireless device development.

By now, you should have a pretty good idea of the variety of output that can be created with Cocoon. With a minimal amount of effort and an extra stylesheet, the same XML document can be served in multiple formats to multiple types of clients; this is one of the reasons the web publishing framework is such a powerful tool. Without XML and a framework like this, separate sites would have to be created for each type of client. Now that you have seen how flexible the generation of output is when using Cocoon, I will move on to how Cocoon provides technology that allows for dynamic creation and customization of the input to these transformations.

Previous | Home | Next
10.2. Installation | Book Index | 10.4. XSP
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSP (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

10.4. XSP

XSP stands for Extensible Server Pages, and is perhaps the most important development coming out of the Cocoon project. JavaServer Pages (JSP) allows tags and inline Java code to be inserted into an otherwise normal HTML page; when the JSP page is requested, the code is executed and the results are inserted right into the output HTML.[17] This has taken the Java and ASP worlds by storm, ostensibly simplifying server-side Java programming and allowing a separation of output and logic. However, there are still some significant problems. First, JSP does not really provide a separation of content and presentation. This is the same problem I have been talking about: changes to a banner, font color, or text size require the JSP (with the inline Java and JavaBean references) to be modified. JSP also mingles content (pure data) with presentation in the same way static HTML does. Second, there is no ability to transform the JSP into any other format, or use it across applications, because the JSP specification is designed primarily for delivery of output.

[17]This is a drastic oversimplification; the JSP is actually precompiled into a servlet, and a PrintWriter handles output. For more information on JSP, refer to JavaServerPages by Hans Bergsten (O'Reilly).

XSP remedies these problems. XSP is simply XML at its heart. Take a look at the sample XSP page in Example 10-7.

Example 10-7. A simple XSP page

<?xml version="1.0"?>
<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="myStylesheet.xsl" type="text/xsl"?>

<xsp:page language="java"
          xmlns:xsp="http://www.apache.org/1999/XSP/Core"
>

 <xsp:logic>
  private static int numHits = 0;

  private synchronized int getNumHits( ) {
   return ++numHits;
  }
 </xsp:logic>

 <page>
  <title>Hit Counter</title>

  <p>I've been requested <xsp:expr>getNumHits( )</xsp:expr> times.</p>
 </page>
</xsp:page>

All XML conventions are followed. For now, think of the xsp:logic element content as "off-limits" to the XML parser; I'll discuss that later. Other than that, the entire document is simply XML with some new elements. In fact, it references an XSL stylesheet that has nothing remarkable about it, as you can see in Example 10-8.

Example 10-8. XSL stylesheet for the XSP page

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>

  <xsl:template match="page">
    <xsl:processing-instruction name="cocoon-format">
      type="text/html"
    </xsl:processing-instruction>
    <html>
      <head>
        <title><xsl:value-of select="title"/></title>
      </head>
      <body>
        <xsl:apply-templates select="*[not(self::title)]" />
      </body>
    </html>
  </xsl:template>

  <xsl:template match="p">
    <p align="center">
      <xsl:apply-templates />
    </p>
  </xsl:template>

</xsl:stylesheet>

Thus, XSP easily handles the first major problem of JSP: it separates content from presentation. This separation allows developers to handle content generation (the XSP page can be generated from a servlet or other Java code as well as being static), while XML and XSL authors can handle presentation and styling through modification of the XSL stylesheet applied to the XSP page. Just as easily, XSP solves the other significant deficiency of JSP: because XSP processing occurs before any stylesheets are applied, the resultant XML document can be transformed into any other format. XSP maintains all the advantages of XML, as the XSP page can be transferred between applications as well as being used just for presentation.

10.4.1. Creating an XSP Page

Now that you have had a taste of XSP, you can build your own XSP page. For this example, I'll continue looking at the XML documents already created. Let's revisit the XML document constructed earlier. This document represents a portion of this chapter and was transformed into a PDF document. Instead of simply using this document for display, assume that the author wants to let his editor view the document as it is being written. However, in addition to the text of the book, the editor should be able to see comments from the author that the public should not see: for example, questions about style and formatting. First, add the following comment to the chapterTen.xml file you built earlier:

<?xml version="1.0"?>

<?cocoon-process type="xslt"?>
<?xml-stylesheet href="XSL/JavaXML.fo.xsl" type="text/xsl"?>

<book>
 <cover>
  <title>Java and XML</title>
  <author>Brett McLaughlin</author>
 </cover>

 <contents>
  <chapter title="Web Publishing Frameworks" number="10">

   <paragraph> This chapter begins looking at specific Java and XML
topics. So far, I have covered the basics of using XML from Java,
looking at the SAX, DOM, JDOM, and JAXP APIs to manipulate XML and the
fundamentals of using and creating XML itself. Now that you have a grasp
on using XML from your code, I want to spend time on specific
applications. The next six chapters represent the most significant
applications of XML, and, in particular, how those applications are
implemented in the Java space. While there are literally thousands of
important applications of XML, the topics in these chapters are those
that continually seem to be in the spotlight, and that have a significant
potential to change the way traditional development processes occur.
   </paragraph>

   <authorComment>Mike - Do you think the following sidebar is a little
   much? I could easily leave it out if it's still clear without it.
   </authorComment>

    <sidebar title="The More Things Change, the More They Stay the Same">
Readers of the first edition of this book will find that
much of this chapter on Cocoon is the same as the first edition. Although
I promised you that Cocoon 2 would be out by now, and although I expected
to be writing a chapter on Cocoon 2, things haven't progressed as quickly
as expected. Stefano Mazzochi, the driving force behind Cocoon, finally
got around to finishing school (good choice, Stefano!), and so
development on Cocoon 2 has significantly slowed. The result is that
Cocoon 1.x is still the current development path, and you should stick
with it for now. I've updated the section on Cocoon 2 to reflect what is
coming, and you should keep an eye out for more Cocoon-related books from
O'Reilly in the months to come.</sidebar>

   <paragraph> I'll begin this look at hot topics with the one XML
application that seems to have generated the largest amount of excitement
in the XML and Java communities: the web publishing framework. Although
I have continually emphasized that generating presentation from content
is perhaps over-hyped when compared to the value of the portable data
that XML provides, using XML for presentation styling is still very
important. This importance increases when looking at web-based
applications.</paragraph>
  </chapter>

 </contents>
</book>

With this comment in your XML document, add a corresponding entry into your XSL stylesheet, JavaXML.fo.xsl :

  <xsl:template match="sidebar">
    <fo:block font-size="14pt"
              font-style="italic"
              color="blue"
              space-before.optimum="16pt"
              text-align="center">
      <xsl:value-of select="@title" />
    </fo:block>
    <fo:block font-size="12pt"
              color="blue"
              space-before.optimum="16pt"
              text-align="justify">
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>

  <xsl:template match="authorComment">
   <fo:block font-size="10pt"
             font-style="italic"
             color="red"
             space-before.optimum="12pt"
             text-align="justify">
     <xsl:apply-templates/>
   </fo:block>
  </xsl:template>

The comments appear slightly smaller than the rest of the text, italicized, and in red. Now it's possible to turn your XML document into an XSP page (as in Example 10-9) by adding processing instructions for Cocoon and surrounding the elements within a new root element, xsp:page.

Example 10-9. Turning chapterTen.xml into an XSP page

<?xml version="1.0"?>

<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="XSL/JavaXML.fo.xsl" type="text/xsl"?>

<xsp:page language="java"
          xmlns:xsp="http://www.apache.org/1999/XSP/Core"
>
<book>
 <cover>
  <title>Java and XML</title>
  <author>Brett McLaughlin</author>
 </cover>

 <contents>
  <chapter title="Web Publishing Frameworks" number="10">
  <!-- Text of chapter -->
  </chapter>
 </contents>
</book>
</xsp:page>

Before adding XSP logic to determine whether to show the comment, build a simple HTML page letting the viewer indicate if he is the book's editor. In a real application, this could be a page that handles authentication and determines a user's role; for this example, it lets the user select author, editor, or just a curious reader, and enter a password for verification. An HTML page that does this is shown in Example 10-10. Save this file as entry.html in your context's document root.

Example 10-10. Entry page for chapterTen.xml XSP page

<html>
 <head>
  <title>Welcome to the Java and XML Book in Progress</title>
 </head>

 <body>
  <h1 align="center"><i>Java and XML</i> Book in Progress</h1>
  <center>
   <form action="/cocoon/chapterTen.xml" method="POST">
    Select your role:
    <select name="userRole">
     <option value="author">I'm the Author</option>
     <option value="editor">I'm the Editor</option>
     <option value="reader">I'm a Reader</option>
    </select>
    <br />
    Enter your password:
    <input type="password" name="password" size="8" />
    <br /><br />
    <input type="submit" value="Take me to the Book!" />
   </form>
  </center>
 </body>
</html>

Also notice that this HTML submits the form data directly to your XSP page. In this example, the XSP acts like a servlet. It reads the request parameters, determines what user role was selected, authenticates that role using the password supplied, and finally determines whether the author comments should be shown. To begin, define a boolean variable; this variable holds the result of comparing the request parameters to see if the user is an author or editor and supplied a correct password. The value of the variable is checked, and if it is true, the authorComment element is displayed; surround the authorComment element with the XSP directives shown here:

   <xsp:logic>
    boolean authorOrEditor = false;

    // Perform logic to see if user is an author or editor

    if (authorOrEditor) {
      <xsp:content>
        <authorComment>Mike - Do you think the following sidebar is
        a little much? I could easily leave it out if it's still
        clear without it.</authorComment>
      </xsp:content>
    }
   </xsp:logic>

This shouldn't look strange; other than the XSP-specific tags, you're just defining a variable and checking its value. If the variable evaluates to true, the authorComment element is added to the XSP page's output; otherwise, the element is not included in the output. One interesting thing to note is that the actual XML document output is surrounded within the xsp:logic block with an xsp:content element (which in turn is within the outer xsp:page element), ensuring that the XSP processor does not try to interpret any elements or text within the block as XSP structures. The same code in JSP might look like this:

<%
 if (authorOrEditor) {
%>
        <authorComment>Mike - Do you think the following sidebar is
        a little much? I could easily leave it out if it's still
        clear without it.</authorComment>
<%
 }
%>

This is not very structured, as the JSP block ends before the authorComment element begins; then a new block is appended after the element, which closes the brackets opened in the first JSP block. It is very easy to mismatch coding structures or forget to add matching JSP blocks. The XSP paradigm forces every open element to be closed (standard XML well-formedness) and one block of code is matched with one element.

With these logical structures in place, the XSP page just needs to interpret the request parameters. You can use the built-in XSP variable request, which mimics the javax.servlet.http.HttpServletRequest object. The following code additions read the values of the userRole and password request parameters (if they exist). The value of userRole is then compared with the roles that can see the comments ("author" and "editor"). If a match occurs, the password is checked as well. If the password matches the key for the supplied role, the boolean variable is set to true, and the authorComment element is part of the XML output:

   <xsp:logic>
    boolean authorOrEditor = false;

    // Perform logic to see if user is an author or editor
 <![CDATA[
 String[] roleValues = request.getParameterValues("userRole");
 String[] passwordValues = request.getParameterValues("password");
 if ((roleValues != null) && (passwordValues != null)) {
   String userRole = roleValues[0];
   String password = passwordValues[0];
   if (userRole.equals("author") && password.equals("brett")) {
     authorOrEditor = true;
   } else
     if (userRole.equals("editor") && password.equals("mike")) {
     authorOrEditor = true;
   }
 }
 ]]>

    if (authorOrEditor) {
...

Notice that I enclose a good bit of this logic within a CDATA tag. Remember that XSP is still evaluated as XML, and must follow the rules of an XML document; however, the double quotes and ampersands used in the Java fragments are not allowed in XML documents. Instead of escaping these characters and getting a very strange XSP fragment, you can use the CDATA tag and write standard Java code. Without it, you would have to code as follows:

<xsp:logic>
 boolean authorOrEditor = false;

 String[] roleValues =
   request.getParameterValues(&quot;userRole&quot;);
 String[] passwordValues =
   request.getParameterValues(&quot;password&quot;);
 if ((roleValues != null) &amp;&amp;
     (passwordValues != null)) {
   String userRole = roleValues[0];
   String password = passwordValues[0];
   if (userRole.equals("author") &amp;&amp;
       password.equals("brett")) {
     authorOrEditor = true;
   } else
   if (userRole.equals("editor") &amp;&amp;
       password.equals("mike")) {
     authorOrEditor = true;
   }
 }
...
</xsp:logic>

Now test the entry page and the resultant PDF generated from the XML. You should get output similar to Figure 10-8 if you direct your web browser to http://<hostname>:<port>/cocoon/entry.html.

Figure 10-8. Entry page for chapterTen.xml XSP page

Select the role of author and use the password "brett", or select the editor role with the password "mike". Either case gives you the PDF output shown in Figure 10-9.

Figure 10-9. XSP output with author or editor role

The one thing that has not yet been done is to isolate the page's logic from its content. Just as JSP allows inclusion of JavaBeans to abstract the content and presentation from the logic of an application component, XSP allows tag libraries to be created. These tag libraries allow XML tags to trigger the matching code within a tag library.

10.4.2. Using XSP Tag Libraries

In addition to showing comments based on the user's identity, the XSP page should indicate that the chapter is in a draft state. The current date can be shown to indicate the date of the draft (then the date is frozen when the chapter is considered complete). Instead of adding inline Java tags to load the current date, simply create a custom tag library for this purpose. It's also worth looking at the process of creating an XSP element that takes in the chapter number and title and formats the complete title. This function will handle the insertion of the draft date, as well. To do this, you first need to create a tag library that is available to the XSP page. Much of the tag library is based on an XSL stylesheet. You can start with the skeleton shown in Example 10-11, which passes anything it receives through as output. Save this skeleton as JavaXML.xsp.xsl in the XSL/ subdirectory. Be sure to include the javaxml2 namespace declaration, as it will be used to match elements within that namespace used in the XSP pages.

Example 10-11. Skeleton of XSP tag library

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xsp="http://www.apache.org/1999/XSP/Core"
  xmlns:javaxml2="http://www.oreilly.com/javaxml2"
>
  <xsl:template match="xsp:page">
    <xsp:page>
      <xsl:copy>
        <xsl:apply-templates select="@*"/>
      </xsl:copy>

      <xsl:apply-templates/>
    </xsp:page>
  </xsl:template>

  <xsl:template match="@*|*|text()|processing-instruction( )">
    <xsl:copy>
      <xsl:apply-templates
           select="@*|*|text()|processing-instruction( )"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

By matching the xsp:page tag, it's possible to ensure that all elements are matched and handled within this stylesheet; this is referred to as a logicsheet in XSP parlance. Now add Java methods for the templates within this logicsheet to call:

  <xsl:template match="xsp:page">
    <xsp:page>
      <xsl:copy>
        <xsl:apply-templates select="@*"/>
      </xsl:copy>

      <xsp:structure>
        <xsp:include>java.util.Date</xsp:include>
        <xsp:include>java.text.SimpleDateFormat</xsp:include>
      </xsp:structure>

      <xsp:logic>
        private static String getDraftDate( ) {
          return (new SimpleDateFormat("MM/dd/yyyy"))
            .format(new Date( ));
        }

        private static String getTitle(int chapterNum,
                                       String chapterTitle) {
          return chapterNum + ". " + chapterTitle;
        }
      </xsp:logic>

      <xsl:apply-templates/>
    </xsp:page>
  </xsl:template>

Several new XSP elements are introduced here. First, xsp:structure is used to surround several xsp:include statements. These work just like their Java counterpart, import, making the specified Java classes available for use by their unqualified names (rather than the complete packaged names). Once these are available, the logicsheet defines and implements two methods: one that creates a chapter title from the chapter number and textual title, and one that returns the current date as a formatted String. These methods are available to any elements within this logicsheet.

Now define the element that specifies when an XSP result should replace an XML element. The javaxml2-associated namespace is already declared in the root element, so it can be used as the namespace for the new tag library elements. Add the following template into your logicsheet:

  <!-- Create formatted title -->
  <xsl:template match="javaxml2:draftTitle">
    <xsp:expr>getTitle(<xsl:value-of select="@chapterNum" />,
                       "<xsl:value-of select="@chapterTitle" />")
    </xsp:expr> (<xsp:expr>getDraftDate( )</xsp:expr>)
  </xsl:template>

  <xsl:template match="@*|*|text()|processing-instruction( )">
    <xsl:copy>
      <xsl:apply-templates
           select="@*|*|text()|processing-instruction( )"/>
    </xsl:copy>
  </xsl:template>

When a document with this tag library uses the element javaxml2:draftTitle (or just draftTitle if the default namespace is mapped to http://www.oreilly.com/javaxml2), the result of the method getTitle( ) is prepended to the value of the getDraftDate( ) method. The javaxml2:draftTitle element also expects two attributes to be declared: the chapter number and the textual title of the chapter. Signify to the XSP processor that you are calling a defined method by enclosing the method call within a set of <xsp:expr> tags. To indicate that the second argument (the chapter title) is a String, it should be enclosed within quotes. Since the chapter number should be treated as an int, it is left without quotation marks.

Once you have completed the XSP logicsheet (available online at the book's web site as well), you need to make it accessible to Cocoon. This can be done one of two ways. The first is to specify the location of the file as a URI, which allows the servlet engine (and therefore Cocoon) to locate the logicsheet. For example, to add the XSP logicsheet to Cocoon's set of resources through its URI, you could add the following lines to the cocoon.properties file on a Unix-based system:

# Set the libraries associated with the given namespace.
# Use the syntax:
#   processor.xsp.logicsheet.<namespace-tag>.<language> = URL to file
# where "URL to file" is usually starting with file:// if you locate
# your custom library in your file system.
processor.xsp.logicsheet.context.java  =resource://org/apache/cocoon/processor/
xsp/library/java/context.xsl
processor.xsp.logicsheet.cookie.java   =resource://org/apache/cocoon/processor/
xsp/library/java/cookie.xsl
processor.xsp.logicsheet.global.java   =resource://org/apache/cocoon/processor/
xsp/library/java/global.xsl
processor.xsp.logicsheet.request.java  =resource://org/apache/cocoon/processor/
xsp/library/java/request.xsl
processor.xsp.logicsheet.response.java =resource://org/apache/cocoon/processor/
xsp/library/java/response.xsl
processor.xsp.logicsheet.session.java  =resource://org/apache/cocoon/processor/
xsp/library/java/session.xsl
processor.xsp.logicsheet.util.java     =
  resource://org/apache/cocoon/processor/xsp/library/java/util.xsl
processor.xsp.logicsheet.sql.java      =
  resource://org/apache/cocoon/processor/xsp/library/sql/sql.xsl
processor.xsp.logicsheet.esql.java      =
  resource://org/apache/cocoon/processor/xsp/library/sql/esql.xsl
processor.xsp.logicsheet.fp.java       =
  resource://org/apache/cocoon/processor/xsp/library/fp/fp.xsl

processor.xsp.library.JavaXML.java =
  file:///usr/local/jakarta-tomcat/webapps/cocoon/XSL/JavaXML.xsp.xsl

For Windows systems, this would be:

# Set the libraries associated with the given namespace.
# Use the syntax:
#   processor.xsp.logicsheet.<namespace-tag>.<language> = URL to file
# where "URL to file" is usually starting with file:// if you locate
# your custom library in your file system.
processor.xsp.logicsheet.context.java  =resource://org/apache/cocoon/processor/
xsp/library/java/context.xsl
processor.xsp.logicsheet.cookie.java   =resource://org/apache/cocoon/processor/
xsp/library/java/cookie.xsl
processor.xsp.logicsheet.global.java   =resource://org/apache/cocoon/processor/
xsp/library/java/global.xsl
processor.xsp.logicsheet.request.java  =resource://org/apache/cocoon/processor/
xsp/library/java/request.xsl
processor.xsp.logicsheet.response.java =resource://org/apache/cocoon/processor/
xsp/library/java/response.xsl
processor.xsp.logicsheet.session.java  =resource://org/apache/cocoon/processor/
xsp/library/java/session.xsl
processor.xsp.logicsheet.util.java     =
  resource://org/apache/cocoon/processor/xsp/library/java/util.xsl
processor.xsp.logicsheet.sql.java      =
  resource://org/apache/cocoon/processor/xsp/library/sql/sql.xsl
processor.xsp.logicsheet.esql.java      =
  resource://org/apache/cocoon/processor/xsp/library/sql/esql.xsl
processor.xsp.logicsheet.fp.java       =
  resource://org/apache/cocoon/processor/xsp/library/fp/fp.xsl

processor.xsp.library.javaxml2.java =
  file:///C:/java/jakarta-tomcat/webapps/cocoon/XSL/JavaXML.xsp.xsl

While this is handy for testing, it is not a very good solution for uncoupling your logicsheets from the servlet engine, and also adds quite a bit of maintenance overhead when adding new logicsheets: a new line would have to be added to the cocoon.properties file for new logicsheets to be available. Allowing specification of a resource in the servlet engine's classpath is an alternative method for loading logicsheets. This lets you add custom logicsheets to a jar file, and that jar file to the servlet engine classpath (which in Tomcat simply means adding that archive to the lib/ directory!). In addition, new logicsheets can be put within the jar file, providing a central location for storing your custom XSP logicsheets. From the XSL/ subdirectory in your web server's document root, perform the following command to create a jar file that contains your logicsheet:

jar cvf logicsheets.jar JavaXML.xsp.xsl

Move the created logicsheets.jar archive into your TOMCAT_HOME/lib/ directory with the other Cocoon libraries. That will ensure that Tomcat loads the library on startup. With your logicsheet available, you can now let Cocoon know where to look for javaxml2-associated namespace references within XSP pages. Edit the cocoon.properties file; locate the section that lists the various Cocoon XSP resources, and add the new logicsheet reference:

# Set the libraries associated with the given namespace.
# Use the syntax:
#   processor.xsp.logicsheet.<namespace-tag>.<language> = URL to file
# where "URL to file" is usually starting with file:// if you locate
# your custom library in your file system.
processor.xsp.logicsheet.context.java  =resource://org/apache/cocoon/processor/
xsp/library/java/context.xsl
processor.xsp.logicsheet.cookie.java   =resource://org/apache/cocoon/processor/
xsp/library/java/cookie.xsl
processor.xsp.logicsheet.global.java   =resource://org/apache/cocoon/processor/
xsp/library/java/global.xsl
processor.xsp.logicsheet.request.java  =resource://org/apache/cocoon/processor/
xsp/library/java/request.xsl
processor.xsp.logicsheet.response.java =resource://org/apache/cocoon/processor/
xsp/library/java/response.xsl
processor.xsp.logicsheet.session.java  =resource://org/apache/cocoon/processor/
xsp/library/java/session.xsl
processor.xsp.logicsheet.util.java     =
  resource://org/apache/cocoon/processor/xsp/library/java/util.xsl
processor.xsp.logicsheet.sql.java      =
  resource://org/apache/cocoon/processor/xsp/library/sql/sql.xsl
processor.xsp.logicsheet.esql.java      =
  resource://org/apache/cocoon/processor/xsp/library/sql/esql.xsl
processor.xsp.logicsheet.fp.java       =
  resource://org/apache/cocoon/processor/xsp/library/fp/fp.xsl

processor.xsp.logicsheet.javaxml2.java = resource://JavaXML.xsp.xsl

Because the logicsheet is not nested within any subdirectories in the logicsheets.jar file, you can simply use the name of the logicsheet as its resource path. Finally, restart your servlet engine (this also ensures that Tomcat auto-loads the new library). This will reload the cocoon.properties file, and the logicsheet will be available for use. As the Cocoon engine is used to handle requests, any XSP page that declares that it uses the javaxml2 namespace will have access to the logicsheet specified as the javaxml2 library. So the XSP page needs to add a namespace declaration for the javaxml2 namespace:

<?xml version="1.0"?>

<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<?xml-stylesheet href="XSL/JavaXML.fo.xsl" type="text/xsl"?>

<xsp:page language="java"
          xmlns:xsp="http://www.apache.org/1999/XSP/Core"
          xmlns:javaxml2="http://www.oreilly.com/javaxml2"
>
<book>
  <!-- Book content -->
</book>
</xsp:page>

With the tag library now available for use, you can finally add in the javaxml2:draftTitle element to your XML document, chapterTen.xml:

<contents>
  <chapter title="Web Publishing Frameworks" number="10">
    <javaxml2:draftTitle chapterNum="10"
                         chapterTitle="Web Publishing Framework" />
...

Replace the hardcoded chapter title with the element defined in the XSP tag library with the following change to your JavaXML.fo.xsl stylesheet:

  <xsl:template match="chapter">
    <fo:block font-size="24pt"
              text-align-last="center"
              space-before.optimum="24pt">
<!--
      <xsl:value-of select="@number" />.
      <xsl:value-of select="@title" />
-->
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>

This should generate the title with the chapter number, chapter title, and the date of the draft. Accessing this new version of the XSP page results in the output shown in Figure 10-10.

Figure 10-10. Output using XSP tag library

I have only scratched the surface of XSP. Even this simple example allows the title to be converted to a different form by modifying only the XSP logicsheet -- not the content or presentation of the page -- when the chapter is complete. In the same way, XSP allows the creation of very strict contracts separating presentation from content from application logic. Adding server-side Java components such as Enterprise JavaBeans can bring business logic into the equation. Rather than using a less flexible solution like JSP that is coupled to HTML and a presentation format, using XSP allows a looser coupling of components and thus is a better solution for application development. XSP also promises to be key in the upcoming Cocoon 2.0, which I look at now.

Previous | Home | Next
10.3. Using a Publishing Framework | Book Index | 10.5. Cocoon 2.0 and Beyond
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Cocoon 2.0 and Beyond (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

10.5. Cocoon 2.0 and Beyond

Cocoon 2.0, the next generation of Cocoon, promises to be a giant leap forward for the web publishing framework. Cocoon 1.x, which is primarily based on XML being transformed via XSL, still has some serious limitations. First, it does not significantly reduce the management costs of large sites. While one XML document can be transformed into different client views, a significant number of documents will still exist. Generally, either long URIs (such as /content/publishing/books/javaxml/contents.xml), a large number of virtual path mappings (/javaxml mapped to /content/publishing/books/javaxml), or a combination of the two result. In addition, a strict separation of presentation from content from logic is difficult to accomplish, and even more difficult to manage.

Cocoon 2 focuses on enforcing the contracts between these different layers, therefore reducing management costs. XSP is a centerpiece in this design. In addition, the sitemap allows the distinction between XSP, XML, and static HTML pages to be hidden from the prying user. Advanced precompilation and memory considerations will also be introduced to make Cocoon 2 even more of an advance over Cocoon 1.x than Cocoon 1.x was over a standard web server.

10.5.1. Servlet Engine Mappings

A significant change in Cocoon 2 is that it no longer requires a simple mapping for XML documents. While this works well in the 1.x model, it still leaves management of non-XML documents to the webmaster, possibly someone completely different from the person responsible for the XML documents. Cocoon 2 seeks to take over management of the entire web site. For this reason, the main Cocoon servlet (org.apache.cocoon.servlet.CocoonServlet in the 2.0 model) is generally mapped to a URI, such as /Cocoon. This could also be mapped to the root of the web server itself (simply "/") to completely control a site. The URL requested then follows the servlet mapping: http://myHost.com/Cocoon/myPage.xml or http://myHost.com/Cocoon/myDynamicPage.xsp, for example.

With this mapping in place, even static HTML documents can be grouped with XML documents, allowing the management of all files on the server to be handled by a central person or group. If HTML, WML, and XML documents must be mixed in a directory, no confusion needs to occur, and uniform URIs can be used. Cocoon 2 will happily serve HTML as well as any other document type; with a mapping from the root of a server to Cocoon, the web publishing framework actually becomes invisible to the client.

10.5.2. The Sitemap

Another important introduction to Cocoon 2 is the sitemap. In Cocoon, a sitemap provides a central location for administration of a web site. Cocoon uses this sitemap to decide how to process the request URIs it receives. For example, when Cocoon receives a request like http://myCocoonSite.com/Cocoon/javaxml/chapterOne.html, the Cocoon servlet dissects the request and determines that the actual URI requested is /javaxml/chapterOne.html. However, suppose that the file chapterOne.html should map not to a static HTML file, but to the transformation of an XML document (as in the earlier examples). The sitemap can handle this, quite easily! Take a look at the sitemap shown in Example 10-12.

Example 10-12. Sample Cocoon 2 sitemap

  <sitemap>
   <process match="/javaxml/*.html">
    <generator type="file" src="/docs/javaxml/*.xml"
    <filter type="xslt">
     <parameter name="stylesheet" value="/styles/JavaXML.html.xsl"/>
    </filter>
    <serializer type="html"/>
   </process>

   <process match="/javaxml/*.pdf">
    <generator type="file" src="/docs/javaxml/*.xml"
    <filter type="xslt">
     <parameter name="stylesheet" value="/styles/JavaXML.pdf.xsl"/>
    </filter>
    <serializer type="fop"/>
   </process>
  </sitemap>

In this example, Cocoon matches the URI /javaxml/chapterOne.html to the sitemap directive /javaxml/*.html. It determines that this is an actual file, and the source for the file should be determined by using the mapping /docs/javaxml/*. xml, which translates to /docs/javaxml/chapterOne.xml (the filename we want transformed). The XSLT filter is then applied; the stylesheet to use, JavaXML.html.xsl, is also specified in the sitemap. The resulting transformation is then displayed to the user. In addition, the XML file could be an XSP file processed before being converted to XML and then styled.

This same process can render a PDF from the request http://myCocoonSite.com/Cocoon/javaxml/chapterOne.pdf, all with a few extra lines in the sitemap (shown in the previous example). The processing instructions in the individual XML documents can be completely removed, a significant change from Cocoon 1.x. First, uniform application of stylesheets and processing can occur based on a directory location. Simply creating XML and placing it in the /docs/javaxml/ directory in the example means the document can be accessed as HTML or PDF. It is also trivial to change the stylesheet used for all documents, something very difficult and tedious to do in Cocoon 1.x. Instead of making a change to each XML document, only the single line in the sitemap needs to be changed.

The Cocoon sitemap is still being developed, and there will probably be quite a few additional enhancements and changes to its format and structure by the time Cocoon 2.0 goes final. To get involved, join the mailing lists at cocoon-users@xml.apache.org and cocoon-dev@xml.apache.org. The Apache XML project at http://xml.apache.org has details about how to participate in these lists and the Cocoon project.

10.5.3. Producers and Processors

One final improvement that Cocoon 2 will include is precompiled and event-based producers and processors. In Cocoon, a producer handles the transformation of a request URI into an XML document stream. A processor then takes an input stream (currently the XML document in a DOM tree) into output readable by the client. I haven't covered producers and processors in the Cocoon 1.x model because they are going to drastically change in the Cocoon 2.0 model; any producers and processors currently being used will most likely be useless and have to be rewritten in Cocoon 2.0.

Cocoon 2 moves from using DOM for these structures to using the more event-based SAX, wrapped within a DOM structure. As a producer in 1.x had to generate an XML document in memory, the corresponding DOM structure could get extremely large. This eventually drained system resources, particularly when performing complex tasks such as large transformations or handling formatting objects (PDF generation). For these reasons, DOM will be a simple wrapper around SAX-based events in Cocoon 2, allowing producers and processors to be very slim and efficient.

In addition, producers and processors will be precompiled versions of other formats. For example, XSL stylesheets can be precompiled into processors, and XSP pages can be precompiled into producers. This further increases performance while removing load from the client. These and other changes continue to use a component model, allowing Cocoon to be a very flexible, very pluggable framework. Keep up on the latest changes by monitoring the Cocoon web site.

Previous | Home | Next
10.4. XSP | Book Index | 10.6. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

10.6. What's Next?

The next chapter looks at a technology that allows XML to be used as a data format in an important request and response model: XML Remote Procedure Calls. XML-RPC allows clients in a distributed system to request that tasks be executed on a server (or servers) on another portion of the network. Until recently, RPC has declined in popularity, mostly due to the surge of RMI-based technologies in the Java space (most notably, EJB). However, with XML as a data format, XML-RPC is a new solution for many problems that could not be solved cleanly or efficiently without RPC. I'll show you XML-RPC next, and in particular, the Java implementation of the technology.

Previous | Home | Next
10.5. Cocoon 2.0 and Beyond | Book Index | 11. XML-RPC
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML-RPC (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 11. XML-RPC

Contents:

RPC Versus RMI
Saying Hello
Putting the Load on the Server
The Real World
What's Next?

XML-RPC is actually a specific flavor of RPC, which stands for remote procedure calls. If you are new to programming, or have worked with the Java language only a short time, remote procedure calls may be new for you; if you've been around the block in the development world, you may be a bit rusty, as RPC has fallen out of vogue in recent years. In this chapter I look at why those three little letters in front of RPC are revolutionizing what was becoming a computing dinosaur, and how to use XML-RPC from the world of Java. I also spend some time at the end of this chapter looking at real-world applications of XML-RPC, trying to shed some light not only on how to use this technology, but when to use it.

If you are part of the tidal wave of object-oriented development that has come along in the past three to five years, even hearing the word "procedure" may send shivers down your back. Procedural languages such as PL/SQL and ANSI C are not popular for a long list of very good reasons. You have probably been scolded for calling a Java method a function or procedure before, and almost certainly know better than to write "spaghetti code," code that has method after method chained together in a long line. RPC has fallen by the wayside much as these languages and techniques have. There are new, object-oriented ways of achieving the same results, often with better design and performance. Surprisingly, though, the rise of XML has brought with it the rise and prominence of APIs specifically built for XML-RPC, and a gradual trend toward using XML-RPC in specific situations despite the connotations it carries.

Before trying to use these APIs, it is worth spending some time looking at what RPC is and how it compares to similar Java technologies, most notably remote method invocation (RMI). If you do choose to use XML-RPC in your applications (and you almost surely will want to at some point), be assured that you will probably have to justify your choice to other developers, particularly those who may have just read books on EJB or RMI. Certainly there are places for all these technologies. Understanding the proper application of each is critical to your success not only as a developer, but as a team member and mentor. Keeping in mind these reasons for understanding the concepts behind these remote methodologies, let's take a look at the two most popular ways to operate upon objects across a network: RPC and RMI.

11.1. RPC Versus RMI

If you haven't been under a rock for the last several years, you should be aware that EJB and RMI have taken the Java world by storm. The entire EJB (Enterprise JavaBeans) specification is founded upon RMI principles, and you will be hard-pressed to write a three-tier application without using RMI, even if indirectly. In other words, if you don't know how to use RMI yet, you may want to pick up Java Enterprise in a Nutshell by David Flanagan, Jim Farley, William Crawford, and Kris Magnusson, or Java Distributed Computing by Jim Farley (both published by O'Reilly) and spend some time looking into this useful technology.

11.1.1. What Is RMI?

In short, RMI is remote method invocation. RMI allows a program to invoke methods on an object when the object is not located on the same machine as the program. This is at the heart of distributed computing in the Java world, and is the backbone of EJB as well as many enterprise application implementations. Without getting into too much detail, RMI uses client stubs to describe the methods a remote object has available for invocation. The client acts upon these stubs (which are Java interfaces), and RMI handles the "magic" of translating requests to a stub into a network call. This call invokes the method on the machine with the actual object, and then streams the result back across the network. Finally, the stub returns this result to the client that made the original method call, and the client moves on. The main idea is that the client doesn't typically worry about the RMI and network details; it uses the stub as if it were the actual object with implemented methods. RMI (using JRMP Figure 11.1.1, Java's remote protocol) makes all this network communication happen behind the scenes, allowing the client to deal with a generic exception (java.rmi.RemoteException) and spend more time handling business rules and application logic. RMI can also use different protocols such as Internet Inter-ORB Protocol (IIOP), allowing communication between Java and CORBA objects, often in different languages such as C or C++.

RMI carries a cost, though. First, using RMI is resource-intensive. JRMP provides very poor performance, and writing a remote protocol to replace it is not a simple task. As clients issue RMI calls, sockets must be opened and maintained, and the number of sockets can affect system performance, particularly when the system is accessible via a network (which then requires more sockets to be opened for HTTP access). RMI also requires a server or provider to bind objects to. Until an object is bound to a name on one of these providers, the object is not accessible to other programs. This requires using an RMI registry, a Lightweight Directory Access Protocol (LDAP) directory server, or a variety of other Java Naming and Directory Interface (JNDI) services. Finally, RMI can involve a lot of coding, even with all the helpful RMI server classes you get with the JDK; a remote interface describing the methods available to be invoked must be coded (as well as quite a few other interfaces if you are using EJB). This also means that adding an additional method to the server class results in a change to the interface and recompilation of the client stubs, something that is often not desirable and sometimes not possible.

11.1.2. What Is RPC?

RPC is remote procedure calls. Where RMI lets you interoperate directly with a Java object, RPC is built in more of a dispatch fashion. Instead of dealing with objects, RPC lets you use standalone methods across a network. Although this limits interactivity, it does make for a slightly simpler interface to the client. You can think of RPC as a way to use "services" on remote machines, while RMI allows you to use "servers" on remote machines. The subtle difference is that RMI typically is driven entirely by the client, with events occurring when methods are invoked remotely. RPC is often built more as a class or set of classes that works to perform tasks with or without client intervention; however, at times these classes service requests from clients, and execute "mini" tasks for the clients. I will show you some examples shortly to clarify these definitions.

RPC, while not as interactive an environment as RMI, does offer some significant advantages. RPC allows disparate systems to work together. While RMI allows the use of IIOP for connecting Java to CORBA servers and clients, RPC allows literally any type of application intercommunication, because the transport protocol can be HTTP. Since virtually every language in use today has some means of communicating via HTTP, RPC is very attractive for programs that must connect to legacy systems. RPC is also typically more lightweight than RMI (particularly when using XML as the encoding, which I'll cover next); while RMI often has to load entire Java classes over the network (such as code for applets and custom helper classes for EJB), RPC only has to pass across the request parameters and the resulting response, generally encoded as textual data. RPC also fits very nicely into the API model, allowing systems that are not part of your specific application to still access information from your application. This means that changes to your server do not have to result in changes to other clients' application code; with pure textual data transfer and requests, additional methods can be added without client recompilation, and minor changes are sufficient to use these new methods.

The problem with RPC has traditionally been the encoding of data in transfer; imagine trying to represent a Java Hashtable or Vector in a very lightweight way through textual formats. When you consider that these structures can, in turn, hold other Java object types, the data representation quickly becomes tricky to write; it also has to remain a format that is usable by all the disparate programming languages, or the advantages of RPC are lessened. Until recently, an inverse relationship had been developing between the quality and usability of the encoding and its simplicity; in other words, the easier it became to represent complex objects, the more difficult it became to use the encoding in multiple programming languages without proprietary extensions and code. Elaborate textual representations of data were not standardized and required completely new implementations in every language to be usable. You can see where this discussion is leading.

11.1.3. XML-RPC

The greatest obstacle to using RPC has traditionally been its encoding. But then XML came along with a solution. XML provided not only a very simple, textual representation of data, but a standard for the structure of that data. Concerns about proprietary solutions became moot when the W3C released the XML 1.0 specification, reassuring RPC coders that XML was not going anywhere. In addition, SAX provided a lightweight, standard way to access XML, making it much easier to implement RPC libraries. This left only transmission over HTTP (something people have been doing for many years) and the specific encoding and decoding APIs for XML-RPC implementers to write. After a few beta implementations of XML-RPC libraries, it became clear that XML was also a very fast and lightweight encoding, resulting in better performance for XML-RPC libraries than expected. XML-RPC is now a viable and stable solution for remote procedure calls.

For you, the Java developer, XML-RPC provides a way to handle simple creation of "hooks" into your application and its services, for your own use as well as for other application clients in different divisions or even different companies. It also uncouples these APIs from Java if clients are unable to use the Java language directly. Finally, XML-RPC removes RMI from the technologies that have to be learned to use distributed services (at least initially). I'll spend this chapter looking at how to implement an XML-RPC server and client; I'll show an example of how a server can operate independently of clients, yet still provide XML-RPC accessible interfaces to interoperate with and query its data. Although I'm not going to look at RMI in depth in this chapter, I continually compare the XML-RPC solution to RMI, pointing out why XML-RPC is a better solution for some specific types of tasks.

Previous | Home | Next
10.6. What's Next? | Book Index | 11.2. Saying Hello
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Saying Hello (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

11.2. Saying Hello

You are probably interested in seeing if XML-RPC might be the right solution for some of your development problems. To elaborate on XML-RPC, we'll now look at building some actual working Java code using XML-RPC. In the great tradition of programming, I'll start with a simple "Hello World" type program. I'll show you how to define an XML-RPC server, and have that server register a handler. This handler takes in a Java String parameter and the user's name, and returns "Hello" and the user's name; for example, the method might return "Hello Shirley" when invoked. Then you'll need to make this handler available for XML-RPC clients. Finally, I'll demonstrate building a simple client to connect to the server and request the method invocation.

In a practical case, the XML-RPC server and handler would be on one machine, probably a heavy-duty server, and the client on another machine, invoking the procedure calls remotely. However, if you don't have multiple machines available, you can still use the examples locally. Although this will be much faster than an actual client and server, you can still see how the pieces fit together and get a taste of XML-RPC.

11.2.1. XML-RPC Libraries

A lot of work has already gone into RPC, and more recently XML-RPC. Like using SAX, DOM, and JDOM for XML handling, there is no reason to reinvent the wheel when there are good, even exceptional, Java packages in existence for your desired purpose. The center for information about XML-RPC and links to libraries for Java as well as many other languages can be found at http://www.xmlrpc.com. Sponsored by Userland (http://www.userland.com), this site has a public specification on XML-RPC, information on what datatypes are supported, and some tutorials on XML-RPC use. Most importantly, it directs you to the XML-RPC package for Java. Following the link on the main page, you are directed to Hannes Wallnofer's site at http://classic.helma.at/hannes/xmlrpc/.

On Hannes's site is a description of the classes in his XML-RPC package and instructions. Download the archive file and expand the files into your development area or IDE. You should then be able to compile these classes; there is one Java servlet example that requires the servlet classes (servlet.jar for Servlet API 2.2). You can obtain these classes with the Tomcat servlet engine by pointing your web browser to http://jakarta.apache.org. If you do not wish to play with the servlet example, the servlet classes are not required for the programs in this chapter.

The core distribution (which does not include the applet or regular expression examples in the downloaded archive) is made up of thirteen classes, all in the helma.xmlrpc package. These are in a ready-to-use format in the lib/xmlrpc.jar file of the distribution. The classes within that distribution are detailed briefly in Table 11-1.

Table 11-1. The XML-RPC classes

Class | Purpose
XmlRpc | Core class allowing method calls on handlers by an XML-RPC server.
XmlRpcClient | Class for client to use for RPC communication over HTTP, including proxy and cookie support.
XmlRpcClientLite | Class for client to use when a less-featured HTTP client is needed (no cookies, proxy support).
XmlRpcServer | Class for servers to use to receive RPC calls.
XmlRpcServlet | Provides the functionality of XmlRpcServer in a servlet format.
XmlRpcProxyServlet | Acts as an XML-RPC servlet proxy.
XmlRpcHandler | Base interface for controlling XML-RPC interactions by handlers.
AuthenticatedXmlRpcHandler | Same as XmlRpcHandler, but allows for authentication.
Base64 | Encodes and decodes between bytes and base 64 encoding characters.
Benchmark | Times roundtrip XML-RPC interactions for a specific SAX driver.
WebServer | A lightweight HTTP server for use by XML-RPC servers.

The SAX classes (from earlier examples) and a SAX driver are not included in the distribution, but they are required for operation. In other words, you need a complete XML parser implementation that supports SAX. I continue to use Apache Xerces in these examples, although the libraries support any SAX 1.0-compatible driver.

Once you have all the source files compiled, ensure that the XML-RPC classes, SAX classes, and your XML parser classes are all in your environment's classpath. This should have you ready to write your own custom code and start the process of "saying hello." Keep the XML-RPC source files handy, as looking at what is going on under the hood can aid in your understanding of the examples.

11.2.2. Writing the Handler

The first thing you need to do is write the class and method you want invoked remotely. This is usually called a handler. Beware, though, as the XML-RPC server mechanism that dispatches requests is also often called a handler; again, naming ambiguity rears its ugly head. A clearer distinction can be drawn as follows: an XML-RPC handler is a method or set of methods that takes an XML-RPC request, decodes its contents, and dispatches the request to a class and method. A response handler, or simply handler, is any method that can be invoked by an XML-RPC handler. With the XML-RPC libraries for Java, you do not need to write an XML-RPC handler because one is included as part of the helma.xmlrpc.XmlRpcServer class. You only need to write a class with one or more methods to register with the server.

It might surprise you to learn that creating a response handler requires no subclassing or other special treatment in your code. Any method can be invoked via XML-RPC as long as its parameter and return types are supported (able to be encoded) by XML-RPC. Table 11-2 lists all currently supported Java types that can be used in XML-RPC method signatures.

Table 11-2. Supported Java types in XML-RPC

XML-RPC datatype | Java type
int | int
boolean | boolean
string | String
double | double
dateTime.iso8601 | Date
struct | Hashtable
array | Vector
base64 | byte[]
nil | null

Although this list includes only a small number of types, they handle most of the XML-RPC requests made over a network. The method in this example only needs to take in a String (the name to say "hello" to), and return a String, and so fits these requirements. This is enough information to write a simple handler class, shown in Example 11-1.

Example 11-1. Handler class with remote method

package javaxml2;

public class HelloHandler {

    public String sayHello(String name) {
        return "Hello " + name;
    }
}

This is as simple as it seems. The method signature takes in and returns legal XML-RPC parameters, so you can safely register it with your (soon to be created) XML-RPC server and know it will be callable via XML-RPC.

11.2.3. Writing the Server

With your handler ready, you need to write a program to start up an XML-RPC server, listen for requests, and dispatch these requests to the handler. For this example, I use the helma.xmlrpc.WebServer class as the request handler. Although you could use a Java servlet, using this lightweight web server implementation allows you to avoid running a servlet engine on the XML-RPC server. I'll spend more time at the end of this chapter discussing servlets in the context of an XML-RPC server. For the server, the example allows the specification of a port to start the server on, and then has the server listen for XML-RPC requests until shut down. Finally, you need to register the class you just created with the server, and specify any other application-specific parameters to the server.

Create the skeleton for this class (shown in Example 11-2) now; you'll need to import the WebServer class and also ensure that a port number is given to the program on the command line when the server is started.

Example 11-2. Skeleton for XML-RPC server

package javaxml2;

import helma.xmlrpc.WebServer;

public class HelloServer {

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println(
                "Usage: java javaxml2.HelloServer [port]");
            System.exit(-1);
        }

        // Start the server on specified port
    }
}

Before starting the server, specify the SAX driver for use in parsing and encoding XML. The default SAX driver for these libraries is James Clark's XP parser, available online at http://www.jclark.com. In this code, I instead request the Apache Xerces parser by specifying the SAX Parser implementation class[18] to the XML-RPC engine. This is done through the setDriver( ) method, a static method belonging to the XmlRpc class. This class underpins the WebServer class, but must be imported and used directly to make this change in SAX drivers. A ClassNotFoundException can be thrown by this method, so must be caught in case the driver class cannot be located in your classpath at runtime. Add the necessary import statement and methods to your HelloServer class now:

[18]Currently this XML-RPC library does not support SAX 2.0 or implement the XMLReader interface. As the Apache Xerces SAXParser class implements both the SAX 1.0 Parser interface and SAX 2.0 XMLReader interface, no code needs to be changed in the examples if SAX 2.0 updates are made to the libraries. However, if you are using a different vendor's parser, you may need to specify a SAX 2.0 class if the XML-RPC libraries are modified to use SAX 2.0.

package javaxml2;

import helma.xmlrpc.WebServer;
import helma.xmlrpc.XmlRpc;

public class HelloServer {

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println(
                "Usage: java javaxml2.HelloServer [port]");
            System.exit(-1);
        }

        try {
            // Use the Apache Xerces SAX Driver
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            // Start the server

        } catch (ClassNotFoundException e) {
            System.out.println("Could not locate SAX Driver");
        }
    }
}

At this point, you are ready to add the main portion of the code, which creates the HTTP listener that services XML-RPC requests, and then registers some handler classes that are available for remote procedure calls. Creating the listener is very simple; the WebServer helper class I have been discussing can be instantiated by supplying it the port to listen to, and just that easily, the server is servicing XML-RPC requests. Although no classes are available to be called yet, you do have a working XML-RPC server. Let's add in the line to create and start the server, as well as a status line for display purposes. You'll also need to add another import statement and exception handler, this one for java.io.IOException. Because the server must start up on a port, it can throw an IOException if the port is inaccessible or if other problems occur in server startup. The modified code fragment looks like this:

package javaxml2;

import java.io.IOException;

import helma.xmlrpc.WebServer;
import helma.xmlrpc.XmlRpc;

public class HelloServer {

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println(
                "Usage: java javaxml2.HelloServer [port]");
            System.exit(-1);
        }

        try {
            // Use the Apache Xerces SAX Driver
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            // Start the server
            System.out.println("Starting XML-RPC Server...");
            WebServer server = new WebServer(Integer.parseInt(args[0]));

        } catch (ClassNotFoundException e) {
            System.out.println("Could not locate SAX Driver");
        } catch (IOException e) {
            System.out.println("Could not start server: " +
                e.getMessage( ));
        }
    }
}

Compile this class and give it a try; it is completely functional, and should print out the status line and then pause, waiting for requests. You now need to add the handler class to the server so that it can receive requests.

One of the most significant differences between RMI and RPC is the way methods are made available. In RMI, a remote interface has the method signature for each remote method. If a method is implemented on the server class, but no matching signature is added to the remote interface, the new method cannot be invoked by an RMI client. This makes for a large amount of code modification and recompilation in the development of RMI classes. This process is quite a bit different, and is generally considered easier and more flexible, in RPC. When a request comes in to an RPC server, the request contains a set of parameters and a textual value, usually in the form "classname.methodname." This signifies to the RPC server that the requested method is in the class "classname" and is named "methodname." The RPC server tries to find a matching class and method that take parameter types that match the types within the RPC request as input. Once a match is made, the method is called, and the result is encoded and sent back to the client.

Thus, the method requested is never explicitly defined in the XML-RPC server, but rather in the request from the client. Only a class instance is registered with the XML-RPC server. You can add methods to that class, restart the XML-RPC server with no code changes (allowing it to register an updated class instance), and then immediately request the new methods within your client code. As long as you can determine and send the correct parameters to the server, the new methods are instantly accessible. This is one of the advantages of XML-RPC over RMI, in that it can more closely represent an API; there are no client stubs, skeletons, or interfaces that must be updated. If a method is added, the method signature can be published to the client community and used immediately.

Now that you've read about how easily an RPC handler can be used, I demonstrate how to register one in the HelloHandler example. The WebServer class allows the addition of a handler through the addHandler( ) method. This method takes a name as input to register the handler class to, and an instance of the handler class itself. This is typically accessed by instantiating a new class with its constructor (using the new keyword), although in the next section I'll look at using other methods, in the event that an instance should be shared instead of created by each client. In the current example, instantiating a new class is an acceptable solution. Register the HelloHandler class to the name "hello". You can include status lines to show what is occurring in the server as it adds the handler:

        try {
            // Use the Apache Xerces SAX Driver
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            // Start the server
            System.out.println("Starting XML-RPC Server...");
            WebServer server = new WebServer(Integer.parseInt(args[0]));

            // Register the handler class
            server.addHandler("hello", new HelloHandler( ));
            System.out.println(
                "Registered HelloHandler class to \"hello\"");

            System.out.println("Now accepting requests...");

        } catch (ClassNotFoundException e) {
            System.out.println("Could not locate SAX Driver");
        } catch (IOException e) {
            System.out.println("Could not start server: " +
                e.getMessage( ));
        }

Now recompile this source file and start up the server. Your output should look similar to Example 11-3.[19]

[19]If you are on a Unix machine, you must be logged in as the root user to start a service up on a port lower than 1024. To avoid these problems, consider using a higher numbered port, as shown in Example 11-3.

Example 11-3. Starting the server

$ java javaxml2.HelloServer 8585
Starting XML-RPC Server...
Registered HelloHandler class to "hello"
Now accepting requests...

It's that simple! You can now write a client for the server, and test communications across a network using XML-RPC. This is another advantage of XML-RPC; the barrier for entry into coding servers and clients is low, compared to the complexity of using RMI. Read on, and see creating a client is just as straightforward.

11.2.4. Writing the Client

With the server running and accepting requests, you done the hardest part of coding the XML-RPC application (believe it or not, that was the hard part!). Now you need to construct a simple client to call the sayHello( ) method remotely. This is made simple by using the helma.xmlrpc.XmlRpcClient . This class takes care of many of the details on the client side that its analogs, XmlRpcServer and WebServer, do on the server. To write your client, you need this class as well as the XmlRpc class; this client must handle encoding of the request, so again set the SAX driver class to use with the setDriver( ) method. Begin your client code with these required import statements, checking for an argument to pass as the parameter to the sayHello( ) method on the server, and some exception handling. Create the Java source file shown in Example 11-4 and save it as HelloClient.java.

Example 11-4. A client for the XML-RPC server

package javaxml2;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.XmlRpcClient;

public class HelloClient {

    public static void main(String args[]) {
        if (args.length < 1) {
            System.out.println(
                "Usage: java HelloClient [your name]");
            System.exit(-1);
        }

        try {
            // Use the Apache Xerces SAX Driver
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            // Specify the Server

            // Create request

            // Make a request and print the result

        } catch (ClassNotFoundException e) {
            System.out.println("Could not locate SAX Driver");
        }
    }
}

As with the rest of the code in this chapter, this is simple and straightforward. To create an XML-RPC client, you need to instantiate the XmlRpcClient class, which requires the hostname of the XML-RPC server to connect to. This should be a complete URL, including the http:// protocol prefix. In creating the client, a java.net.MalformedURLException can be thrown when this URL is in an unacceptable format. You can add this class to the list of imported classes, instantiate the client, and add the required exception handler:

package javaxml2;

import java.net.MalformedURLException;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.XmlRpcClient;

public class HelloClient {

    public static void main(String args[]) {
        if (args.length < 1) {
            System.out.println(
                "Usage: java HelloClient [your name]");
            System.exit(-1);
        }

        try {
            // Use the Apache Xerces SAX Driver
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            // Specify the server
            XmlRpcClient client =
                new XmlRpcClient("http://localhost:8585/");

            // Create request

            // Make a request and print the result

        } catch (ClassNotFoundException e) {
            System.out.println("Could not locate SAX Driver");
        } catch (MalformedURLException e) {
            System.out.println(
                "Incorrect URL for XML-RPC server format: " +
                e.getMessage( ));
        }
    }
}

Although no actual RPC calls are being made, you now have a fully functional client application. You can compile and run this application, although you won't see any activity, as no connection is made until a request is initiated.

WARNING: Make sure you use the port number in your source code that you plan to specify to the server when you start it up. Obviously, this is a poor way to implement connectivity between the client and server; changing the port the server listens to requires changing the source code of our client! In your own applications, make this a user-defined variable; I've kept it simple for example purposes.

The ease with which this client and our server come together is impressive. Still, this program is not of much use until it actually makes a request and receives a response. To encode the request, invoke the execute( ) method on your XmlRpcClient instance. This method takes in two parameters: the name of the class identifier and method to invoke, which is a single String parameter, and a Vector containing the method parameters to pass in to the specified method. The class identifier is the name you registered to the HelloHandler class on the XML-RPC server; this identifier can be the actual name of the class, but it is often something more readable and meaningful to the client, and in this case it was "hello". The name of the method to invoke is appended to this, separated from the class identifier with a period, in the form [class identifier].[method name]. The parameters must be in the form of a Java Vector, and should include any parameter objects that are needed by the specified method. In the simple sayHello( ) method, this is a String with the name of the user, which should have been specified on the command line.

Once the XML-RPC client encodes this request, it sends the request to the XML-RPC server. The server locates the class that matches the request's class identifier, and looks for a matching method name. If a matching method name is found, the parameter types for the method are compared with the parameters in the request. If a match occurs, the method is executed. If multiple methods are found with the same name, the parameters determine which method is invoked; this process allows normal Java overloading to occur in the handler classes. The result of the method invocation is encoded by the XML-RPC server, and sent back to the client as a Java Object (which in turn could be a Vector of Objects!). This result can be cast to the appropriate Java type, and used in the client normally. If a matching class identifier/method/parameter signature is not found, an XmlRpcException is thrown back to the client. This ensures the client is not trying to invoke a method or handler that does not exist, or sending incorrect parameters.

All this happens with a few additional lines of Java code. You must import the XmlRpcException class, as well as java.io.IOException; the latter is thrown when communication between the client and server causes error conditions. You can then add the Vector class and instantiate it, adding to it a single String parameter. This allows your code to invoke the execute( ) method with the name of the handler, the method to call, and its parameters; the result of this call is cast to a String, which is printed out to the screen. In this example, the local machine is running the XML-RPC server on port 8585:

package javaxml2;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Vector;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.XmlRpcClient;
import helma.xmlrpc.XmlRpcException;

public class HelloClient {

    public static void main(String args[]) {
        if (args.length < 1) {
            System.out.println(
                "Usage: java HelloClient [your name]");
            System.exit(-1);
        }

        try {
            // Use the Apache Xerces SAX Driver
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            // Specify the server
            XmlRpcClient client =
                new XmlRpcClient("http://localhost:8585/");

            // Create request
            Vector params = new Vector( );
            params.addElement(args[0]);

            // Make a request and print the result
            String result =
               (String)client.execute("hello.sayHello", params);

            System.out.println("Response from server: " + result);

        } catch (ClassNotFoundException e) {
            System.out.println("Could not locate SAX Driver");
        } catch (MalformedURLException e) {
            System.out.println(
                "Incorrect URL for XML-RPC server format: " +
                e.getMessage( ));
        } catch (XmlRpcException e) {
            System.out.println("XML-RPC Exception: " + e.getMessage( ));
        } catch (IOException e) {
            System.out.println("IO Exception: " + e.getMessage( ));
        }
    }
}

That's all that is required to make this work. Now compile your source code and open a command shell for running the example.

11.2.5. Talk to Me

Make sure that you have the XML-RPC classes and your example classes in your environment's classpath. Also, confirm that Apache Xerces or your chosen SAX driver is in your classpath and accessible, as the examples must load these classes for parsing. Once that is set up, start the HelloServer class by giving it a port number. On Windows, use the start command to start the server in a separate process:

c:\javaxml2\build>start java javaxml2.HelloServer 8585
Starting XML-RPC Server...
Registered HelloHandler class to "hello"
Now accepting requests...

On Unix, use the background processing command (&) to make sure you can run your client as well (or open another terminal window and duplicate your environment settings):

$ java javaxml2.HelloServer &
Starting XML-RPC Server...
Registered HelloHandler class to "hello"
Now accepting requests...

You can then run your client by specifying your name to the program as a command-line argument. You should quickly see a response (similar to that shown in Example 11-5) as the HelloServer receives your client's request, handles it, and returns the result of the sayHello( ) method, which is then printed by the client.

Example 11-5. Running the HelloClient class

$ java javaxml2.HelloClient Leigh
Response from server: Hello Leigh

You have just seen XML-RPC in action. Certainly this is not a particularly useful example, but it should have given you an idea of the basics and shown you the simplicity of coding an XML-RPC server and client in Java. With these fundamentals, I want to move on to a more realistic example. In the next section, I'll show you how to build a more useful server, and take a look at what XML-RPC handlers often look like. I'll then demonstrate creating a client (similar to our HelloClient) to test the new code.

Previous | Home | Next
11. XML-RPC | Book Index | 11.3. Putting the Load on the Server
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Putting the Load on the Server (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

11.3. Putting the Load on the Server

As instructional as the "hello" example has been in demonstrating how to use XML-RPC with Java, it isn't very realistic. In addition to being a trivial example, the server is not very flexible and the handler itself doesn't give any indication of how a practical XML-RPC handler might operate. Here I'll try to give an example of using XML-RPC in a production environment by increasing the usefulness of the handler and the usability of the server. While it's not code you might add to your current project, this example begins to demonstrate how XML-RPC might be of use, and how to build applications that can use XML-RPC but are not limited by it.

11.3.1. A Shared Handler

The HelloHandler class was simple, but useless in a practical application. Most XML-RPC uses relate to letting events occur on a server that is more suited for complex tasks, while allowing a thin client to request procedures to be executed and then use the returned results. In addition, it is possible that part or even all of the computations needed to respond to a request can be done in advance; in other words, the handler class may be running tasks and ensuring that results are already available when a method call comes in. As a Java coder, threads and shared instance data should leap to your mind. Here I'll take a look at a very simple Scheduler class to illustrate these principles.

The scheduler should allow clients to add and remove events. Clients can then query the scheduler for a list of all events in the queue. To make this more practical (and to have a task for the server to perform later), querying the current events returns them sorted by the time they occurred. An event for this example is simply a String event name and a time for the event (in a java.util.Date format). Though this is not a complete scheduler implementation, it can demonstrate how to let the server do behind-the-scenes work for clients.

First, code the addEvent( ) and removeEvent( ) methods. Because these are both client-triggered events, there is nothing particularly remarkable about them; what is worth thinking about is how to store these events in the Scheduler class. Although the XML-RPC server will instantiate this class, and that instance will be used for all XML-RPC calls coming into that server, it is possible and even probable that other classes or even XML-RPC servers may interact with the scheduler. If the scheduler stores a list of events as a member variable, multiple instances will not be able to share data. To solve this problem in this example, it's best to make the class's storage static, causing it to be shared across all Scheduler class instances. To store both an event name and an event time, a Hashtable would seem appropriate, allowing the use of key-value pairs. In addition to this Hashtable, the class stores the names of the events in a Vector. Although this uses some extra storage space (and memory in the Java Virtual Machine), the class can sort the Vector and not have to deal with sorting the Hashtable; the advantage is that it's simple to swap the event names in the Vector (a single swap) and not have to swap the event times in the Hashtable (two swaps for each exchange). With that information, you're ready to code the skeleton of this class, and add these first two methods to allow addition and removal of events. For now, add the storage as well, but I'll leave the implementation of the retrieval and sorting of events for later. Example 11-6 is a code listing for this new handler.

Example 11-6. The Scheduler class

package javaxml2;

import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

public class Scheduler {

    /** List of event names (for sorting) */
    private static Vector events = new Vector( );

    /** Event details (name, time) */
    private static Hashtable eventDetails = new Hashtable( );

    public Scheduler( ) {
    }

    public boolean addEvent(String eventName, Date eventTime) {
        // Add this event to the list of events
        if (!events.contains(eventName)) {
            events.addElement(eventName);
            eventDetails.put(eventName, eventTime);
        }

        return true;
    }

    public synchronized boolean removeEvent(String eventName) {
        events.remove(eventName);
        eventDetails.remove(eventName);

        return true;
    }
}

The addEvent( ) method adds the name of the event to both storage objects, and the time to the Hashtable. The removeEvent( ) method does the opposite. Both methods return a boolean value. Although in the example this value is always true, in a more complex implementation, this value could be used to indicate problems in the addition or removal of events.

With the ability to add and remove events, you now need to add a method that returns a list of events. This method returns all events added to the event store, regardless of the client or application that added them; in other words, these could be events added by a different XML-RPC client, a different XML-RPC server, another application, or a standalone implementation of this same scheduler. Since the method has to return a single Object result, it can return a Vector of formatted String values that contain the name of each event and its time. In a more useful implementation this might return the Vector of events or some other form of the events in a typed format (with the date as a Date object, etc.). This method acts more as a view of the data, though, and does not allow the client to further manipulate it. To return this list of events, the method uses the event store and the java.text.SimpleDateFormat class, which allows textual formatting of Date objects. Iterating through all events, a String is created with the event name and the time it is set for; each String is inserted into the Vector result list, and this list is returned to the client. Now add the required import statement and the code to return the events in the store to the scheduler code:

package javaxml2;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

public class Scheduler {

    // Existing method implementations

    public Vector getListOfEvents( ) {
        Vector list = new Vector( );

        // Create a Date Formatter
        SimpleDateFormat fmt =
            new SimpleDateFormat("hh:mm a MM/dd/yyyy");

        // Add each event to the list
        for (int i=0; i<events.size( ); i++) {
            String eventName = (String)events.elementAt(i);
            list.addElement("Event \"" + eventName +
                            "\" scheduled for " +
                            fmt.format(
                                (Date)eventDetails.get(eventName)));
        }

        return list;
    }
}

At this point, you could use this class as an XML-RPC handler without any problems. However, the point of this exercise is to look at how work can be done by the server while the client is performing other tasks. The getListOfEvents( ) method assumes the event list (the Vector variable events) is correctly ordered when this method is called, and that sorting has already occurred. I haven't shown you any code to sort the events yet, but more importantly, there isn't any code to trigger this sorting. Furthermore, as the event store increases, sorting is time-consuming, and the client should not wait for it to complete. First it makes sense to add a method that the class can use to sort the events. For simplicity, a bubble sort is used. (Discussion of sorting algorithms is beyond the scope of this book, so this code is presented without any explanation of its workings.) At the end of the method, though, the Vector variable events is sorted in order of the time the events within it occur. For information on this and other sorting algorithms, refer to AlgorithmsinJava by Robert Sedgewick and Tim Lindholm (Addison Wesley). The algorithm and method to handle sorting of the events are presented here, and should be added to your code:

package javaxml2;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

public class Scheduler {

    /** List of event names (for sorting) */
    private static Vector events = new Vector( );

    /** Event details (name, time) */
    private static Hashtable eventDetails = new Hashtable( );

    /** Flag to indicate if events are sorted */
    private static boolean eventsSorted;

    // Existing method implementations

    private synchronized void sortEvents( ) {
        if (eventsSorted) {
            return;
        }

        // Create array of events as they are (unsorted)
        String[] eventNames = new String[events.size( )];
        events.copyInto(eventNames);

        // Bubble sort these
        String tmpName;
        Date date1, date2;
        for (int i=0; i<eventNames.length - 1; i++) {
            for (int j=0; j<eventNames.length - i - 1; j++) {
                // Compare the dates for these events
                date1 = (Date)eventDetails.get(eventNames[j]);
                date2 = (Date)eventDetails.get(eventNames[j+1]);
                if (date1.compareTo(date2) > 0) {

                    // Swap if needed
                    tmpName = eventNames[j];
                    eventNames[j] = eventNames[j+1];
                    eventNames[j+1] = tmpName;

                }
            }
        }

        // Put into new Vector (ordered)
        Vector sortedEvents = new Vector( );
        for (int i=0; i<eventNames.length; i++) {
            sortedEvents.addElement(eventNames[i]);
        }

        // Update the global events
        events = sortedEvents;
        eventsSorted = true;

    }
}

In addition to the core algorithm, the code imports the java.util.Enumeration class and adds a boolean member variable, eventsSorted. This flag allows shortcircuiting of the execution of the sorting when the events are already ordered. Although you have not yet added code to update this flag, it's easy to do so. The sorting method already indicates that events are sorted at its completion. The class's constructor should initially set this value to true, indicating that all events are in order. It is only when events are added that the list may become unordered, so in the addEvents( ) method you'll need to set this flag to false if an event is added. This lets the Scheduler class know that something should occur to trigger the sort. When the getListOfEvents( ) method is invoked, the events will be ordered and ready for retrieval. You should add code to the constructor and the method for adding events that will update this flag:

package javaxml2;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

public class Scheduler {

    public Scheduler( ) {
        eventsSorted = true;
    }

    public boolean addEvent(String eventName, Date eventTime) {
        // Add this event to the list of events
        if (!events.contains(eventName)) {
            events.addElement(eventName);
            eventDetails.put(eventName, eventTime);
            eventsSorted = false;
        }

        return true;
    }

    // Other method implementations
}

You do not need to make any changes to the removeEvent( ) method, as removing an entry does not affect the order of the events. The ideal mechanism to handle server-side processing while freeing the client for further action is a thread that sorts events. With this thread started in the JVM, client processing can continue without waiting for the thread to complete. This is particularly important in a multithreaded environment where synchronization and threads waiting for object locks are in use. In this example, I've avoided threading issues, but you can add the relevant code to handle these issues fairly easily. You'll want to create an inner class that extends Thread , and does nothing but invoke the sortEvents( ) method. You can then add to the addEvents( ) method the code that creates and starts this thread when events are added. Then any additional events trigger a resorting of the events, but allow the client to continue with its actions (which might include adding additional events, in turn starting more threads to sort the data). When the client does request the list of events, the events should be sorted when returned, all without the client ever waiting on this action to occur or spending processing power to make it happen. The addition of the inner class to sort, as well as the code to run that class as a thread in our addEvents( ) method, rounds out the Scheduler class and is shown here:

package javaxml2;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

public class Scheduler {

    // Existing variables and methods

    public boolean addEvent(String eventName, Date eventTime) {
        // Add this event to the list of events
        if (!events.contains(eventName)) {
            events.addElement(eventName);
            eventDetails.put(eventName, eventTime);
            eventsSorted = false;

            // Start thread on server sorting
            SortEventsThread sorter = new SortEventsThread( );
            sorter.start( );
        }

        return true;
    }

    class SortEventsThread extends Thread {

        public void run( ) {
            sortEvents( );
        }
    }
}

Now when you compile the modified source code, you'll have a threaded scheduler that performs the process-intensive task of sorting on the server, allowing any clients to work uninterrupted while the sorting occurs. This is still a simple example of using a handler class properly, but it does introduce the concepts of resource distribution and letting a server handle the workload when possible. To complement this more advanced handler class, I'll next demonstrate building a more robust XML-RPC server.

11.3.2. A Configurable Server

The XML-RPC server class still needs some work. The current version requires you to specifically add handler classes to the server in the code. This means the addition of a new handler class requires coding and recompilation. Not only is this undesirable from a change-control perspective, but it is annoying and time-consuming. Obtaining the newest code from a source control system, adding the change, and testing to add one or two handlers is not practical, and won't win you friends among management. What is preferred is to have a robust server that can read this sort of information from a configuration file and load the needed classes at runtime. We will build a lightweight server to do this now.

To begin, you'll want to create a new server class. You can either start from scratch, or copy and paste from the HelloServer class given earlier in this chapter. Start by setting up our framework, adding the required import statements, and instantiating the server, similar to the earlier example; however, you should not add any code that registers handlers, as there will be a helper method to load the needed information from a file. The one change from the earlier version is that this class requires an additional command-line parameter that should be the name of a file. The server will read this file in using methods I'll cover later, and add handlers to the server. You can create the LightweightXmlRPcServer class, which continues to use the thin WebServer helper class, with the code shown in Example 11-7.

Example 11-7. A reusable XML-RPC server

package javaxml2;

import java.io.IOException;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.WebServer;

public class LightweightXmlRpcServer {

    /** The XML-RPC server utility class */
    private WebServer server;

    /** Port number to listen on */
    private int port;

    /** Configuration file to use */
    private String configFile;

    public LightweightXmlRpcServer(int port, String configFile) {
        this.port = port;
        this.configFile = configFile;
    }

    public void start( ) throws IOException {
        try {
            // Use Apache Xerces SAX Parser
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            System.out.println("Starting up XML-RPC Server...");
            server = new WebServer(port);

            // Register handlers

        } catch (ClassNotFoundException e) {
            throw new IOException("Error loading SAX parser: " +
                e.getMessage( ));
        }
    }

    public static void main(String[] args) {

        if (args.length < 2) {
            System.out.println(
                "Usage: " +
                "java com.oreilly.xml.LightweightXmlRpcServer " +
                "[port] [configFile]");
            System.exit(-1);
        }

        LightweightXmlRpcServer server =
            new LightweightXmlRpcServer(Integer.parseInt(args[0]),
                                        args[1]);

        try {
            // Start the server
            server.start( );
        } catch (IOException e) {
            System.out.println(e.getMessage( ));
        }
    }
}

Nothing remarkable here. The code ensures that the required parameters are passed in and then starts the server on the requested port. It's now time to add in methods to load the handlers from a file, and then add those handlers one by one to the server.

Because each handler needs a name and an associated class, you can create a configuration file that has these two pieces of information. With Java, it is easy to load and instantiate a class with its complete package and name. This means you can completely represent a new handler with a pair of textual values. Within this file, you can add both the original HelloHandler class as well as the new Scheduler class. Since you are writing the file parser as well, it's safe to arbitrarily decide to use commas as delimiters and the pound sign (#) as a comment marker. In fact, you can use whatever format you wish as long as you write code that uses your conventions in parsing the file.

NOTE: You may be surprised that I'm not using an XML file format here. There are several reasons for this. First, I'm going to talk about SOAP in the next chapter, which uses XML throughout. Using a non-XML format here provides a good contrast between the two methodologies. Second, you're certainly prepared at this point to write your own XML parsing code, so this task is a good exercise. And third, I'm a realist; you'll be amazed at how many times "XML frameworks" and "XML applications" use non-XML formats. So get used to it now, as you're sure to encounter it time and time again.

Create the configuration file shown in Example 11-8, which will add the HelloHandler class under the class identifier "hello" and the Scheduler class under the class identifier "scheduler", and save it as xmlrpc.conf.

Example 11-8. XML-RPC configuration file

# Hello Handler: sayHello( )
hello,javaxml2.HelloHandler

# Scheduler: addEvent(), removeEvent(), getEvents( )
scheduler,javaxml2.Scheduler

For documentation purposes, I've specified the methods available to each handler in comments. This allows future maintainers of this configuration file to know what methods are available for each handler.

Java's I/O classes make it easy to load this file and read its contents. It's simple to create a helper method that reads the specified file and stores the pairs of values in a Java Hashtable. The object can then be passed on to another helper that loads and registers each handler. This example method does not do extensive error checking as a production-ready server might, and it simply ignores any line without a pair of comma-separated values. It is easy to add error handling if you want to use this code in your applications. Once it finds a line with a pair of values, the line is broken up and the class identifier and class name are stored as an entry within the Hashtable. Add the import statements for the required utility classes and then the new getHandlers( ) method to the LightweightServer class now:

package javaxml2;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Hashtable;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.WebServer;

public class LightweightXmlRpcServer {

    // Existing method implementations

    private Hashtable getHandlers( ) throws IOException {

        Hashtable handlers = new Hashtable( );

        BufferedReader reader =
            new BufferedReader(new FileReader(configFile));
        String line = null;

        while ((line = reader.readLine( )) != null) {
            // Syntax is "handlerName, handlerClass"
            int comma;

            // Skip comments
            if (line.startsWith("#")) {
                continue;
            }

            // Skip empty or useless lines
            if ((comma = line.indexOf(",")) < 2) {
                continue;
            }

            // Add the handler name and the handler class
            handlers.put(line.substring(0, comma),
                         line.substring(comma+1));
        }

        return handlers;
    }
}

Instead of adding code to save the result of this method, you can use that result as input to a method that iterates through the Hashtable and adds each handler to the server. The code needed to accomplish this task is not complicated; the only notable thing is that the addHandler( ) method of WebServer requires an instantiated class as a parameter. The code is required to take the name of the class to register from the Hashtable, load that class into the JVM with Class.forName( ), and then instantiate that class with newInstance( ). This is the methodology used in class loaders and other dynamic applications in Java, but may be unfamiliar to you if you are new to Java or have not had to dynamically instantiate classes from a textual name before. Once the class is loaded in this way, it and the class identifier are passed to the addHandler( ) method, and the iteration continues. Once the contents of the Hashtable are loaded, the server is set up and ready to go. I've used the Enumeration class to cycle through the keys in the Hashtable, so you'll need to add this import statement to your source file:

package javaxml2;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.WebServer;

public class LightweightXmlRpcServer {

    // Existing method implementations

    private void registerHandlers(Hashtable handlers) {
        Enumeration handlerNames = handlers.keys( );

        // Loop through the requested handlers
        while (handlerNames.hasMoreElements( )) {
            String handlerName = (String)handlerNames.nextElement( );
            String handlerClass = (String)handlers.get(handlerName);

            // Add this handler to the server
            try {
                server.addHandler(handlerName,
                    Class.forName(handlerClass).newInstance( ));

                System.out.println("Registered handler " + handlerName +
                                   " to class " + handlerClass);
            } catch (Exception e) {
                System.out.println("Could not register handler " +
                                   handlerName + " with class " +
                                   handlerClass);
            }
        }
    }
}

This is simply a complement to the getHandlers( ) method; in fact, it takes the result of that method as input. It uses the String values within the Hashtable and registers each. Now the server is running and will have any handlers in the configuration file loaded and available for remote calls. You could just as easily have consolidated these methods into one larger method. However, the purposes of the two methods are significantly different; while one, getHandlers( ), deals with parsing a file, the other, registerHandlers( ), deals with registering handlers once information about the handlers is available. With this methodology, you can change the way you parse the configuration file (or even have it read from a database or other medium) without having to worry about the way the handlers are registered.

Once you have added these two helper methods, add their invocation to the start( ) method of your server class:

    public void start( ) throws IOException {
        try {
            // Use Apache Xerces SAX Parser
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            System.out.println("Starting up XML-RPC Server...");
            server = new WebServer(port);

            // Register handlers
            registerHandlers(getHandlers( ));

        } catch (ClassNotFoundException e) {
            throw new IOException("Error loading SAX parser: " +
                e.getMessage( ));
        }
    }

Compile this code, ensure you have created the configuration file, and your server is ready for use.

11.3.3. A Useful Client

The new client has no new concepts or techniques in it; just as the HelloClient class was simple, so is the SchedulerClient class. It needs to start up an XML-RPC client, invoke handler methods, and print out the result of those handlers. The complete code for the client is here. Comments indicate what is occurring, and since this is all ground already covered, you can simply enter the code in Example 11-9 into your editor and compile it.

Example 11-9. The SchedulerClient class

package javaxml2;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.XmlRpcClient;
import helma.xmlrpc.XmlRpcException;

public class SchedulerClient {

    public static void addEvents(XmlRpcClient client)
        throws XmlRpcException, IOException {

        System.out.println("\nAdding events...\n");

        // Parameters for events
        Vector params = new Vector( );

        // Add an event for next month
        params.addElement("Proofread final draft");

        Calendar cal = Calendar.getInstance( );
        cal.add(Calendar.MONTH, 1);
        params.addElement(cal.getTime( ));

        // Add the event
        if (((Boolean)client.execute("scheduler.addEvent", params))
                            .booleanValue( )) {
            System.out.println("Event added.");
        } else {
            System.out.println("Could not add event.");
        }

        // Add an event for tomorrow
        params.clear( );
        params.addElement("Submit final draft");

        cal = Calendar.getInstance( );
        cal.add(Calendar.DAY_OF_MONTH, 1);
        params.addElement(cal.getTime( ));

        // Add the event
        if (((Boolean)client.execute("scheduler.addEvent", params))
                            .booleanValue( )) {
            System.out.println("Event added.");
        } else {
            System.out.println("Could not add event.");
        }

    }

    public static void listEvents(XmlRpcClient client)
        throws XmlRpcException, IOException {

        System.out.println("\nListing events...\n");

        // Get the events in the scheduler
        Vector params = new Vector( );
        Vector events =
            (Vector)client.execute("scheduler.getListOfEvents", params);
        for (int i=0; i<events.size( ); i++) {
            System.out.println((String)events.elementAt(i));
        }
    }

    public static void main(String args[]) {

        try {
            // Use the Apache Xerces SAX Parser Implementation
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            // Connect to server
            XmlRpcClient client =
                new XmlRpcClient("http://localhost:8585/");

            // Add some events
            addEvents(client);

            // List events
            listEvents(client);

        } catch (Exception e) {
            System.out.println(e.getMessage( ));
        }
    }
}

As you are entering this code, notice that the events are added in reverse order of the event time. The server rearranges these events with the sortEvents( ) method to facilitate correctly ordered results when the getListOfEvents( ) method is called. The server takes care of this sorting next.

11.3.4. Talk to Me (Again)

Once you have entered the code for the handler, server, and client, compile all of the source files. You also need to create the configuration file that lists handlers to register with the XML-RPC server discussed previously in this chapter, in Section 11.3.2, "A Configurable Server". First, start up the XML-RPC server as a separate process:

c:\javaxml2\build>start java javaxml2.LightweightXmlRpcServer 8585
                        c:\javaxml2\ch11\conf\xmlrpc.conf

On Unix, use:

$ java javaxml2.LightweightServer 8585 conf/xmlrpc.conf &

You should see the server indicate that the handlers in the supplied configuration file are registered to the names you provided:

Starting up XML-RPC Server...
Registered handler scheduler to class javaxml2.Scheduler
Registered handler hello to class javaxml2.HelloHandler
NOTE: If you never stopped the previous XML-RPC server, HelloServer, you will get an error trying to start another server on the same port. Be sure to stop the HelloServer before trying to start the LightweightXmlRpcServer.

Finally, execute your client and see the results:

$ java javaxml2.SchedulerClient

Adding events...

Event added.
Event added.

Listing events...

Event "Submit final draft" scheduled for 10:55 AM 05/09/2001
Event "Proofread final draft" scheduled for 10:55 AM 06/08/2001

You should not notice a significant pause as your client adds and lists events, yet the server still sorts the events in a separate thread within the server JVM (and bubble sorting is not a quick algorithm, by the way). You have written your first useful XML-RPC application!

Previous | Home | Next
11.2. Saying Hello | Book Index | 11.4. The Real World
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Real World (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

11.4. The Real World

I'll conclude this chapter with a short look at some important details of using XML-RPC in the real world. This continues the focus on allowing you to use XML not because it is the newest and neatest technology, but because it is the best for solving certain situations. All of the knowledge within this book, the XML specifications, and other XML books will not make your application operate as well as it could if you do not know when and how to use XML and XML-RPC correctly! This section highlights some of the common issues that arise in using XML-RPC.

11.4.1. Where's the XML in XML-RPC?

After working through this chapter, you may have been surprised that you didn't have to write any SAX, DOM, or JDOM code. In fact, you used very little XML directly at all. This is because the XML-RPC libraries were responsible for the encoding and decoding of the requests that your clients sent to and from the servers. While this may seem a little bit of a letdown, as you didn't write any code that directly manipulates XML, you are definitely using XML technology. The simple request to the sayHello( ) method was actually translated to an HTTP call that looks like Example 11-10.

Example 11-10. XML for XML-RPC request

POST /RPC2 HTTP/1.1
User-Agent: Tomcat Web Server/3.1 Beta (Sun Solaris 2.6)
Host: newInstance.com
Content-Type: text/xml
Content-length: 234

<?xml version="1.0"?>
<methodCall>
  <methodName>hello.sayHello</methodName>
  <params>
    <param>
      <value><string>Brett</string></value>
    </param>
  </params>
</methodCall>

The XML-RPC libraries on the server receive this and decode it, matching it with a handler method (if one is available that matches). The requested Java method is then invoked, and the server encodes the result back into XML, as shown in Example 11-11.

Example 11-11. XML underlying an XML-RPC response

HTTP/1.1 200 OK
Connection: close
Content-Type: text/xml
Content-Length: 149
Date: Mon, 11 Apr 2000 03:32:19 CST
Server: Tomcat Web Server/3.1 Beta-Sun Solaris 2.6

<?xml version="1.0"?>
<methodResponse>
  <params>
    <param>
      <value><string>Hello Brett</string></value>
    </param>
  </params>
</methodResponse>

This communication all happens without you having to worry about the details.

11.4.2. Shared Instances

In the examples, I looked at using static data objects to share data across multiple instances of the same class. However, there are times when an instance itself is shared. This may not be because of an XML-RPC need, but because of a need to use the class differently on the server. For example, the singleton design pattern in Java mandates that only one instance of a class ever be created, and that instance is shared across all applications. This is usually accomplished by using a static method called getInstance( ) instead of constructing the object:

Scheduler scheduler;

// Get the single instance, which is managed in the Scheduler class
scheduler = Scheduler.getInstance( );

// Add an event for right now
scheduler.addEvent("Picnic", new Date( ));

To ensure that no classes directly instantiate the Scheduler class, the constructor is usually made private or protected. While this forces clients to use this code to get an instance, it can also cause confusion when using the class as an XML-RPC handler. Remember that registering a handler has always been accomplished with the instantiation of the handler class. However, the WebServer class requires only a valid instance as a parameter, not necessarily a new instance. For example, the following code is a perfectly acceptable way to add a handler:

WebServer server = new WebServer(8585);

// Create a handler class
HelloHandler hello = new HelloHandler( );
server.addHandler("hello", hello);

The server class does not distinguish between these methodologies, as long as the handler class is instantiated when it gets passed into the addHandler( ) method. So you can make a small change to this code if you want to add an instance of the singleton Scheduler class described previously:

WebServer server = new WebServer(8585);

// Pass in the singleton instance
server.addHandler("scheduler", Scheduler.getInstance( ));

This passes in the shared instance just as if the class were being instantiated through a constructor with the new keyword, and preserves any information shared across the singleton class. Many classes used in services such as XML-RPC are built as singletons to avoid the use of static data variables, as a shared instance allows the data to be stored in member variables; the single instance then operates upon those member variables for all client requests.

11.4.3. To Servlet or Not To Servlet

The use of a servlet as an XML-RPC server has become a popular option recently. For more details on servlets, see Jason Hunter's Java Servlet Programming (O'Reilly). In fact, the XML-RPC Java classes that you downloaded include a servlet with the distribution. It is both legal and common to use a servlet in this way, having the servlet do nothing but field XML-RPC requests. However, it is not always the best idea.

If you have a machine that must serve other HTTP requests for Java tasks, then a servlet engine is a good choice for handling the details of these requests. In this case, running a servlet as an XML-RPC server is a good idea. However, one of the advantages of XML-RPC is it allows handler classes with complex, process-intensive tasks to be separated from other application code. The Scheduler class could be placed on a server with classes that performed complex indexing, algorithmic modeling, and perhaps graphical transformations. All of these functions are very expensive for application clients to perform. However, adding a servlet engine and accepting application requests for other tasks as well as the XML-RPC handling greatly reduces the processing power available to these handler classes. In this case, the only requests that should be coming to the server are for these handler classes.

In the case where only XML-RPC requests are accepted (as indicated previously), it is rarely a good idea to use a servlet for the XML-RPC server. The provided WebServer class is small, light, and designed specifically for handling XML-RPC requests over HTTP. A servlet engine is designed to accept any HTTP request, and is not tuned as well for XML-RPC requests in particular. Over time, you will begin to see performance degradation in the servlet engine as compared to the WebServer class. Unless you have a compelling reason to use a servlet for other non-XML-RPC tasks, stick with the lightweight XML-RPC server designed for the purpose you need.

Previous | Home | Next
11.3. Putting the Load on the Server | Book Index | 11.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

11.5. What's Next?

Now that you have a handle on RPC and XML-RPC, it's time to take the next logical step. That step is SOAP, the Simple Object Access Protocol. SOAP builds upon XML-RPC to add support for custom object types, better error reporting, and more features. It's also quite the rage these days. In the next chapter, I'll give you the lowdown so you'll be prepared.

Previous | Home | Next
11.4. The Real World | Book Index | 12. SOAP
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SOAP (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 12. SOAP

Contents:

Starting Out
Setting Up
Getting Dirty
Going Further
What's Next?

SOAP is the Simple Object Access Protocol. If you haven't heard of it by now, you've probably been living under a rock somewhere. It's become the newest craze in web programming, and is integral to the web services fanaticism that has taken hold of the latest generation of web development. If you've heard of .NET from Microsoft or the peer-to-peer "revolution," then you've heard about technologies that rely on SOAP (even if you don't know it). There's not one but two SOAP implementations going on over at Apache, and Microsoft has hundreds of pages on their MSDN web site devoted to it (http://msdn.microsoft.com).

In this chapter, I explain what SOAP is, and why it is such an important part of where the web development paradigm is moving. That will help you get the fundamentals down, and prepare you for actually working with a SOAP toolkit. From there, I briefly run over the SOAP projects currently available, and then delve into the Apache implementation. This chapter is not meant to be the complete picture on SOAP; the next chapter fills in lots of gaps. Take this as the first part of a miniseries; many of your questions at the end of this chapter will be answered in the next.

12.1. Starting Out

The first thing to do is get an understanding of what SOAP is. You can read through the complete W3C note submission, which is fairly lengthy, at http://www.w3.org/TR/SOAP. When you take away all of the hype, SOAP is just a protocol. It's a simple protocol (to use, not necessarily to write), based on the idea that at some point in a distributed architecture, you'll need to exchange information. Additionally, in a system that is probably overtaxed and process-heavy, this protocol is lightweight, requiring a minimal amount of overhead. Finally, it allows all this to occur over HTTP, which allows you to get around tricky issues like firewalls and keep away from having all sorts of sockets listening on oddly numbered ports. Once you get that down, everything else is just details.

Of course, I'm sure you're here for the details, so I won't leave them out. There are three basic components to the SOAP specification: the SOAP envelope, a set of encoding rules, and a means of interaction between request and response. Begin to think about a SOAP message as an actual letter; you know, those antiquated things in envelopes with postage and an address scrawled across the front? That analogy helps SOAP concepts like "envelope" make a lot more sense. Figure 12-1 seeks to illustrate the SOAP process in terms of this analog.

Figure 12-1. The SOAP message process

With this picture in your head, let's look at the three components of the SOAP specification. I cover each briefly and provide examples that illustrate these concepts more completely. Additionally, it's these three key components that make SOAP so important and valuable. Error handling, support for a variety of encodings, serialization of custom parameters, and the fact that SOAP runs over HTTP make it more attractive in many cases than the other choices for a distributed protocol.[20] Additionally, SOAP provides a high degree of interoperability with other applications, which I delve into more completely in the next chapter. For now, I want to focus on the basic pieces of SOAP.

[20]There's a lot of talk about running SOAP over other protocols, like SMTP (or even Jabber). This isn't part of the SOAP standard, but it may be added in the future. Don't be surprised if you see it discussed.

12.1.1. The Envelope

The SOAP envelope is analogous to the envelope of an actual letter. It supplies information about the message that is being encoded in a SOAP payload, including data relating to the recipient and sender, as well as details about the message itself. For example, the header of the SOAP envelope can specify exactly how a message must be processed. Before an application goes forward with processing a message, the application can determine information about a message, including whether it will even be able to process the message. Distinct from the situation with standard XML-RPC calls (remember that? XML-RPC messages, encoding, and the rest are all wrapped into a single XML fragment), with SOAP actual interpretation occurs in order to determine something about the message. A typical SOAP message can also include the encoding style, which assists the recipient in interpreting the message. Example 12-1 shows the SOAP envelope, complete with the specified encoding.

Example 12-1. The SOAP envelope

<soap:Envelope
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
   soap:encodingStyle="http://myHost.com/encodings/secureEncoding"
>

 <soap:Body>
   <article xmlns="http://www.ibm.com/developer">
     <name>Soapbox</name>
     <url>
       http://www-106.ibm.com/developerworks/library/x-soapbx1.html
     </url>
   </article>
  </soap:Body>
</soap:Envelope>

You can see that an encoding is specified within the envelope, allowing an application to determine (using the value of the encodingStyle attribute) whether it can read the incoming message situated within the Body element. Be sure to get the SOAP envelope namespace correct, or SOAP servers that receive your message will trigger version mismatch errors, and you won't be able to interoperate with them.

12.1.2. Encoding

The second major element that SOAP brings to the table is a simple means of encoding user-defined datatypes. In RPC (and XML-RPC), encoding can only occur for a predefined set of datatypes: those that are supported by whatever XML-RPC toolkit you download. Encoding other types requires modifying the actual RPC server and clients themselves. With SOAP, however, XML schemas can be used to easily specify new datatypes (using the complexType structure discussed way back in Chapter 2, "Nuts and Bolts"), and those new types can be easily represented in XML as part of a SOAP payload. Because of this integration with XML Schema, you can encode any datatype in a SOAP message that you can logically describe in an XML schema.

12.1.3. Invocation

The best way to understand how SOAP invocation works is to compare it with something you already know, such as XML-RPC. If you recall, an XML-RPC call would look something like the code fragment shown in Example 12-2.

Example 12-2. Invocation in XML-RPC

// Specify the XML parser to use
XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

// Specify the server to connect to
XmlRpcClient client =
    new XmlRpcClient("http://rpc.middleearth.com");

// Create the parameters
Vector params = new Vector( );
params.addElement(flightNumber);
params.addElement(numSeats);
params.addElement(creditCardType);
params.addElement(creditCardNum);

// Request reservation
Boolean boughtTickets =
    (Boolean)client.execute("ticketCounter.buyTickets", params);

// Deal with the response

I've coded up a simple ticket counter-style application. Now, look at Example 12-3, which shows the same call in SOAP.

Example 12-3. Invocation in SOAP

// Create the parameters
Vector params = new Vector( );
params.addElement(
    new Parameter("flightNumber", Integer.class, flightNumber, null));
params.addElement(
    new Parameter("numSeats", Integer.class, numSeats, null));
params.addElement(
    new Parameter("creditCardType", String.class, creditCardType, null));
params.addElement(
    new Parameter("creditCardNumber", Long.class, creditCardNum, null));

// Create the Call object
Call call = new Call( );
call.setTargetObjectURI("urn:xmltoday-airline-tickets");
call.setMethodName("buyTickets");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
call.setParams(params);

// Invoke
Response res = call.invoke(new URL("http://rpc.middleearth.com"), "");

// Deal with the response

As you can see, the actual invocation itself, represented by the Call object, is resident in memory. It allows you to set the target of the call, the method to invoke, the encoding style, the parameters, and more not shown here. It is more flexible than the XML-RPC methodology, allowing you to explicitly set the various parameters that are determined implicitly in XML-RPC. You'll see quite a bit more about this invocation process in the rest of the chapter, including how SOAP provides fault responses, an error hierarchy, and of course the returned results from the call.

With that brief introduction, you probably know enough to want to get on with the fun stuff. Let me show you the SOAP implementation I'm going to use, explain why I made that choice, and get to some code.

Previous | Home | Next
11.5. What's Next? | Book Index | 12.2. Setting Up
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Setting Up (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

12.2. Setting Up

Now that you have some basic concepts down, it's time to get going on the fun part, the code. You need a project or product for use, which turns out to be simpler to find than you might think. If you want a Java-based project providing SOAP capability, you don't have to look that far. There are two groups of products out there: commercial and free. As in most of the rest of the book, I'm steering away from covering commercial products. This isn't because they are bad (on the contrary, some are wonderful); it's because I want every reader of this book to be able to use every example. That calls for accessibility, something commercial products don't provide; you have to pay to use them, or download them and at some point the trial period runs out.

That brings us to open source projects. In that realm, I see only one available: Apache SOAP. Located online at http://xml.apache.org/soap, this project seeks to provide a SOAP toolkit in Java. Currently in a Version 2.2 release, you can download it from the Apache web site. That's the version and project I use for the examples throughout this chapter.

12.2.1. Other Options

Before moving on to the installation and setup of Apache SOAP, I will answer a few questions that might be rattling around in your head. It's probably clear why I'm not using a commercial product. However, you may be thinking of a couple of other open source or related options that you might want to use, and wondering why I am not covering those.

12.2.1.1. What about IBM SOAP4J?

First on the list of options is IBM's SOAP implementation, IBM SOAP4J. IBM's work is actually the basis of the current Apache SOAP project, much as IBM XML4J fed into what is now the Apache Xerces XML parser project. Expect the IBM implementation to resurface, wrapping the Apache SOAP project's implementation. This is similar to what is happening with IBM's XML4J; it currently just provides IBM packaging over Xerces. This makes some additional levels of vendor-backing available to the open source version, although the two (Apache and IBM) projects are using the same codebase.

12.2.1.2. Isn't Microsoft a player?

Yes. Without a doubt, Microsoft and its SOAP implementation, as well as the whole .NET initiative (covered more in the next chapter), are very important. In fact, I wanted to spend some time covering Microsoft's SOAP implementation in detail, but it only supports COM objects and the like, without Java support. For this reason, coverage of it doesn't belong in a book on Java and XML. However, Microsoft (despite the connotations we developers tend to have about the company) is doing important work in web services, and you'd be making a mistake in writing it off, at least in this particular regard. If you need to communicate with COM or Visual Basic components, I highly recommend checking out the Microsoft SOAP toolkit, found online at http://msdn.microsoft.com/library/default.asp?url=/nhp/Default.asp?contentid=28000523 along with a lot of other SOAP resources.

12.2.1.3. What's Axis?

Those of you who monitor activity in Apache may have heard of Apache Axis. Axis is the next-generation SOAP toolkit, also being developed under the Apache XML umbrella. With SOAP (the specification, not a specific implementation) undergoing fairly fast and radical change these days, tracking it is difficult. Trying to build a version of SOAP that meets current requirements and moves with new development is also awfully tough. As a result, the current Apache SOAP offering is somewhat limited in its construction. Rather than try to rearchitect an existing toolkit, the Apache folks started fresh with a new codebase and project; thus, Axis was born. Additionally, the naming of SOAP was apparently going to change, from SOAP to XP and then to XMLP. As a result, the name of this new SOAP project was uncoupled from the specification name; thus, you have "Axis." Of course, now it looks like the W3C is going back to calling the specification SOAP (Version 1.2, or Version 2.0), so things are even more confusing!

Think of IBM SOAP4J as architecture 1 of the SOAP toolkit. Following that is Apache SOAP (covered in this chapter), which is architecture 2. Finally, Axis provides a next-generation architecture, architecture 3. This project is driven by SAX, while Apache SOAP is based upon DOM. Additionally, Axis provides a more user-friendly approach in header interaction, something missing in Apache SOAP. With all of these improvements, you're probably wondering why I'm not covering Axis. It's simply too early. Axis is presently trying to get together a 0.51 release. It's not a beta, or even an alpha, really; it's very early on. While I'd love to cover all the new Axis features, there's no way your boss is going to let you put in a pre-alpha release of open source software in your mission-critical systems, now is there? As a result, I've chosen to focus on something you can use, today: Apache SOAP. I'm sure when Axis does finalize, I'll update this chapter in a subsequent revision of the book. Until then, let's focus on a solution you can use.

12.2.2. Installation

There are two forms of installation with regard to SOAP. The first is running a SOAP client, using the SOAP API to communicate with a server that can receive SOAP messages. The second is running a SOAP server, which can receive messages from a SOAP client. I cover installation of both cases in this section.

12.2.2.1. The client

To use SOAP on a client, you first need to download Apache SOAP, available online at http://xml.apache.org/dist/soap. I've downloaded Version 2.2, in the binary format (in the version-2.2 subdirectory). You should then extract the contents of the archive into a directory on your machine; my installation is in the javaxml2 directory (c:\javaxml2 on my Windows machine, /javaxml2 on my Mac OS X machine). The result is /javaxml2/soap-2_2. You'll also need to download the JavaMail package, available from Sun at http://java.sun.com/products/javamail/. This is for the SMTP transfer protocol support included in Apache SOAP. Then, download the JavaBeans Activation Framework (JAF), also from Sun, available online at http://java.sun.com/products/beans/glasgow/jaf.html. I'm assuming that you still have Xerces or another XML parser available for use.

NOTE: Ensure your XML parser is JAXP-compatible and namespace-aware. Your parser, unless it's a very special case, probably meets both of these requirements. If you have problems, go back to using Xerces.

Use a recent version of Xerces; Version 1.4 or greater should suffice. There are a number of issues with SOAP and Xerces 1.3(.1), so I'd avoid that combination like the plague.

Expand both the JavaMail and JAF packages, and then add the included jar files to your classpath, as well as the soap.jar library. Each of these jar files is either in the root directory or in the lib/ directory of the relevant installation. At the end, your classpath should look something like this:

$ echo $CLASSPATH
/javaxml2/soap-2_2/lib/soap.jar:/javaxml2/lib/xerces.jar:
/javaxml2/javamail-1.2/mail.jar:/javaxml2/jaf-1.0.1/activation.jar

On Windows, it should look like:

c:\>echo %CLASSPATH%
c:\javaxml2\soap-2_2\lib\soap.jar;c:\javaxml2\lib\xerces.jar;
c:\javaxml2\javamail-1.2\mail.jar;c:\javaxml2\jaf-1.0.1\activation.jar

Finally, add the javaxml2/soap-2_2/ directory to your classpath if you want to run the SOAP examples. I cover setup for specific examples in this chapter as I get to them.

12.2.2.2. The server

To build a SOAP-capable set of server components, you first need a servlet engine. As in earlier chapters, I'll use Apache Tomcat (available from http://jakarta.apache.org) throughout this chapter for examples. You'll then need to add everything needed on the client to the server's classpath. The easiest way to do that is to drop soap.jar, activation.jar, and mail.jar, as well as your parser, in your servlet engine's library directory. On Tomcat, this is simply the lib/ directory, which contains libraries that should be autoloaded. If you want to support scripting (which is not covered in this chapter, but is in the Apache SOAP examples), you'll need to put bsf.jar (available online at http://oss.software.ibm.com/developerworks/projects/bsf) and js.jar (available from http://www.mozilla.org/rhino/) in the same directory.

NOTE: If you are using Xerces with Tomcat, you'll need to perform the same renaming trick I talked about in Chapter 10, "Web Publishing Frameworks". Rename parser.jar to z_parser.jarand jaxp.jar to z_jaxp.jar, to ensure that xerces.jar and the included version of JAXP are loaded prior to any other parser or JAXP implementation.

Now restart your servlet engine, and you're ready to write SOAP server components.

12.2.2.3. The router servlet and admin client

In addition to basic operation, Apache SOAP includes a router servlet as well as an admin client; even if you don't want to use these, I recommend you install them so you can test your SOAP installation. This process is servlet-engine-specific, so I just cover the Tomcat installation here. However, installation instructions for several other servlet engines are available online at http://xml.apache.org/soap/docs/index.html.

Installation under Tomcat is simple; just take the soap.war file in the soap-2_2/webapps directory, and drop it in your $TOMCAT_HOME/webapps directory. That's it! To test the installation, point your web browser to http://localhost:8080/soap/servlet/rpcrouter. You should get a response like that shown in Figure 12-2.

Figure 12-2. The RPC router servlet

Although this looks like an error, it does indicate that things are working correctly. You should get the same response pointing your browser to the admin client, at http://localhost:8080/soap/servlet/messagerouter.

As a final test of both the server and client, ensure you have followed all the setup instructions so far. Then execute the following Java class as shown, supplying your servlet URL for the RPC router servlet:

C:\>java org.apache.soap.server.ServiceManagerClient
         http://localhost:8080/soap/servlet/rpcrouter list
Deployed Services:

You should get the empty list of services, as shown here. If you get any other message, consult the long list of possible errors at http://xml.apache.org/soap/docs/trouble/index.html. A fairly complete list of problems that you can run into is there. If you do get the empty list of services, then you're set up and ready to continue with the examples in the rest of this chapter.

Previous | Home | Next
12. SOAP | Book Index | 12.3. Getting Dirty
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Getting Dirty (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

12.3. Getting Dirty

There are three basic steps in writing any SOAP-based system, and I'll look at each in turn:

  • Decide on SOAP-RPC or SOAP messaging

  • Write or obtain access to a SOAP service

  • Write or obtain access to a SOAP client

The first step is to decide if you want to use SOAP for RPC-style calls, in which a remote procedure is invoked on a server, or for messaging, in which a client simply sends pieces of information to a server. I'll detail these processes in the next section. Once you've made that design decision, you need to access, or code up, a service. Of course, since we're all Java pros here, this chapter shows you how to code up your own. Finally, you need to write the client for this service, and watch things take off.

12.3.1. RPC or Messaging?

Your first task is actually not code-related but design-related. You need to determine if you want an RPC service or a messaging one. The first, RPC, is something you should be pretty familiar with after the last chapter. A client invokes a remote procedure on a server somewhere, and then gets some sort of response. In this scenario, SOAP is simply acting as a more extensible XML-RPC system, allowing better error handling and passing of complex types across the network. This is a concept you should already understand, and because it turns out that RPC systems are simple to write in SOAP, I'll start off there. This chapter describes how to write an RPC service, and then an RPC client, and put the system in action.

The second style of SOAP processing is message-based. Instead of invoking remote procedures, it provides for transfer of information. As you can imagine, this is pretty powerful, and doesn't depend on a client knowing about a particular method on some server. It also models distributed systems more closely, allowing packets of data (packet in the figurative sense, not in the network sense) to be passed around, keeping various systems aware of what other systems are doing. It is also more complicated than the simpler RPC-style programming, so I'll cover it in the next chapter with other business-to-business details after you're well grounded in SOAP-RPC programming.

Like most design issues, the actual process of making this decision is left up to you. Look at your application and determine exactly what you want SOAP to do for you. If you have a server and a set of clients that just need to perform tasks remotely, then RPC is probably well suited for your needs. However, in larger systems that are exchanging data rather than performing specific business functions on request, SOAP's messaging capabilities may be a better match.

12.3.2. An RPC Service

With the formalities out of the way, it's time to get going, fast and furious. As you'll recall from the last chapter, in RPC you need a class that is going to have its methods invoked remotely.

12.3.2.1. Code artifacts

I'll start by showing you some code artifacts to have available on the server. These artifacts are classes with methods that are exposed to RPC clients.[21] Rather than use the simple class from last chapter, I offer a slightly more complex example to show you what SOAP can do. In that vein, Example 12-4 is a class that stores a CD inventory, such as an application for an online music store might use. I'm introducing a basic version here, and will add to it later in the chapter.

[21]You can use scripts through the Bean Scripting Framework, but for the sake of space I won't cover that here. Check out the upcoming O'Reilly SOAP book, as well as the online documentation at http://xml.apache.org/soap, for more details on script support in SOAP.

Example 12-4. The CDCatalog class

package javaxml2;

import java.util.Hashtable;

public class CDCatalog {

    /** The CDs, by title */
    private Hashtable catalog;

    public CDCatalog( ) {
        catalog = new Hashtable( );

        // Seed the catalog
        catalog.put("Nickel Creek", "Nickel Creek");
        catalog.put("Let it Fall", "Sean Watkins");
        catalog.put("Aerial Boundaries", "Michael Hedges");
        catalog.put("Taproot", "Michael Hedges");
    }

    public void addCD(String title, String artist) {
        if ((title == null) || (artist == null)) {
            throw new IllegalArgumentException("Title and artist cannot be null.");
        }
        catalog.put(title, artist);
    }

    public String getArtist(String title) {
        if (title == null) {
            throw new IllegalArgumentException("Title cannot be null.");
        }

        // Return the requested CD
        return (String)catalog.get(title);
    }

    public Hashtable list( ) {
        return catalog;
    }
}

This allows for adding a new CD, searching for an artist by a CD title, and getting all current CDs. Take note that the list( ) method returns a Hashtable, and there is nothing special I have to do to make that work; Apache SOAP provides automatic mapping of the Hashtable Java type, much as XML-RPC did.

Compile this class, and make sure you've got everything typed in (or downloaded, if you choose) correctly. Notice that the CDCatalog class has no knowledge about SOAP. This means you can take your existing Java classes and expose them through SOAP-RPC, which reduces the work required on your end to move to a SOAP-based architecture if needed.

12.3.2.2. Deployment descriptors

With the Java coding done, you now need to define a deployment descriptor. This specifies several key things to a SOAP server:

  • The URN of the SOAP service for clients to access

  • The method or methods available to clients

  • The serialization and deserialization handlers for any custom classes

The first is similar to a URL, and required for a client to connect to any SOAP server. The second is exactly what you expect: a list of methods letting the client know what are allowable artifacts for a SOAP client. It also lets the SOAP server, which I'll cover in a moment, know what requests to accept. The third is a means of telling the SOAP server how to handle any custom parameters; I'll come back to this in the next section when I add some more complex behavior to the catalog.

I'll show you the deployment descriptor and detail each item within it. Example 12-5 is the deployment descriptor for the CDCatalog service we're creating.

Example 12-5. The CDCatalog deployment descriptor

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
             id="urn:cd-catalog"
>
  <isd:provider type="java"
                scope="Application"
                methods="addCD getArtist list"
  >
    <isd:java class="javaxml2.CDCatalog" static="false" />
  </isd:provider>

  <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>
</isd:service>

First, I referenced the Apache SOAP deployment namespace, and then supplied a URN for my service through the id attribute. This should be something unique across services, and descriptive of the service. I showed about as much originality in naming the service as Dave Matthews did with his band, but it gets the job done. Then, I specified through the java element the class to expose, including its package name (through the class attribute), and indicated that the methods being exposed were not static ones (through the static attribute).

Next, I specified a fault listener implementation to use. Apache's SOAP implementation provides two; I used the first, DOMFaultListener. This listener returns any exception and fault information through an additional DOM element in the response to the client. I'll get back to this when I look at writing clients, so don't worry too much about it right now. The other fault listener implementation is org.apache.soap.server.ExceptionFaultListener. This listener exposes any faults through an additional parameter returned to the client. Since quite a few SOAP-based applications are already going to be working in Java and XML APIs like DOM, it's common to use the DOMFaultListener in most cases.

12.3.2.3. Deploying the service

At this point, you've got a working deployment descriptor and a set of code artifacts to expose, and you can deploy your service. Apache SOAP comes with a utility to do this task, provided you have done the setup work. First, you need a deployment descriptor for your service, which I just talked about. Second, you need to make the classes for your service available to the SOAP server. The best way to do this is to jar up the service class from the last section:

jar cvf javaxml2.jar javaxml2/CDCatalog.class

Take this jar file and drop it into your lib/ directory (or wherever libraries are auto-loaded for your servlet engine), and restart your servlet engine.

WARNING: When you do this, you have created a snapshot of your class file. Changing the code in the CDCatalog.java file and recompiling it will not cause the servlet engine to pick up the changes. You'll need to re-jar the archive and copy it over to your lib/ directory each time code changes are made to ensure your service is updated. You'll also want to restart your servlet engine to make sure the changes are picked up by the engine as well.

With your service class (or classes) accessible by your SOAP server, you can now deploy the service, using Apache SOAP's org.apache.soap.server.ServiceManager utility class:

C:\javaxml2\Ch12>java org.apache.soap.server.ServiceManagerClient
    http://localhost:8080/soap/servlet/rpcrouter deploy xml\CDCatalogDD.xml

The first argument is the SOAP server and RPC router servlet, the second is the action to take, and the third is the relevant deployment descriptor. Once this has executed, verify your service was added:

(gandalf)/javaxml2/Ch12$ java org.apache.soap.server.ServiceManagerClient
    http://localhost:8080/soap/servlet/rpcrouter list
Deployed Services:
        urn:cd-catalog
        urn:AddressFetcher
        urn:xml-soap-demo-calculator

At a minimum, this should show any and all services you have available on the server. Finally, you can easily undeploy the service, as long as you know its name:

C:\javaxml2\Ch12>java org.apache.soap.server.ServiceManagerClient
    http://localhost:8080/soap/servlet/rpcrouter undeploy urn:cd-catalog

Every time you update your service code, you must undeploy and then redeploy to ensure the SOAP server is running the newest copy.

12.3.3. An RPC Client

Next up is the client. I'm going to keep things simple, and just write a couple of command-line programs that invoke SOAP-RPC. It would be impossible to try and guess your business case, so I just focus on the SOAP details and let you work out integration with your existing software. Once you have the business portion of your code working, there are some basic steps you'll take in every SOAP-RPC call:

  • Create the SOAP-RPC call

  • Set up any type mappings for custom parameters

  • Set the URI of the SOAP service to use

  • Specify the method to invoke

  • Specify the encoding to use

  • Add any parameters to the call

  • Connect to the SOAP service

  • Receive and interpret a response

That may seem like a lot, but most of the operations are one- or two-line method invocations. In other words, talking to a SOAP service is generally a piece of cake. Example 12-6 shows the code for the CDAdder class, which allows you to add a new CD to the catalog. Take a look at the code, and then I'll walk you through the juicy bits.

Example 12-6. The CDAdder class

package javaxml2;

import java.net.URL;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;

public class CDAdder {

    public void add(URL url, String title, String artist)
        throws SOAPException {

        System.out.println("Adding CD titled '" + title + "' by '" +
            artist + "'");

        // Build the Call object
        Call call = new Call( );
        call.setTargetObjectURI("urn:cd-catalog");
        call.setMethodName("addCD");
        call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);

        // Set up parameters
        Vector params = new Vector( );
        params.addElement(new Parameter("title", String.class, title, null));
        params.addElement(new Parameter("artist", String.class, artist, null));
        call.setParams(params);

        // Invoke the call
        Response response;
        response = call.invoke(url, "");

        if (!response.generatedFault( )) {
            System.out.println("Successful CD Addition.");
        } else {
            Fault fault = response.getFault( );
            System.out.println("Error encountered: " + fault.getFaultString( ));
        }
    }

    public static void main(String[] args) {
        if (args.length != 3) {
            System.out.println("Usage: java javaxml2.CDAdder [SOAP server URL] " +
                "\"[CD Title]\" \"[Artist Name]\"");
            return;
        }

        try {
            // URL for SOAP server to connect to
            URL url = new URL(args[0]);

            // Get values for new CD
            String title = args[1];
            String artist = args[2];

            // Add the CD
            CDAdder adder = new CDAdder( );
            adder.add(url, title, artist);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

This program captures the URL of the SOAP server to connect to, as well as information needed to create and add a new CD to the catalog. Then, in the add( ) method, the code creates the SOAP Call object, on which all the interesting interaction occurs. The target URI of the SOAP service and the method to invoke are set on the call, and both match up to values from the service's deployment descriptor from Example 12-5. Next, the encoding is set, which should always be the constant Constants.NS_URI_SOAP_ENC unless you have very unique encoding needs.

The program creates a new Vector populated with SOAP Parameter objects. Each of these represents a parameter to the specified method, and since the addCD( ) method takes two String values, this is pretty simple. Supply the name of the parameter (for use in the XML and debugging), the class for the parameter, and the value. The fourth argument is an optional encoding, if a single parameter needs a special encoding. For no special treatment, the value null suffices. The resulting Vector is then added to the Call object.

Once your call is set up, use the invoke( ) method on that object. The return value from this method is an org.apache.soap.Response instance, which is queried for any problems that resulted. This is fairly self-explanatory, so I'll leave it to you to walk through the code. Once you've compiled your client and followed the instructions earlier in this chapter for setting up your classpath, run the example as follows:

C:\javaxml2\build>java javaxml2.CDAdder
   http://localhost:8080/soap/servlet/rpcrouter
   "Riding the Midnight Train" "Doc Watson"

Adding CD titled 'Riding the Midnight Train' by 'Doc Watson'
Successful CD Addition

Example 12-7 is another simple class, CDLister , which lists all current CDs in the catalog. I won't go into detail on it, as it's very similar to Example 12-6, and is mainly a reinforcement of what I've already talked about.

Example 12-7. The CDLister class

package javaxml2;

import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;

public class CDLister {

    public void list(URL url) throws SOAPException {
        System.out.println("Listing current CD catalog.");

        // Build the Call object
        Call call = new Call( );
        call.setTargetObjectURI("urn:cd-catalog");
        call.setMethodName("list");
        call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);

        // No parameters needed

        // Invoke the call
        Response response;
        response = call.invoke(url, "");

        if (!response.generatedFault( )) {
            Parameter returnValue = response.getReturnValue( );
            Hashtable catalog = (Hashtable)returnValue.getValue( );
            Enumeration e = catalog.keys( );
            while (e.hasMoreElements( )) {
                String title = (String)e.nextElement( );
                String artist = (String)catalog.get(title);
                System.out.println("  '" + title + "' by " + artist);
            }
        } else {
            Fault fault = response.getFault( );
            System.out.println("Error encountered: " + fault.getFaultString( ));
        }
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java javaxml2.CDAdder [SOAP server URL]");
            return;
        }

        try {
            // URL for SOAP server to connect to
            URL url = new URL(args[0]);

            // List the current CDs
            CDLister lister = new CDLister( );
            lister.list(url);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

The only difference in this method from the CDAdder class is that the Response object has a return value (the Hashtable from the list( ) method). This is returned as a Parameter object, which allows a client to check its encoding and then extract the actual method return value. Once that's done, the client can use the returned value like any other Java object, and in the example simply runs through the CD catalog and prints out each one. You can now run this additional client to see it in action:

C:\javaxml2\build>java javaxml2.CDLister
    http://localhost:8080/soap/servlet/rpcrouter
Listing current CD catalog.
  'Riding the Midnight Train' by Doc Watson
  'Taproot' by Michael Hedges
  'Nickel Creek' by Nickel Creek
  'Let it Fall' by Sean Watkins
  'Aerial Boundaries' by Michael Hedges

That's really all there is to basic RPC functionality in SOAP. I'd like to push on a bit, though, and talk about a few more complex topics.

Previous | Home | Next
12.2. Setting Up | Book Index | 12.4. Going Further
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Going Further (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

12.4. Going Further

Although you can now do everything in SOAP you knew how to do in XML-RPC, there is a lot more to SOAP. As I said in the beginning of the chapter, two important things that SOAP brings to the table are the ability to use custom parameters with a minimal amount of effort, and more advanced fault handling. In this section, I cover both of these topics.

12.4.1. Custom Parameter Types

The most limiting thing with the CD catalog, at least at this point, is that it stores only the title and artist for a given CD. It is much more realistic to have an object (or set of objects) that represents a CD with the title, artist, label, track listings, perhaps a genre, and all sorts of other information. I'm not going to build this entire structure, but will move from a title and artist to a CD object with a title, artist, and label. This object needs to be passed from the client to the server and back, and demonstrates how SOAP can handle these custom types. Example 12-8 shows this new class.

Example 12-8. The CD class

package javaxml2;

public class CD {

    /** The title of the CD */
    private String title;

    /** The artist performing on the CD */
    private String artist;

    /** The label of the CD */
    private String label;

    public CD( ) {
        // Default constructor
    }

    public CD(String title, String artist, String label) {
        this.title = title;
        this.artist = artist;
        this.label = label;
    }

    public String getTitle( ) {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist( ) {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public String getLabel( ) {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public String toString( ) {
        return "'" + title + "' by " + artist + ", on " +
            label;
    }
}

This requires a whole slew of changes to the CDCatalog class as well. Example 12-9 shows a modified version of this class with the changes that use the new CD support class highlighted.

Example 12-9. An updated CDCatalog class

package javaxml2;

import java.util.Hashtable;

public class CDCatalog {

    /** The CDs, by title */
    private Hashtable catalog;

    public CDCatalog( ) {
        catalog = new Hashtable( );

        // Seed the catalog
        addCD(new CD("Nickel Creek", "Nickel Creek", "Sugar Hill"));
        addCD(new CD("Let it Fall", "Sean Watkins", "Sugar Hill"));
        addCD(new CD("Aerial Boundaries", "Michael Hedges", "Windham Hill"));
        addCD(new CD("Taproot", "Michael Hedges", "Windham Hill"));
    }

    public void addCD(CD cd) {
        if (cd == null) {
            throw new IllegalArgumentException("The CD object cannot be null.");
        }
        catalog.put(cd.getTitle( ), cd);
    }

    public CD getCD(String title) {
        if (title == null) {
            throw new IllegalArgumentException("Title cannot be null.");
        }

        // Return the requested CD
        return (CD)catalog.get(title);
    }

    public Hashtable list( ) {
        return catalog;
    }
}

In addition to the obvious changes, I've also updated the old getArtist(String title) method to getCD(String title), and made the return value a CD object. This means the SOAP server will need to serialize and deserialize this new class, and the client will be updated. First, I look at an updated deployment descriptor that details the serialization issues related to this custom type. Add the following lines to the deployment descriptor for the CD catalog, as well as changing the available method names to match the updated CDCatalog class:

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
             id="urn:cd-catalog"
>
  <isd:provider type="java"
                scope="Application"
                methods="addCD getCD list"
  >
    <isd:java class="javaxml2.CDCatalog" static="false" />
  </isd:provider>

  <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>

  <isd:mappings>
    <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:x="urn:cd-catalog-demo" qname="x:cd"
             javaType="javaxml2.CD"
             java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
             xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
  </isd:mappings>
</isd:service>

The new element, mappings, specifies how a SOAP server should handle custom parameters such as the CD class. First, define a map element for each custom parameter type. For the encodingStyle attribute, at least as of Apache SOAP 2.2, you should always supply the value http://schemas.xmlsoap.org/soap/encoding/, the only encoding currently supported. You need to supply a namespace for the custom type and then the name of the class, with this namespace prefix, for the type. In my case, I used a "dummy" namespace and the simple prefix "x" for this purpose. Then, using the javaType attribute, supply the actual Java class name: javaxml2.CD in this case. Finally, the magic occurs in the java2XMLClassName and xml2JavaClassName attributes. These specify a class to convert from Java to XML and from XML to Java, respectively. I've used the incredibly handy BeanSerializer class, also provided with Apache SOAP. If your custom parameter is in a JavaBean format, this serializer and deserializer will save you from having to write your own. You need to have a class with a default constructor (remember that I defined an empty, no-args constructor within the CD class), and expose all the data in that class through setXXX and getXXX style methods. Since the CD class fits the bill here, the BeanSerializer works perfectly.

NOTE: It's no accident that the CD class follows the JavaBean conventions. Most data classes fit easily into this format, and I knew I wanted to avoid writing my own custom serializer and deserializer. These are a pain to write (not overly difficult, but easy to mess up), and I recommend you go to great lengths to try and use the Bean conventions in your own custom parameters. In many cases, the Bean conventions only require that a default constructor (with no arguments) is present in your class.

Now recreate your service jar file. Then, redeploy your service:

(gandalf)/javaxml2/Ch12$ java org.apache.soap.server.ServiceManagerClient
    http://localhost:8080/soap/servlet/rpcrouter xml/CDCatalogDD.xml
WARNING: If you have kept your servlet engine running and the service deployed all this time, you'll need to restart the servlet engine to activate the new classes for the SOAP service, and redeploy the service.

At this point, all that's left is modifying the client to use the new class and methods. Example 12-10 is an updated version of the client class CDAdder . The changes from the previous version of the class are highlighted.

Example 12-10. The updated CDAdder class

package javaxml2;

import java.net.URL;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.util.xml.QName;

public class CDAdder {

    public void add(URL url, String title, String artist, String label)
        throws SOAPException {

        System.out.println("Adding CD titled '" + title + "' by '" +
            artist + "', on the label " + label);

        CD cd = new CD(title, artist, label);

        // Map this type so SOAP can use it
        SOAPMappingRegistry registry = new SOAPMappingRegistry( );
        BeanSerializer serializer = new BeanSerializer( );
        registry.mapTypes(Constants.NS_URI_SOAP_ENC,
            new QName("urn:cd-catalog-demo", "cd"),
            CD.class, serializer, serializer);

        // Build the Call object
        Call call = new Call( );
        call.setSOAPMappingRegistry(registry);
        call.setTargetObjectURI("urn:cd-catalog");
        call.setMethodName("addCD");
        call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);

        // Set up parameters
        Vector params = new Vector( );
        params.addElement(new Parameter("cd", CD.class, cd, null));
        call.setParams(params);

        // Invoke the call
        Response response;
        response = call.invoke(url, "");

        if (!response.generatedFault( )) {
            System.out.println("Successful CD Addition.");
        } else {
            Fault fault = response.getFault( );
            System.out.println("Error encountered: " + fault.getFaultString( ));
        }
    }

    public static void main(String[] args) {
        if (args.length != 4) {
            System.out.println("Usage: java javaxml2.CDAdder [SOAP server URL] " +
                "\"[CD Title]\" \"[Artist Name]\" \"[CD Label]\"");
            return;
        }

        try {
            // URL for SOAP server to connect to
            URL url = new URL(args[0]);

            // Get values for new CD
            String title = args[1];
            String artist = args[2];
            String label = args[3];

            // Add the CD
            CDAdder adder = new CDAdder( );
            adder.add(url, title, artist, label);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

The only really interesting change is in dealing with the mapping of the CD class:

        // Map this type so SOAP can use it
        SOAPMappingRegistry registry = new SOAPMappingRegistry( );
        BeanSerializer serializer = new BeanSerializer( );
        registry.mapTypes(Constants.NS_URI_SOAP_ENC,
            new QName("urn:cd-catalog-demo", "cd"),
            CD.class, serializer, serializer);

This is how a custom parameter can be encoded and sent across the wire. I already discussed how the BeanSerializer class could be used to handle parameters in the JavaBean format, such as the CD class. To specify that to the server, I used the deployment descriptor; however, now I need to let the client know to use this serializer and deserializer. This is what the SOAPMappingRegistry class allows. The mapTypes( ) method takes in an encoding string (again, using the constant NS_URI_SOAP_ENC is the best idea here), and information about the parameter type a special serialization should be used for. First, a QName is supplied. This is why the odd namespacing was used back in the deployment descriptor; you need to specify the same URN here, as well as the local name of the element (in this case "CD"), then the Java Class object of the class to be serialized (CD.class), and finally the class instance for serialization and deserialization. In the case of the BeanSerializer, the same instance works for both. Once all this is set up in the registry, let the Call object know about it through the setSOAPMapping-Registry( ) method.

You can run this class just as before, adding the CD label, and things should work smoothly:

C:\javaxml2\build>java javaxml2.CDAdder
    http://localhost:8080/soap/servlet/rpcrouter
    "Tony Rice" "Manzanita" "Sugar Hill"
Adding CD titled 'Tony Rice' by 'Manzanita', on the label Sugar Hill
Successful CD Addition.

I'll leave it up to you to modify the CDLister class in the same fashion, and the downloadable samples have this updated class as well.

NOTE: You might think that since the CDLister class doesn't deal directly with a CD object (the return value of the list( ) method was a Hashtable), you don't need to make any changes. However, the returned Hashtable contains instances of CD objects. If SOAP doesn't know how to deserialize these, your client is going to give you an error. Therefore, you must specify a SOAPMappingRegistry instance on the Call object to make things work.

12.4.2. Better Error Handling

Now that you're tossing around custom objects, making RPC calls, and generally showing up everyone else in the office, let me talk about a less exciting topic: error handling. In any network transaction, many things can go wrong. The service isn't running, an error occurs on the server, objects can't be found, classes are missing, and a whole lot of other problems can arise. Until now, I just used the fault.getString( ) method to report errors. But this method isn't always very helpful. To see it in action, comment out the following line in the CDCatalog constructor:

    public CDCatalog( ) {
        //catalog = new Hashtable( );

        // Seed the catalog
        addCD(new CD("Nickel Creek", "Nickel Creek", "Sugar Hill"));
        addCD(new CD("Let it Fall", "Sean Watkins", "Sugar Hill"));
        addCD(new CD("Aerial Boundaries", "Michael Hedges", "Windham Hill"));
        addCD(new CD("Taproot", "Michael Hedges", "Windham Hill"));
    }

Recompile, restart your server engine, and redeploy. The result is that a NullPointerException occurs when the class constructor tries to add a CD to an uninitialized Hashtable. Running the client will let you know an error has occurred, but not in a very meaningful way:

(gandalf)/javaxml2/build$ java javaxml2.CDLister
    http://localhost:8080/soap/servlet/rpcrouter
Listing current CD catalog.
Error encountered: Unable to resolve target object: null

This isn't exactly the type of information you need to track down the problem. However, the framework is in place to do a better job of error handling; remember the DOMFaultListener you specified as the value of the faultListener element? This is where it comes into play. The returned Fault object in the case of a problem (as in this one) contains a DOM org.w3c.dom.Element with detailed error information. First, add an import statement for java.util.Iterator to your client source code:

import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.util.xml.QName;

Next, make the following change to how errors are handled in the list( ) method:

        if (!response.generatedFault( )) {
            Parameter returnValue = response.getReturnValue( );
            Hashtable catalog = (Hashtable)returnValue.getValue( );
            Enumeration e = catalog.keys( );
            while (e.hasMoreElements( )) {
                String title = (String)e.nextElement( );
                CD cd = (CD)catalog.get(title);
                System.out.println("  '" + cd.getTitle( ) + "' by " +
cd.getArtist( ) +
                    " on the label " + cd.getLabel( ));
            }
        } else {
            Fault fault = response.getFault( );
            System.out.println("Error encountered: " + fault.getFaultString( ));

            Vector entries = fault.getDetailEntries( );
            for (Iterator i = entries.iterator(); i.hasNext( ); ) {
                org.w3c.dom.Element entry = (org.w3c.dom.Element)i.next( );
                System.out.println(entry.getFirstChild().getNodeValue( ));
            }
        }

By using the getDetailEntries( ) method, you get access to the raw data supplied by the SOAP service and server about the problem. The code iterates through these (there is generally only a single element, but it pays to be careful) and grabs the DOM Element contained within each entry. Essentially, here's the XML you are working through:

<SOAP-ENV:Fault>
  <faultcode>SOAP-ENV:Server.BadTargetObjectURI</faultcode>
  <faultstring>Unable to resolve target object: null</faultstring>
  <stacktrace>Here's what we want!</stackTrace>
</SOAP-ENV:Fault>

In other words, the Fault object gives you access to the portion of the SOAP envelope that deals with errors. Additionally, Apache SOAP provides a Java stack trace if errors occur, and that provides the detailed information needed to troubleshoot problems. By grabbing the stackTrace element and printing the Text node's value from that Element, your client will now print out the stack trace from the server. Compile these changes and rerun the client. You should get the following output:

C:\javaxml2\build>java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcr
outer
Listing current CD catalog.
Error encountered: Unable to resolve target object: null
java.lang.NullPointerException
        at javaxml2.CDCatalog.addCD(CDCatalog.java:24)
        at javaxml2.CDCatalog.<init>(CDCatalog.java:14)
        at java.lang.Class.newInstance0(Native Method)
        at java.lang.Class.newInstance(Class.java:237)

This goes on for a bit, but you can see the juicy bits of information indicating that a NullPointerException occurred, and even get the line numbers on the server classes where the problems happened. The result of this fairly minor change is a much more robust means of handling errors. That should prepare you for tracking down bugs on your server classes. Oh, and be sure to change your CDCatalog class back to a version that won't cause these errors before moving on!

Previous | Home | Next
12.3. Getting Dirty | Book Index | 12.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

12.5. What's Next?

The next chapter is a direct continuation of these topics. More than ever, XML is becoming the cornerstone of business-to-business activity, and SOAP is key to that. In the next chapter, I'll introduce two important technologies to you, UDDI and WSDL. If you have no idea what those are, you're in the right place. You'll learn how they all fit together to form the backbone of web services architectures. Get ready to finally find out what the web services, peer-to-peer craze is all about.

Previous | Home | Next
12.4. Going Further | Book Index | 13. Web Services
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Web Services (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 13. Web Services

Contents:

Web Services
UDDI
WSDL
Putting It All Together
What's Next?

In the last chapter, I focused on SOAP as a standalone technology; your application was a SOAP client talking to a SOAP server, based on nothing but Java and a servlet engine. While this is a perfectly good solution for applications in which you write all the clients and all the services, it is fairly limited for interoperating with other applications. If you've heard anything about SOAP, it's the interoperability aspect that has gotten it such good press lately. The last chapter was incomplete; it did not teach you how to do true servicing of other applications using SOAP as a transport mechanism. This chapter fills in the rest of the picture, and solves the remaining problems in interoperability.

To get you started, I apply some simple definitions to the overused term "web services." Although it's hard to quantify this term right now, since everyone is using it for their own particular flavor of software and architecture, some general principles apply. In almost every definition of web services, you'll find that there is a need for an exchange of information with other applications. This exchange needs a set of standards, and the two that are the most important (at least right now) are UDDI and WSDL. I cover both and show you how they fit in with SOAP. Finally, I'll try and pull all these various acronyms and technologies together into one concrete example to finish off the chapter.

13.1. Web Services

Web services seems to have become the next big thing in computing these days, so I've made room for this chapter in the second edition of this book. However, trying to define the term is somewhat tricky; from one person's perspective, it may be correct, but from everyone else's it's probably completely off-base. I'm left with trying to boil down the hype and differences in implementation to a simple set of concepts that remain true across the board.

Web services is about interoperability. More and more, applications must communicate with each other. But the problems in communication grow, instead of shrink, with every new day of technology innovation. As more languages, increasingly complex data structures, and varied business needs develop, the difference between one system and another (even if they are performing the same business task!) also grows. For systems to interoperate, there must be a common language. This isn't a language in the sense that Java is a language; programs not written in Java must have just as much access as those that are in Java. Instead, it's a language that anyone with a dictionary can access; even if the words aren't the same, applications have access to the words, which can be translated into something that can be understood.

XML solves the problem of data transfer, and is one piece of that language. It is accepted and usable in almost every programming language around: C, Perl, Python, LISP, Java, Pascal, C#, Visual Basic. . . the list goes on and on. However, web service frameworks seek to go quite a bit further. The key to interoperability is not just data, but what data. What information can I get from your system? What can you find out from mine? In other words, there must be a means of broadcasting what services an application makes available. And this is where we have, until recently, had a gaping hole.

However, recent additions to the web services realm have begun to fill this hole. First, UDDI provides a means of discovering other services and registering your services for others to discover. Second, WSDL offers a way to provide the needed information about a discovered service to allow a client to interact with it. I've purposely left out the explanation of what UDDI and WSDL stand for and mean for now, because I want to focus on the big picture.

Figure 13-1 shows how the process flows. To begin, a service provider codes up a service (like you did in the last chapter). This service could be as simple as adding CDs to a catalog, or as complex as storing the VIN (vehicle identification numbers) for all automobiles registered in Massachusetts that are used by the government. As long as the service is web-accessible, it's a candidate for a service registry. This is all part of the UDDI portion of the picture. UDDI also allows a user to search all registered services for a specific service name, like "cd-catalog" or "govt-bin-numbers." The registry returns all the matching services.

Figure 13-1. The web services process

Hopefully, the client has found the desired service. But interaction takes more than just a name; it requires the URL to connect to and the parameters required, as well as the return value. This is accomplished through XML in a WSDL format, which I cover as well. And then, your client interacts with the located service, knowing (because of WSDL) you are using the service correctly. Life is grand, and all because of web services. Of course, I've skimmed over all the hard parts, but I'll delve into some of that complexity now that you have the big picture.

Previous | Home | Next
12.5. What's Next? | Book Index | 13.2. UDDI
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


UDDI (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

13.2. UDDI

With no further suspense, I want to define what UDDI is. It stands for Universal Discovery, Description, and Integration, and is most often associated with the word "registry." The primary place to learn about UDDI is its web site, http://www.uddi.org (shown in Figure 13-2), also the home of the UDDI registry that is so important in registering and locating services. This site describes the UDDI project, which seeks to create and define a complete framework for the exchange of data I've been talking about in this chapter. The initiative is supported by both IBM and Microsoft, so it looks like it's around for the long haul.

Figure 13-2. The UDDI web site

The core of this is the network of services about which UDDI stores information. The ability to register a new service and search for an existing one is part and parcel of this registry, and is available online at the UDDI web site. All that is required is a simple registration process. Neither IBM nor Microsoft require a company or even an individual to pay high fees to make their services publicly available. Because of this, there are many, many more services being registered every day than a "for-profit" system would engender.

That's all that there really is to UDDI. There is little complexity in the use of UDDI; the hard stuff is all in implementation, which you and I aren't really interested as a service provider or consumer. There are several UDDI implementations available that can be downloaded and run locally, and I prefer jUDDI over the others. You can check out this Java-based open source project at http://www.juddi.org. Additionally, IBM's web services toolkit (covered in Section 13.3, "WSDL" later in this chapter) includes a trial version of a private UDDI registry. I'm not going to cover jUDDI or the IBM UDDI registry here, because they don't aid in understanding how to use UDDI, but rather how to implement it. If you have an interest in seeing what drives a UDDI registry, I'd recommend you check out jUDDI. If you are just interested in writing web services and making them available to others, though, I wouldn't worry too much about it. Finally, I leave out the specifics of registering and searching for services until the final section, in which I walk you through a fairly complex real-world example using SOAP, UDDI, and WSDL.

Previous | Home | Next
13. Web Services | Book Index | 13.3. WSDL
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


WSDL (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

13.3. WSDL

WSDL is the Web Services Description Language. The entire specification is online at http://www.w3.org/TR/wsdl, and describes everything you need to know about a service in order to interact with it. Like UDDI, it's a fairly simple piece of technology on its own (really, it's not even technology; it's just markup), but becomes extremely important in the overall web services picture. The WSDL file describes several critical pieces of information a service client would need:

  • The name of the service, including its URN

  • The location the service can be accessed at (usually an HTTP URL address)

  • The methods available for invocation

  • The input and output parameter types for each method

Each of these pieces of data on their own are fairly useless, but together, they represent the complete client picture of the service. Additionally, a WSDL document incorporates elements of XML Schema, XML-RPC-style parameters, and quite a bit of everything else you've read about so far. Example 13-1 is a portion of a WSDL schema for the CD catalog from the last chapter; it only describes the getCD( ) method of the service. It's not complete, but it should give you an idea of what a WSDL document looks like.

Example 13-1. Portion of a WSDL document

<?xml version="1.0"?>

<definitions name="CDCatalog"
             targetNamespace="http://www.oreilly.com/javaxml2/cd-catalog.wsdl"
             xmlns:cd="http://www.oreilly.com/javaxml2/cd-catalog.wsdl"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:cdXSD="http://www.oreilly.com/javaxml2/cd-catalog.xsd"
             xmlns="http://schemas.xmlsoap.org/wsdl/"
>
  <types>
    <schema targetNamespace="http://www.oreilly.com/javaxml2/cd-catalog.xsd"
            xmlns="http://www.w3.org/2000/10/XMLSchema">
      <element name="Title">
        <complexType>
          <all><element name="title" type="string" /></all>
        </complexType>
      </element>
      <element name="CD">
        <complexType>
          <all>
            <element name="title" type="string" />
            <element name="artist" type="string" />
            <element name="label" type="string" />
          </all>
        </complexType>
      </element>
    </schema>
  </types>

  <message name="getCDInput">
    <part name="body" element="cdXSD:Title" />
  </message>

  <message name="getCDOutput">
    <part name="body" element="cdXSD:CD" />
  </message>

  <portType name="CDCatalogPortType">
    <operation name="getCD">
      <input message="cd:getCDInput" />
      <output message="cd:getCDOutput" />
    </operation>
  </portType>

  <binding name="CDCatalogBinding" type="cd:CDCatalogPortType">
    <soap:binding style="rpc"
                  transport="http://schemas.xmlsoap.org/soap/http" />
    <operation name="getCD">
      <soap:operation soapAction="urn:cd-catalog" />
      <input>
        <soap:body use="encoded"
            encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
            namespace="urn:cd-catalog" />
      </input>
      <output>
        <soap:body use="encoded"
            encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
            namespace="urn:cd-catalog" />
      </output>
    </operation>
  </binding>

  <service name="CDCatalog">
    <documentation>CD Catalog Service from Java and XML</documentation>
    <port name="CDCatalogPort" binding="cd:CDCatalogBinding">
      <soap:address location="http://newInstance.com/soap/servlet/rpcrouter" />
    </port>
  </service>
</defintions>

As you can see, this is a fairly verbose format for describing a service; however, it's also easy to understand. First, any types that must be passed across the wire are described using the types element, and an XML Schema-style syntax.

WARNING: Currently, WSDL specifications are using the 2000 version of the XML Schema specification, and not the finalized April 2001 XML Schema specification. You'll need to use the slightly older schema constructs until the WSDL specification is brought completely up to date.

Next, the message element is used to define interaction from the client to the server, and the server to the client. These are combined in the portType element to define available operations (you would find additional available methods in this section as well). The binding element details how the operations can be accessed and the URN where they are accessible, and the service element brings all of this together. Thinking about this process as a hierarchy will help keep everything straight in your head.

SOAP's All You Got?

Don't get the idea that a SOAP service is the only type of web service around. It's certainly possible to build a program (or programs) that interact with clients through some other means, and represent that interaction through a WSDL file. For example, an XML-RPC service fits the bill pretty nicely; even though it doesn't have an envelope and custom parameter support, it still can easily interact with clients and represent its input and output parameters in WSDL. However, almost all the services I've seen (and I've seen a lot!) are SOAP, so it is certainly the overriding trend. Still, keep in the back of your mind the ability to use any program as a service, not just SOAP ones.

Currently, the Apache SOAP implementation does not directly use WSDL documents. In other words, you can't consume a WSDL document and automatically get a client class, for example. While some of the other platforms, such as Microsoft, are further along, Apache's Axis project is working on this functionality. For now, you'll need to interpret the WSDL document on your own, and then manually code up a client. That's more fun anyway.

Previous | Home | Next
13.2. UDDI | Book Index | 13.4. Putting It All Together
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Putting It All Together (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

13.4. Putting It All Together

With that fairly basic understanding of WSDL added to the UDDI discussion, you're ready for a complete web services example. In this section, I detail the process of writing a SOAP service (a messaging one, this time), registering it with a UDDI registry, finding it using UDDI, getting the WSDL descriptor, and then interacting with the service via a client.

For the example, I add a little more complexity. Here's the scenario. CDs-R-Us is a new company that wants to provide CDs to distributors around the world. Since they are late to market, they seek to gain business by providing a high-tech interface, using web services to make interaction easy. They are going to provide the ability to send XML messages requesting CDs through SOAP. Their applications will fulfill these orders by looking up the CD on their catalog server (running, of course, a heavy-duty version of the CDCatalog service from last chapter), and then returning an invoice. There are actually two SOAP transactions going on: one from the client to CDs-R-Us, which is messaging-based, and one internal to CDs-R-Us, which is RPC-based. Figure 13-3 shows the complete process flow. They also want to register their messaging service with a UDDI registry so potential clients can find them.

Figure 13-3. Process flow for the example application

13.4.1. A Messaging Service

Since we'll be using CDCatalog from last chapter for the RPC client, I can skip right to the new code, the messaging service. This should receive an XML purchase order and make a request to the catalog service on another machine on CDs-R-Us's local network; in other words, the messaging service is also a SOAP-RPC client. This is perfectly legal in the web services world and quite common. One business receives information from another, and in turn starts an interaction with another business. If this still seems odd, ask your home builder how many subcontractors he employs, and then ask each of them how many subcontractors they employ; it would probably blow your mind!

First, let's define the purchase order (PO) format that CDs-R-Us requires. The XML Schema for the PO document is shown in Example 13-2.

Example 13-2. po.xsd XML Schema

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="http://www.cds-r-us.com"
           targetNamespace="http://www.cds-r-us.com">
  <xs:element name="purchaseOrder">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="recipient" />
        <xs:element ref="order" />
      </xs:sequence>
      <xs:attribute name="orderDate" type="xs:string" />
    </xs:complexType>
  </xs:element>

  <xs:element name="recipient">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name" />
        <xs:element ref="street" />
        <xs:element ref="city" />
        <xs:element ref="state" />
        <xs:element ref="postalCode" />
      </xs:sequence>
      <xs:attribute name="country" type="xs:string" />
    </xs:complexType>
  </xs:element>

  <xs:element name="name" type="xs:string"/>
  <xs:element name="street" type="xs:string" />
  <xs:element name="city" type="xs:string" />
  <xs:element name="state" type="xs:string" />
  <xs:element name="postalCode" type="xs:short" />

  <xs:element name="order">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="cd" maxOccurs="unbounded" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:element name="cd">
    <xs:complexType>
      <xs:attribute name="artist" type="xs:string" />
      <xs:attribute name="title" type="xs:string" />
    </xs:complexType>
  </xs:element>
</xs:schema>

With that schema in place, a typical PO would look something like Example 13-3.

Example 13-3. Example PO for CDs

<purchaseOrder orderDate="07.23.2001"
    xmlns="http://www.cds-r-us.com"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.cds-r-us.com po.xsd"
>
  <recipient country="USA">
    <name>Dennis Scannell</name>
    <street>175 Perry Lea Side Road</street>
    <city>Waterbury</city>
    <state>VT</state>
    <postalCode>05676</postalCode>
  </recipient>
  <order>
    <cd artist="Brooks Williams" title="Little Lion" />
    <cd artist="David Wilcox" title="What You Whispered" />
  </order>
</purchaseOrder>

The service should accept an XML document like Example 13-3, figure out what information is relevant, and then pass that information on to the CD catalog service through RPC. Once it gets a response, it formulates some sort of invoice or acknowledgment for the messaging client, and sends that message back. I keep things simple for the purpose of this example, but you can easily see where to add additional processing as we walk through the code.

Writing a service that accepts XML messages is a bit different from writing one that accepts RPC requests; with messaging, you need to interact more directly with the request and response objects, and your class needs to know about SOAP. Remember that in the RPC-style processing, the class receiving requests didn't know a thing about RPC or SOAP, and was therefore encapsulated fairly well. With a messaging-style service, all methods that can be interacted with must follow this convention:

public void methodName(SOAPEnvelope env, SOAPContext req, SOAPContext res)
    throws java.io.IOException, javax.mail.MessagingException;

This should feel somewhat similar to how servlets work; you get a request and response object to interact with, as well as the actual SOAP envelope for the message sent across the wire. You can see the expected IOException that may be thrown when network and related errors occur; additionally, a MessagingException (from the JavaMail package) can result from problems with the SOAP message envelope. Additionally, the method name must be the same as the name of the root element of the message content! This is easy to forget; in our case, it means that the method receiving XML must be called purchaseOrder, since that is the root element in Example 13-3. With this knowledge, it's possible to set up the skeleton for a message service. This skeleton is shown in Example 13-4; in addition to putting in the framework for receiving a SOAP message, it also has the logic to make the appropriate call to the CDCatalog service on another machine. I've left a comment as a placeholder for the messaging code we'll look at in a moment.

Example 13-4. Skeleton for CDs-R-Us messaging service

package javaxml2;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import javax.mail.MessagingException;

// SOAP imports
import org.apache.soap.Constants;
import org.apache.soap.Envelope;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.rpc.SOAPContext;
import org.apache.soap.util.xml.QName;

public class OrderProcessor {

    /** Mapping for CD class */
    private SOAPMappingRegistry registry;

    /** The serializer for the CD class */
    private BeanSerializer serializer;

    /** The RPC Call object */
    private Call call;

    /** Parameters for call */
    private Vector params;

    /** Response from RPC call */
    private Response rpcResponse;

    /** The URL to connect to */
    private URL rpcServerURL;

    public void initialize( ) {
        // Set up internal URL for SOAP-RPC
        try {
            rpcServerURL =
                new URL("http://localhost:8080/soap/servlet/rpcrouter");
        } catch (MalformedURLException neverHappens) {
            // ignored
        }

        // Set up a SOAP mapping to translate CD objects
        registry = new SOAPMappingRegistry( );
        serializer = new BeanSerializer( );
        registry.mapTypes(Constants.NS_URI_SOAP_ENC,
            new QName("urn:cd-catalog-demo", "cd"),
            CD.class, serializer, serializer);

        // Build a Call to the internal SOAP service
        call = new Call( );
        call.setSOAPMappingRegistry(registry);
        call.setTargetObjectURI("urn:cd-catalog");
        call.setMethodName("getCD");
        call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);

        // Set up input
        params = new Vector( );
    }

    public void purchaseOrder(Envelope env, SOAPContext req, SOAPContext res)
        throws IOException, MessagingException {

        // Set up SOAP environment
        initialize( );

        // Set up list of CDs successfully ordered
        List orderedCDs = new LinkedList( );

        // Set up hashtable of failed orders
        Hashtable failedCDs = new Hashtable( );

        // Parse incoming message and get list of CDS ordered

        // Loop through each ordered CD from the PO request
            String artist = "";
            String title = "";

            // Set up input
            params.clear( );
            params.addElement(new Parameter("title", String.class, title, null));
            call.setParams(params);

            try {
                // Invoke the call
                rpcResponse = call.invoke(rpcServerURL, "");

                if (!rpcResponse.generatedFault( )) {
                    Parameter returnValue = rpcResponse.getReturnValue( );
                    CD cd = (CD)returnValue.getValue( );

                    // See if the CD is available
                    if (cd == null) {
                        failedCDs.put(title, "Requested CD is not available.");
                        continue;
                    }

                    // Verify it's by the right artist
                    if (cd.getArtist( ).equalsIgnoreCase(artist)) {
                        // Add this CD to the successful orders
                        orderedCDs.add(cd);
                    } else {
                        // Add this to the failed orders
                        failedCDs.put(title, "Incorrect artist for specified CD.");
                    }
                } else {
                    Fault fault = rpcResponse.getFault( );
                    failedCDs.put(title, fault.getFaultString( ));
                }
            } catch (SOAPException e) {
                failedCDs.put(title, "SOAP Exception: " + e.getMessage( ));
            }

        // At the end of the loop, return something useful to the client
    }
}
NOTE: In this example and in the rest of the chapter, I use the hostname http://localhost:8080 to represent a SOAP service running on your local machine. Most of you will be testing the example locally, and this will help you avoid putting in fictional hostnames and getting frustrated when things don't work.

In a real environment you would expect the client to connect to a CDs-R-Us machine, like http://www.cds-r-us.com, and the messaging service to connect to an internal machine running the CD catalog, such as http://catalog.cds-r-us.com, perhaps behind an external firewall. Still, I'd rather your code work right away than try and put false hostnames in the example code. That's why everything uses the local machine as the hostname.

I briefly run through what is going on here, and then get to the interesting aspect: the messaging interaction. First, the initialize( ) method is used to set up an RPC call for each client. This Call object is used and reused, so no resources are wasted on a single client. At the same time, each client gets their own Call object, ensuring that synchronization and threading issues don't surface. Next, some storage is set up: a List for successful orders, and a Hashtable for failed ones. The Hashtable has the title of the ordered CD as the key, and error information as the value. Then, the SOAP message from the client would be read (here's where I've left a placeholder, for now). For each CD ordered, a looping process begins. The CD title and artist are extracted from the message, and an RPC call is invoked to obtain the requested CD object. Depending on the result from the request to the CD catalog, the CD is added to the list of successful or failed orders. At the end of the loop, a message would be constructed and sent back to the client.

It's worth noting that the CDCatalog is a simple version, and not complete in this context. A real CD catalog service would probably check for a CD in its inventory, ensure copies are available, remove one CD from the inventory, report its SKU, etc. In this case, all the CD catalog service does is check for the requested CD in its list of available CDs. Still, you get the idea.

Now that this skeleton is in place, you are ready to interact with the user's message. Let's take care of some additional classes that will be used. Add the import statements shown here:

import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import javax.mail.MessagingException;

// DOM
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

// SOAP imports
import org.apache.soap.Constants;
import org.apache.soap.Envelope;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.rpc.SOAPContext;
import org.apache.soap.util.xml.QName;

The code needs to use DOM to work with the XML in the message sent by the client; that message is what I want to look at first. You remember the XML shown in Example 13-3, which is the content of the message that the service expects to receive. However, the message will be wrapped in some SOAP specifics, and ends up looking like Example 13-5 before it's sent. The extra information is used by SOAP to allow interpretation of the message.

Example 13-5. The SOAP-ready document from Example 13-3

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
 <s:Body>
  <purchaseOrder orderDate="07.23.2001"
      xmlns="urn:cd-order-service"
  >
    <recipient country="USA">
      <name>Dennis Scannell</name>
      <street>175 Perry Lea Side Road</street>
      <city>Waterbury</city>
      <state>VT</state>
      <postalCode>05676</postalCode>
    </recipient>
    <order>
      <cd artist="Brooks Williams" title="Little Lion" />
      <cd artist="David Wilcox" title="What You Whispered" />
    </order>
  </purchaseOrder>
 </s:Body>
</s:Envelope>

The actual message is in the body of the SOAP envelope. The corollaries to these structures in Apache SOAP are org.apache.soap.Envelope and org.apache.soap.Body. To get the entries in the body, use envelope.getBody().getBodyEntries( ) , which returns a Vector. The first (and only) item in this Vector in this example turns out to be a DOM Element, which is the Java equivalent for the XML purchaseOrder element. That, of course, is exactly what we want. Once that element is obtained, you can use normal DOM methods to walk the DOM tree and get each ordered CD. Add the following code to your purchaseOrder( ) method, which extracts and iterates through each CD requested by the client:

    public void purchaseOrder(Envelope env, SOAPContext req, SOAPContext res)
        throws IOException, MessagingException {

        // Set up SOAP environment
        initialize( );

        // Set up list of CDs successfully ordered
        List orderedCDs = new LinkedList( );

        // Set up hashtable of failed orders
        Hashtable failedCDs = new Hashtable( );

        // Get the purchaseOrder element - always the first body entry
        Vector bodyEntries = env.getBody().getBodyEntries( );
        Element purchaseOrder = (Element)bodyEntries.iterator().next( );

        // In a real application, do something with the buyer information

        // Get the CDs ordered
        Element order =
            (Element)purchaseOrder.getElementsByTagName("order").item(0);
        NodeList cds = order.getElementsByTagName("cd");

        // Loop through each ordered CD from the PO request
        for (int i=0, len=cds.getLength( ); i<len; i++) {
            Element cdElement = (Element)cds.item(i);
            String artist = cdElement.getAttribute("artist");
            String title = cdElement.getAttribute("title");

            // Set up input for SOAP-RPC call, shown in Example 13-4
            params.clear( );
            params.addElement(new Parameter("title", String.class, title, null));
            call.setParams(params);

            try {
                // Existing RPC code from code, shown in Example 13-4
            } catch (SOAPException e) {
                failedCDs.put(title, "SOAP Exception: " + e.getMessage( ));
            }
        }

        // At the end of the loop, return something useful to the client
    }

Once this code completes, the list of ordered CDs that were successful is in the orderedCDs List, and the failed orders are in the failedCDs Hashtable. Since the client already knows how to "speak" XML (it sent an XML message), it makes sense to send an XML response. Rather than constructing a response from scratch, formatting it for SOAP, and manually responding, it's possible to use the Envelope object the code just read from. Add in the code shown here, which generates a response:

    public void purchaseOrder(Envelope env, SOAPContext req, SOAPContext res)
        throws IOException, MessagingException {

        // Existing code for Messaging parsing, shown above

        // Loop through each ordered CD from the PO request
        for (int i=0, len=cds.getLength( ); i<len; i++) {
            Element cdElement = (Element)cds.item(i);
            String artist = cdElement.getAttribute("artist");
            String title = cdElement.getAttribute("title");

            // Set up input
            params.clear( );
            params.addElement(new Parameter("title", String.class, title, null));
            call.setParams(params);

            try {
                // Existing RPC code from code, shown in Example 13-4
            } catch (SOAPException e) {
                failedCDs.put(title, "SOAP Exception: " + e.getMessage( ));
            }
        }

        // At the end of the loop, return something useful to the client
        Document doc = new org.apache.xerces.dom.DocumentImpl( );
        Element response = doc.createElement("response");
        Element orderedCDsElement = doc.createElement("orderedCDs");
        Element failedCDsElement = doc.createElement("failedCDs");
        response.appendChild(orderedCDsElement);
        response.appendChild(failedCDsElement);

        // Add the ordered CDs
        for (Iterator i = orderedCDs.iterator(); i.hasNext( ); ) {
            CD orderedCD = (CD)i.next( );
            Element cdElement = doc.createElement("orderedCD");
            cdElement.setAttribute("title", orderedCD.getTitle( ));
            cdElement.setAttribute("artist", orderedCD.getArtist( ));
            cdElement.setAttribute("label", orderedCD.getLabel( ));
            orderedCDsElement.appendChild(cdElement);
        }

        // Add the failed CDs
        Enumeration keys = failedCDs.keys( );
        while (keys.hasMoreElements( )) {
            String title = (String)keys.nextElement( );
            String error = (String)failedCDs.get(title);
            Element failedElement = doc.createElement("failedCD");
            failedElement.setAttribute("title", title);
            failedElement.appendChild(doc.createTextNode(error));
            failedCDsElement.appendChild(failedElement);
        }

        // Set this as the content of the envelope
        bodyEntries.clear( );
        bodyEntries.add(response);
        StringWriter writer = new StringWriter( );
        env.marshall(writer, null);

        // Send the envelope back to the client
        res.setRootPart(writer.toString( ), "text/xml");
    }

This builds up a new XML tree with the successful and failed orders within it. It then sets the tree as the content of the envelope's Body, replacing the original request from the client. Next, the envelope has to be converted to a textual format, which can be sent as the response using the SOAPContext res object. SOAP provides a means of doing this, through the marshall( ) method. Supplying the method a StringWriter means that the value "dropped into" that writer can be extracted as a String for use later. The second argument is an instance of org.apache.soap.util.xml.XMLJavaMappingRegistry. An example is the SOAPMappingRegistry class, a subclass of XMLJavaMappingRegistry used earlier and in the last chapter; since no special types need to be mapped, a null argument suffices.

Finally, the result of all this work and serialization is set as the content of the response, through the setRootPart( ) element. The second value of this method is the HTML-style content type. Since the code sends back XML, the correct value is "text/xml". Once the client gets that response, it can figure out the content is in an XML format and decode it. Other than setting this content on the SOAPContext response object, you don't need to do anything else in order to communicate back to the client. Once this method completes, the SOAP server will automatically return that object to the client, along with any information you've put within it. This is also a lot like working with the HttpServletResponse object in servlets, if you are familiar with that methodology.

At this point, you can compile the OrderProcessor class, and deploy it to your SOAP server:

java org.apache.soap.server.ServiceManagerClient
    http://localhost:8080/soap/servlet/rpcrouter deploy xml/OrderProcessorDD.xml

Once this is done, you're ready to register the service with a UDDI registry.

13.4.2. Registering with UDDI

To begin the process of registering your service, make sure it is publicly accessible. You can't register a service that is only available on your local machine (http://localhost:8080 and the like). If you are in the testing or experimentation phase, read through this section and file it away for later use. If you are ready to actually register a service on a network somewhere, be sure you know the hostname of the machine where it can be accessed. Then hop online and visit http://www.uddi.org.

Once you're at the UDDI website, click the "Register" link at the top right of the screen (see Figure 13-2 if you are lost at this point). Then select the node to register your service within. Right now, registering in one makes it accessible in all, so this is a fairly meaningless decision. I chose IBM in my case, and click the "Go" button. At this point, you'll need an account to access the IBM registry. If you don't have one, it's free to sign up and get one; click the "Register" button over on the left, and follow the instructions there. Once you're registered, login through the "Login" link on the left (see Figure 13-4). You also must provide an activation key, supplied via email after registration on your first login.

Figure 13-4. Logging into the IBM UDDI registry

Once you make it through the registration and account activation process, you're ready to publish your service. First select whether you want to add a new business; this is generally a good idea. I've added a new business, shown in Figure 13-5.

Figure 13-5. Adding a new business to the UDDI registry

You can then add services and attach them to your business, which adds an additional layer of organization to your searches. You can optionally add business contacts, locations, and more. Once that's done, you are ready to add your service to the registry.

Select "Add a new service" and enter in the name of your service; in this example, it would be cd-order-service. You'll be given options to enter the description, access point, and service location for the service. I entered "This service allows ordering CDs via a purchase order" as my description. For my access point, I selected "http" and then "newInstance.com/soap/servlet/rpcrouter" for the access point. Do the same for your service, using your own hostname and URL. You can then specify a service locator, which is a formal set of standards for categorizing your service; I'm not going to get into this here, but you can read up on it at the web site. Once you're finished entering information, you should have something that looks similar to Figure 13-6.

Figure 13-6. Results from adding a service

At this point things begin to get a little less efficent. Unfortunately, there is no capability to upload a WSDL document describing your service: that would allow a description, in technical terms, to be made available with your service for those who may want to be clients of the service. The only information allowed is the service name and access point (or points, if it's available at multiple locations). However, this does make it possible for anyone with a UDDI registration and login to search for a service and a related business. So you've registered your service, and it's available for anyone else to search for.

13.4.3. Searching a UDDI Registry

The opposite side of this coin is looking for a service; I'm moving from the service provider realm into that of the client, the service consumer. If you want to use SOAP, WSDL, and the rest, visit the UDDI web site's registry online and login, then search for services (like the one you just registered). This is simple: just click the "Find" link, select the node to search within (again, I chose "IBM"), and enter the name of the service to look for. You'll need to login if you haven't already.

This is another area where web services is still evolving; searches are pretty basic, and can only be done based on a service name. If I name my service "Reading Library Service" and you enter "book" as the search text, you'll never find my service. You would need to enter "reading" or "library" to get my service. Still, it is a good start. Once you've registered your service, you can enter "cd" as your search text. Specify that you want to find services, as opposed to businesses or service types. Then, click the "Find" button on the search screen. You should get results similar to those shown in Figure 13-7 (which includes the CD service I added, and may include other readers' services once this book is out).

Figure 13-7. Search results with search text of "cd"

You can click on the service name and find the access point I entered earlier, as well as any other information I supplied (such as whether I selected a service type or category). Again, you might expect to be able to download WSDL from this service description, but that's not currently an option. At this point, you contact the business which has a service you are interested in, determine available methods to use from them, and set up any cost-based arragements required to use their services.

The UDDI registries still have some work left; however, the infrastructure is in place for a powerful means of registering and searching for web services. Additionally, as WSDL becomes available to upload (for service providers) and download (for service consumers), personal interaction will not be required to use a service. Despite the lack of human contact and immersion in LCD screens, this means more services are used, which in turn causes more services to be developed.

13.4.4. WSDL

Now I spend some time talking about how useful WSDL documents are. I'll tell you how to take one and run a simple tool, like the IBM WSTK mentioned earlier in the chapter, and generate a Java client. I'd like to describe how WSDL is a lifesaver, right now, today! However, that's not yet the case; instead, I'll let you know what is going on with Java and WSDL, so you'll be prepared when all the pieces fall into place.

Expect to see an array of tools beginning to appear that allow you to take a Java service, like the OrderProcessor or CDCatalog classes, and generate WSDL for those services. In fact, some of these tools are already available. IBM's WSTK is one I've already mentioned, and there are other packages from The Mind Electric ( Glue, at http://www.themindelectric.com), Silverstream (http://www.silverstream.com), and SOAPWiz (http://www.bju.edu/cps/faculty/sschaub/soapwiz/). I tried these with varying levels of success. In simple cases like CDCatalog, the tools could usually generate WSDL (although IBM's toolkit choked on the method retuning a Hashtable). This is because the methods expect as input, and return as output, fairly primitive Java types like String and CD, which is made up of primitives.

The problems began when I tried to use these tools on the OrderProcessor class. Since this class is message-based, instead of RPC-based, it has some complex types as input: Envelope and SOAPContext. Because these are complex types, which in turn are made up of complex types, WSDL generators tend to get confused very fast, and generally end up choking and spewing out stack traces. The tools still have some work to do in order to handle message-based SOAP services or extremely complex RPC-based services.

The end result is twofold: first, it should get you excited about the future. As tools emerge that can handle these more complex types, it will be easy to generate WSDL from even complex SOAP services. Then, those same tools can even generate clients that speak to these services. Imagine searching the UDDI registry for a service, downloading its WSDL descriptor, and using a tool to generate a client that speaks to the service. With only a small amount of code modification specific to your business needs, you're working with the new service. The future is bright for web services (as well as for a book on the subject, I imagine!).

The second result is that you are still going to have to pick up a phone and talk to someone about their service, at least for the short term. Once someone tells you the method signatures that you can interact with, or sends it to you in email (we programmer-types tend to be poor communicators in person), you are ready to code up a client, as I now describe.

13.4.5. Writing a Client

Once you find the service you want, a set of methods to use, and the messages you're allowed to send, you're ready to write a client. Example 13-6 shows this client, ready to compile and use.

Example 13-6. The CDOrderer client

package javaxml2;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;

// SAX and DOM imports
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

// SOAP imports
import org.apache.soap.Constants;
import org.apache.soap.Envelope;
import org.apache.soap.SOAPException;
import org.apache.soap.messaging.Message;
import org.apache.soap.transport.SOAPTransport;
import org.apache.soap.util.xml.XMLParserUtils;

public class CDOrderer {

    public void order(URL serviceURL, String msgFilename)
        throws IOException, SAXException, SOAPException {

        // Parse the XML message
        FileReader reader = new FileReader(msgFilename);
        DocumentBuilder builder = XMLParserUtils.getXMLDocBuilder( );
        Document doc = builder.parse(new InputSource(reader));
        if (doc == null) {
            throw new SOAPException(Constants.FAULT_CODE_CLIENT,
                "Error parsing XML message.");
        }

        // Create the message envelope
        Envelope msgEnvelope = Envelope.unmarshall(doc.getDocumentElement( ));

        // Send the message
        Message msg = new Message( );
        msg.send(serviceURL, "urn:cd-order-service", msgEnvelope);

        // Handle the response
        SOAPTransport transport = msg.getSOAPTransport( );
        BufferedReader resReader = transport.receive( );

        String line;
        while ((line = resReader.readLine( )) != null) {
            System.out.println(line);
        }
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java javaxml2.CDOrderer " +
                "[XML Message Filename]");
            return;
        }

        try {
            URL serviceURL =
                new URL("http://localhost:8080/soap/servlet/messagerouter");

            CDOrderer orderer = new CDOrderer( );
            orderer.order(serviceURL, args[0]);
        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

This is a very simple, trivial client. It reads in an XML message supplied on the command line, and converts that to a SOAP Envelope. The org.apache.soap.messaging.Message object is used to encapsulate the envelope and send it to the service URN and hostname specified. The response is obtained through the SOAPTransport from the Message, and I simply echoed the resulting message to the screen. In your own applications, you could write this to another XML file, extract it and use DOM or JDOM to manipulate it, and then continue processing.

Instead of directly instantiating an instance of org.apache.xerces.dom.DocumentImpl, I used the JAXP DocumentBuilder class and SOAP's XMLUtils class to avoid a vendor-specific piece of code. This is a better practice than that shown in the OrderProcessor class, where the Xerces class is directly referenced. I show both just to give you a good idea of the difference; I recommend changing the code in OrderProcessor to mimic the client shown here.

Once you've ensured all the required SOAP client classes are in your classpath, and compiled the CDOrderer class, you are ready to try things out. Ensure that the services at urn:cd-order-service and urn:cd-catalog are deployed and available. Additionally, you may want to add one or both of the CDs in the po.xml document, from Example 13-2 and Example 13-3. I tried it once with a single CD added to the catalog, to see both a successful and failed order, and then with both added to see it succeed on both:

C:\javaxml2\build>java javaxml2.CDAdder
    http://localhost:8080/soap/servlet/rpcrouter
    "Little Lion" "Brooks Williams" "Signature Sounds"
Adding CD titled 'Little Lion' by 'Brooks Williams', on the label
    Signature Sounds
Successful CD Addition.

Make sure you have the SOAP-suitable XML shown in Example 13-5 saved; I used the filename poMsg.xml in my xml directory. Finally, you're ready to run the client:

bmclaugh@GANDALF
$ java javaxml2.CDOrderer c:\javaxml2\ch13\xml\poMsg.xml
<?xml version='1.0' encoding='UTF-8'?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<response>
 <orderedCDs>
  <orderedCD artist="Brooks Williams" label="Signature Sounds"
             title="Little Lion"/>
 </orderedCDs>
 <failedCDs>
  <failedCD title="What You Whispered">Requested CD is not available.</failedCD>
 </failedCDs>
</response>
</s:Body>
</s:Envelope>

The program spits out the XML response from the server; I formatted my response to make it more readable. You should get a similar output (in this example, I've added the Brooks Williams CD but not the David Wilcox CD to the catalog, as shown previously) in your tests.

At this point, you should be feeling pretty comfortable with writing both SOAP servers and client. Additionally, you probably realize that UDDI and WSDL are not that complex. Used with SOAP, they provide a nice framework for web services and interbusiness communication. I also recommend you take a look at some of the more advanced properties of Apache SOAP, or the SOAP implementation you are using. For example, Apache SOAP supports using SMTP (the Simple Mail Transport Protocol) as a transport for RPC and messaging. I don't cover this, because it's a more advanced SOAP topic, and because the specification does not yet cover SMTP as a transport. In other words, it's implementation-dependent, and I avoid those cases as much as possible. In any case, getting to know the ins and outs of your SOAP implementation only increases the effectiveness of your web services.

13.4.6. Where Do I Go From Here?

If you're like me, you probably are ready for about three or four more chapters of this stuff. A chapter on working with UDDI registries programmatically, another chapter on working with WSDL, some more examples. . . it would be fun. Of course, then this would be a book on web services, not Java and XML. However, there are several resources online you should check out to get to the next level. First, try http://www.webservices.org, which has a lot of additional introductory material. Then check out IBM's site on the subject, at http://www.ibm.com/developerworks/webservices; if you are working with Microsoft clients (C#, Visual Basic, and COM objects), you'll want to visit http://msdn.microsoft.com/soap. In other words, use this chapter as a solid jumping off point, visit the web services sites mentioned here, and look for upcoming books from O'Reilly on SOAP.

Previous | Home | Next
13.3. WSDL | Book Index | 13.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

13.5. What's Next?

With a working understanding of web services, you're ready to conquer the world, right? Well, not quite. As with anything else in Java, there are as many ways to do business-to-business and service-based communication as there are people doing it. I'd rather not imply that the latest craze (SOAP, UDDI, and WSDL) is the end-all, be-all solution, so in the next chapter I'll introduce a completely different way to tackle intercommunication problems, using various languages, servlets, and RSS (Rich Site Summary). That should round out your knowledge base considerably.

Previous | Home | Next
13.4. Putting It All Together | Book Index | 14. Content Syndication
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Content Syndication (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 14. Content Syndication

Contents:

The Foobar Public Library
mytechbooks.com
Push Versus Pull
What's Next?

In the last two chapters, I tried to give you a "web services" view of the world. In other words, you saw how to write applications that communicated with each other through the various web services technologies like WSDL, UDDI, and SOAP. However, as you also saw, some things in this worldview are still a bit shaky, like WSDL generation and support (providing you're using open standards like Apache SOAP). Today, you may want to consider other options for business-to-business communication. In this chapter, I present an alternative solution for communicating across businesses to round out your skillset.

In this chapter, I look at using different XML specifications to provide this sort of communication across application and company lines, using some companies invented for the purpose. To begin with, I'll examine the Foobar Public Library, a library that allows its suppliers to enter online new books being shipped to the library. These books are then added to the library's data store for later use. Unfortunately, the library is having a hard time finding good Java developers, so it has implemented a Perl-based CGI solution. New books are entered online and then stored by a Perl script. Already, you can see that alternatives to web services would be handy, as finding a good Perl SOAP implementation is not easy (at least not yet!).

I'll also look at another company, mytechbooks.com. mytechbooks.com sells technical and computing books (such as this one) online through various partnerships with large bookstores. It has recently signed an agreement with the Foobar Public Library to obtain books from the library. mytechbooks.com will pay for the shipping and inventory costs of the books, while the library agrees to order extra books at its discounted costs; these extra books are then sold by mytechbooks.com. mytechbooks.com needs to be able to access the new books entered into the Foobar Public Library by suppliers to know when new offerings are available, and then advertise those new offerings. However, mytechbooks.com has no idea how to interface with the Foobar Public Library's Perl-based system. Additionally, there are no protected network connections between the two organizations, so normal HTTP must be used for communication. And just to get us out of the web services world, mytechbooks.com wants to wait until web services is more fleshed out, and has WSDL support integrated in more firmly, so wants a more stable solution (or at least one that has been in use a little longer).

Finally, I'll look at customers of mytechbooks.com. The bookstore targets people who are active online, so wants to advertise on sites like Netscape Netcenter; it also wants to allow people to easily obtain information from its site when new offerings are available. However, as in the situation with the Foobar Public Library, the people at mytechbooks.com have no idea how to achieve this goal. Seeing as they've read O'Reilly books and articles on http://www.oreillynet.com, they've heard RSS's spec lead, Rael Dornfest, talk about how cool RSS is, and want to try it. Of course, Rael is right, so that's what I talk about in this chapter.

We tackle this common scenario by starting with the Foobar Public Library and examining its Perl system. Moving out to mytechbooks.com and then the customers of the bookstore, I show you how to enable this business-to-business (to-customer) application by using XML as a communication tool between each layer.

14.1. The Foobar Public Library

To start the creation of a business-to-business system, I describe the system currently in place at the Foobar Public Library. Before diving into the code, though, it's necessary to examine the library's requirements so that you do not create a system it cannot support.

14.1.1. Evaluating the Requirements

All too often, good solutions to a problem are not appropriate solutions for the company with the problem. The Foobar Library is a perfect example of this: certainly a Java servlet that could communicate with servlets built by mytechbooks.com could quickly solve the two organizations' problems. However, this ignores the library's requirements. Before creating a solution, the library detailed its requirements:

  • The solution must be Perl-based; no Java engineers are on staff.

  • The solution must not involve new software or library installations.

  • The solution must not impact the existing order-entry system (no interface changes).

While these are not extremely stringent requirements, they force a solution other than Java servlets. You must avoid using Java as a solution. Of course, as this is a book on XML, you should be thinking that storing the data about new books in an XML format could allow the library to then supply that XML to clients through an HTTP request, thus enabling those clients to use the data in any way they wish. In fact, this is a much better solution than servlet-to-servlet communication, as the XML can be used by any company or client in its applications, rather than tying the library (and its books) to a specific company. This then defines the goal for updating the Foobar Public Library's system: save the entered information as XML data, and then provide HTTP access to that XML data for clients and customers.

14.1.2. Entering the Books

We need to examine the existing HTML interface for suppliers entering new books into the system. Example 14-1 shows the static HTML used to generate this form.

Example 14-1. Static HTML for Foobar Public Library interface

<html>

<head>
  <title>Foobar Public Library: Add Books</title>
  <style>
<!--
body         { font-family: Arial }
h1           { color: #000080 }
-->
  </style>
</head>

<body link="#FFFF00" vlink="#FFFF00" alink="#FFFF00">
 <table border="0" width="100%" cellpadding="0" cellspacing="0">
  <tr>
   <td width="15%" bgcolor="#000080" valign="top" align="center">
    <b><i>
     <font color="#FFFFFF" size="4">Options</font>
    </i></b>
   <p><b>
     <font color="#FFFFFF">
      <a href="/javaxml/foobar">Main Menu</a>
     </font>
   </p></b>
   <p><b>
    <font color="#FFFFFF">
     <a href="/javaxml/foobar/catalog.html">Catalog</a>
    </font>
   </b></p>
   <p><b>
    <i><font color="#FFFF00">Add Books</font></i>
   </b></p>
   <p><b>
    <font color="#FFFFFF">
     <a href="/javaxml/foobar/logout.html">Log Out</a>
    </font>
   </p></td>
   <td width="*" valign="top" align="center">
    <h1 align="center">The Foobar Public Library</h1>
    <h3 align="center"><i>- Add Books -</i></h3>

<!-- This will need to point at your CGI directory and script, which
     we look at next -->
    <form method="POST" action="/cgi/addBook.pl">

     <table border="0" cellpadding="5" width="100%">
      <tr>
       <td width="100%" valign="top" align="center" colspan="2">
        Title&nbsp;
        <input type="text" name="title" size="20">
        <hr width="85%" />
       </td>
      </tr>
      <tr>
       <td width="50%" valign="top" align="right">Author&nbsp;
        <input type="text" name="author" size="20">
       </td>
       <td width="50%" valign="top" align="left">Subject&nbsp;
        <select size="1" name="subject">
         <option>Fiction</option>
         <option>Biography</option>
         <option>Science</option>
         <option>Industry</option>
         <option>Computers</option>
        </select></td>
       </tr>
       <tr>
        <td width="50%" valign="top" align="right">Publisher&nbsp;
         <input type="text" name="publisher" size="20">
        </td>
        <td width="50%" valign="top" align="left">ISBN&nbsp;
         <input type="text" name="isbn" size="20">
        </td>
       </tr>
       <tr>
        <td width="50%" valign="top" align="right">Price&nbsp;
         <input type="text" name="price" size="20">
        </td>
        <td width="50%" valign="top" align="left">Pages&nbsp;
         <input type="text" name="numPages" size="20">
        </td>
       </tr>
       <tr>
        <td width="100%" valign="top" align="center" colspan="2">
         Description&nbsp;
         <textarea rows="2" name="description" cols="20"></textarea>
        </td>
       </tr>
      </table>
      <p>
       <input type="submit" value="Add this Book" name="addBook">
       <input type="reset" value="Reset Form" name="reset">
       <input type="button" value="Cancel" name="cancel">
      </p>
    </form>
   </td>
  </tr>
 </table>
</body>
</html>

This file, saved as addBooks.html, provides the portion of the library application allowing suppliers to add new books they are sending to the library.

NOTE: In Example 14-1 and throughout the rest of the chapter, complete code and HTML listings are given so that you can create the example applications and walk through the process of enabling XML communication across the applications. Additionally, the code examples in this chapter assume you are using the filenames supplied in the text; you will need to change the code and examples if you use your own filenames. Code that may need to be changed to reference different filenames or scripts is emphasized in the listings to help you walk through the examples.

The HTML in Example 14-1, when accessed through a web server, results in the output shown in Figure 14-1. Although we do not look at the other menu options, the supplier can also view the library's catalog, go to the application's main menu, and log out of the application by using the menu on the left of the screen.

Figure 14-1. HTML user interface for Foobar Public Library

This form allows the supplier to enter the details about each book it is sending to the library. The supplier enters the book's essentials (title, author, publisher, pages, and a description), as well as a subject to categorize the book, and sales details, which include the price and ISBN number.

Once this information has been entered, it is submitted to a Perl CGI script:

<form method="POST" action="/cgi/addBook.pl">

This script, then, must produce XML output. The easiest solution would be to download a Perl library that handled XML parsing, such as Xerces-Perl; however, remember that one requirement of the library was that no libraries or software could be added. While this may seem silly and frustrating, keep in mind that many companies have very strict lock-downs on their production systems. In this case, the Foobar Public Library is just beginning to introduce applications on the Internet, and it does not have resources to support additional software.

Luckily, the code only has to output XML; this is done fairly easily by generating a file with information on the entered books by brute force. Things would be much trickier if parsing incoming XML were required. Because the library needs to keep any existing books, each new entry is appended to an existing file, instead of creating a new file upon a new request. Writing the Perl is almost trivial, and the complete Perl program to read the request parameters and append the information to an existing file is shown in Example 14-2.

Example 14-2. Perl CGI script to generate XML entries from entered books

#!/usr/local/bin/perl

# This should be the directory you wish to write files to
$baseDir = "/home/bmclaugh/javaxml/foobar/books/";

# This should be the filename to use
$filename = "books.txt";

$bookFile = $baseDir . $filename;

# Get the user's input
use CGI;
$query = new CGI;

$title = $query->param('title');
$author = $query->param('author');
$subject = $query->param('subject');
$publisher = $query->param('publisher');
$isbn = $query->param('isbn');
$price = $query->param('price');
$numPages = $query->param('numPages');
$description = $query->param('description');

# Save the book to a file in XML
if (open(FILE, ">>" . $bookFile)) {
  print FILE "<book subject=\"" . $subject . "\">\n";
  print FILE " <title><![CDATA[" . $title . "]]></title>\n";
  print FILE " <author><![CDATA[" . $author . "]]></author>\n";
  print FILE " <publisher><![CDATA[" . $publisher . "]]></publisher>\n";
  print FILE " <numPages>" . $numPages . "</numPages>\n";
  print FILE " <saleDetails>\n";
  print FILE "  <isbn>" . $isbn . "</isbn>\n";
  print FILE "  <price>" . $price . "</price>\n";
  print FILE " </saleDetails>\n";
  print FILE " <description>";
  print FILE "<![CDATA[" . $description . "]]>";
  print FILE "</description>\n";
  print FILE "</book>\n\n";

  # Give the user a confirmation
  print <<"EOF";
Content-type: text/html

  <html>
   <head>
    <title>Foobar Public Library: Confirmation</title>
   </head>
   <body>
    <h1 align="center">Book Added</h1>
    <p align="center">
     Thank you.  The book you submitted has been added to the Library.
    </p>
   </body>
  </html>
EOF

} else {
  print <<"EOF";
Content-type: text/html

  <html>
   <head>
    <title>Foobar Public Library: Error</title>
   </head>
   <body>
    <h1 align="center">Error in Adding Book</h1>
    <p align="center">
     We're sorry.  The book you submitted has <i>not</i> been added to
     the Library.
    </p>
   </body>
  </html>
EOF
}
close (FILE);

This program, saved as addBook.pl, is invoked by a form submitted when the supplier enters a new book. The script defines the file to write to, and then assigns the request parameter values to local variables:

$title = $query->param('title');
$author = $query->param('author');
$subject = $query->param('subject');
$publisher = $query->param('publisher');
$isbn = $query->param('isbn');
$price = $query->param('price');
$numPages = $query->param('numPages');
$description = $query->param('description');

Once these values are easily accessible, the script opens the file defined earlier in append mode (signified by >> preceding the filename) and writes raw XML-formatted information about the entered book to the end of the file:

  print FILE "<book subject=\"" . $subject . "\">\n";
  print FILE " <title><![CDATA[" . $title . "]]></title>\n";
  print FILE " <author><![CDATA[" . $author . "]]></author>\n";
  print FILE " <publisher><![CDATA[" . $publisher . "]]></publisher>\n";
  print FILE " <numPages>" . $numPages . "</numPages>\n";
  print FILE " <saleDetails>\n";
  print FILE "  <isbn>" . $isbn . "</isbn>\n";
  print FILE "  <price>" . $price . "</price>\n";
  print FILE " </saleDetails>\n";
  print FILE " <description>";
  print FILE "<![CDATA[" . $description . "]]>";
  print FILE "</description>\n";
  print FILE "</book>\n\n";

The subject is used as an attribute on the enclosing element, book, and the rest of the information is entered in as elements. Because a book's title, author, description, and publisher may include quotation marks, apostrophes, ampersands, and other characters that would have to be escaped, the code encloses that data within a CDATA section so as not to have to worry about escaping the data.

Additionally, you should notice that no XML declaration or root element is created, as multiple books will exist in a single file. Because it is a bit difficult to check if the file exists, write the declaration and root element if the file is new, and then write out the ending element (which has to be overwritten at each new entry), the file is left as an XML document fragment. For example, here is what the file might look like after two books have been entered:

<book subject="Computers">
 <title><![CDATA[Java Servlet Programming]]></title>
 <author><![CDATA[Jason Hunter]]></author>
 <publisher><![CDATA[O'Reilly & Associates]]></publisher>
 <numPages>753</numPages>
 <saleDetails>
  <isbn>0596000405</isbn>
  <price>44.95</price>
 </saleDetails>
 <description><![CDATA[This book is a superb introduction to Java
  servlets and their various communications mechanisms.]]></description>
</book>

<book subject="Fiction">
 <title><![CDATA[Second Foundation]]></title>
 <author><![CDATA[Isaac Asimov]]></author>
 <publisher><![CDATA[Bantam Books]]></publisher>
 <numPages>279</numPages>
 <saleDetails>
  <isbn>0553293362</isbn>
  <price>5.59</price>
 </saleDetails>
 <description><![CDATA[fter the First Foundation was taken over by the
  Mule, only the Second Foundation stood between order and the utter
  destruction the Mule would bring.]]></description>
</book>

Although not a complete XML document, this fragment is well-formed and could be inserted into an XML document with the header and root element already set. In fact, when I look at providing a listing of books in the next section, that is precisely how I'll handle output of the fragment.

The rest of the script outputs HTML indicating whether the book was successfully added or if errors occurred. Once a book has been added to the XML storage, the supplier receives the simple confirmation message shown in Figure 14-2.

Figure 14-2. Confirmation message when a book is added

Now that there is an XML document fragment with information about new books, you'll need to take that file and provide it to requestors.

14.1.3. Providing a Listing of Available Books

We again can use Perl as a mechanism to provide clients and customers with an XML listing of new books. I'm making the assumption that some other portion of the library's application periodically reads the XML data and updates the library's catalog; at this point, that application component is responsible for removing the entries within the file (or the file itself) so that the books within it are no longer regarded as new entries. With this assumption, all a second Perl script has to do is read the XML fragment and add the data within it to an XML document that is output to the screen. As I already mentioned, the script also needs to add an XML declaration and a root element to surround the content within the new books file. This new script, shown in Example 14-3, reads the file created by the addBook.pl script and outputs the content within an XML document when it is requested over HTTP.

Example 14-3. Perl CGI script to output XML document with new book listings

#!/usr/local/bin/perl

# This should be the directory you wish to write files to
$baseDir = "/home/bmclaugh/javaxml/foobar/books/";

# This should be the filename to use
$filename = "books.txt";

$bookFile = $baseDir . $filename;

# First open the file
open(FILE, $bookFile) || die "Could not open $bookFile.\n";

# Let browser know what is coming
print "Content-type: text/plain\n\n";

# Print out XML header and root element
print "<?xml version=\"1.0\"?>\n";
print "<books>\n";

# Print out books
while (<FILE>) {
  print "$_";
}

# Close root element
print "</books>\n";

close(FILE);

This script, saved as supplyBooks.pl, accepts a request, reads the file created by addBook.pl, and outputs XML upon an HTTP request. The result of requesting this script in a web browser (with several books added) is shown in Figure 14-3.

Figure 14-3. XML output from supplyBooks.pl

As you can see, this easily turned the library's simple Perl-based application into a component capable of supplying useful information to its clients, including the mytechbooks.com technical bookstore. Additionally, we were able to accomplish this without installing new software, changing the architecture of the library's system or application, or even writing a line of Java!

Previous | Home | Next
13.5. What's Next? | Book Index | 14.2. mytechbooks.com
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


mytechbooks.com (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

14.2. mytechbooks.com

With the Foobar Public Library allowing access to an XML listing of its new books, mytechbooks.com is moving closer to its goal of providing up-to-date content to its customers. In addition, mytechbooks.com already has an established standard for using Java for application development. This makes the process of accessing and using the XML from the library even easier, as Java has the excellent support for XML we have been looking at throughout this book. You'll need to allow mytechbooks.com to provide an online listing of new books first, and then look at how to get this information out to its customers automatically.

14.2.1. Filtering XML Data

If you remember, the Foobar Public Library allowed books on several different subjects to be entered into its system; mytechbooks.com wants only the books about computer-related subjects. Fortunately, the library captured this information in the subject attribute of the book element for each book in its XML data. The first task is to filter out any book whose subject is not "Computers". Once the technical books have been obtained, they should be formatted into an HTML page that can be shown to customers visiting mytechbooks.com.

For this company and application, there is no static HTML, since the page showing new listings must be generated each time it is accessed. I'm going to use a servlet here for handling these responses. Although Apache Cocoon would be an excellent choice for converting the XML data from the library into an HTML response, mytechbooks.com is under tremendous time pressure to make these book listings available, and does not want to introduce such a large change into its system immediately; instead, it would prefer to use XML parsers and processors and then add Cocoon in as a second-phase addition. This means that you'll have to handle conversion from XML to HTML as well as the filtering of the data and the addition of other presentation-specific items, such as a company logo and menu bar.

However, taking all the information at your disposal about XML and XSL, you remember that even without Cocoon you can use XSL to transform an XML document into HTML. Applying a transformation would also allow you to filter out the books that do not have the subject criteria that mytechbooks.com desires. With this in mind, it's simple to create an XSL stylesheet that can be applied to the XML response from the Foobar Public Library. Example 14-4 shows the beginning of this stylesheet, which handles generation of the HTML specific to the mytechbooks.com web site.

Example 14-4. XSL stylesheet for Foobar Public Library book listings

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0"
>

  <xsl:template match="books">
   <html>
    <head>
     <title>mytechbooks.com - Your Computer Bookstore</title>
    </head>
    <body background="/javaxml/techbooks/images/background.gif"
          link="#FFFFFF" vlink="#FFFFFF" alink="#FFFFFF">
     <h1 align="center">
      <font face="Arial" color="#00659C">
       &lt;mytechbooks.com&gt;
      </font>
     </h1>
     <p align="center">
      <i><b>
       Your source on the Web for computing and technical books.
      </b></i>
     </p>
     <p align="center">
      <b><font size="4" color="#00659C">
       <u>New Listings</u>
      </font></b>
     </p>
     <table border="0" cellpadding="5" cellspacing="5">
      <tr>
       <td valign="top" align="center" nowrap="nowrap" width="115">
        <p align="center">
         <font color="#FFFFFF"><b>
          <a href="/javaxml/techbooks/">Home</a>
         </b></font>
        </p>
        <p align="center">
         <font color="#FFFFFF"><b>
          <a href="/javaxml/techbooks/current.html">Current Listings</a>
         </b></font>
        </p>
        <p align="center">
         <b><font color="#FFFFFF">
          <i>New Listings</i>
         </font></b>
        </p>
        <p align="center">
         <font color="#FFFFFF"><b>
          <a href="/javaxml/techbooks/contact.html">Contact Us</a>
         </b></font>
        </p>
       </td>
       <td valign="top" align="left">
        <table border="0" cellpadding="5" cellspacing="5">
         <tr>
          <td width="450" align="left" valign="top">
           <p>
            <b>
             Welcome to <font face="courier">mytechbooks.com</font>,
             your source on the Web for computing and technical books.
             Our newest offerings are listed on the left.  To purchase
             any of these fine books, simply click on the
             &quot;Buy this Book!&quot; link, and you will be taken to
             the shopping cart for our store.  Enjoy!
            </b>
           </p>
           <p>
            <b>
             You should also check out our current listings, information
             about the store, and you can call us with your questions.
             Use the links on the menu to the left to access this
             information.  Thanks for shopping!
            </b>
           </p>
          </td>
          <td align="left">

        <!-- Handle creation of content for each new *computer* book -->

          </td>
         </tr>
        </table>
       </td>
      </tr>
     </table>
    </body>
   </html>
  </xsl:template>

</xsl:stylesheet>

While this doesn't yet filter the incoming XML data or transform that data, it does take care of the HTML interface for the user. Often it is much easier to take care of these presentation details first, and then add the transformation-specific logic afterwards.

NOTE: When developing XSL stylesheets, particularly for web applications, you should test the results with your XSLT Processor using its command-line capabilities. This can help you ensure that the stylesheet is transforming your document as you expect at each step of its development; trying to debug a large stylesheet's problems once it is complete is much more difficult. For this example, you could access the supplyBooks.pl script in a web browser, save the results to an XML file, and test that and the stylesheet as you follow the examples.

Similar to the Foobar Public Library's application, this provides a menu on the left with hyperlinks to other portions of the application, contains some text about the company and its offerings, and then leaves a right column open for the addition of new book listings.

Before filtering the content, you need to add a template for outputting HTML content from a single book element's entry. As you recall, an entry looks like this:

<book subject="Computers">
 <title><![CDATA[Running Linux]]></title>
 <author><![CDATA[Matt Welsh]]></author>
 <publisher><![CDATA[O'Reilly & Associates]]></publisher>
 <numPages>729</numPages>
 <saleDetails>
  <isbn> 156592469X</isbn>
  <price>39.95</price>
 </saleDetails>
 <description><![CDATA[In the tradition of all O'Reilly books, Running
  Linux features clear, step-by-step instructions that always seem to
  provide just the right amount of information.]]></description>
</book>

You can then convert this to HTML with the following XSL template:

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0"
>

 <xsl:template match="books">
   <!-- Presentation of User Interface -->
 </xsl:template>

 <xsl:template match="book">
  <table border="0" cellspacing="1" bgcolor="#000000">
   <tr>
    <td>
     <table border="0" cellpadding="3" cellspacing="0">
      <tr>
       <td width="100%" bgcolor="#00659C" nowrap="nowrap" align="center">
        <b><font color="#FFFFFF">
         <xsl:value-of select="title" />
        </font></b>
       </td>
      </tr>
      <tr>
       <td width="100%" align="center" nowrap="nowrap" bgcolor="#FFFFFF">
        <font color="#000000"><b>
         Author: <xsl:value-of select="author" /><br />
         Publisher: <xsl:value-of select="publisher" /><br />
         Pages: <xsl:value-of select="numPages" /><br />
         Price: <xsl:value-of select="saleDetails/price" /><br />
         <br />
        </b></font>
        <xsl:element name="a">
         <xsl:attribute name="href">/servlets/BuyBookServlet?isbn=
          <xsl:value-of select="saleDetails/isbn" />
         </xsl:attribute>
         <font color="#00659C">Buy the Book!</font>
        </xsl:element>
       </td>
      </tr>
     </table>
    </td>
   </tr>
  </table>
  <br />
 </xsl:template>

</xsl:stylesheet>

This template matches the book element, and then creates a table with a heading in one row, and contents in the second row. The entire table is within another table with a black background, which results in the appearance of the table being surrounded by a beveled black border. The title is inserted into the header of the table, and the information about the book (author, publisher, pages, and price) is added to the content of the table. Finally, a link to a Java servlet, BuyBookServlet, is provided to allow easy access to purchasing the book. The value of the book's isbn element is supplied as an argument to this servlet, which enables it to load the book being purchased.

WARNING: In your XSL stylesheet, you should ensure that the line indicating the use of BuyBookServlet and the line with the xsl:value-of element selecting the book's ISBN number is actually one single line. If not, spaces and a carriage return could be inserted into the resultant URL, causing incorrect information to be passed to the servlet. The example stylesheet has this information broken into two lines because of the space constraints of the printed page.

The last addition you need to make to your stylesheet is to ensure that the new template is applied, and that only books with the subject "Computers" are passed to the new template. You can reference the value of the subject attribute with the @ symbol in your stylesheet, and filter the requests with the select attribute on the xsl:apply-templates element:

  </td>
  <td align="left">

   <!-- Handle creation of content for each new *computer* book -->
  <xsl:apply-templates select="book[@subject='Computers']" />

  </td>
 </tr>
</table>

This references the value of the attribute and compares it to a literal, enclosed within single quotes because the entire XPath expression is enclosed within double quotes. Because you are accessing an attribute of a nested element, you'll need to reference the element by name, and surround the expression on the element's attribute with brackets. This will ensure that only books with a subject of "Computers" have templates applied, and are therefore included in the HTML output. Once the stylesheet is complete, it can be saved as computerBooks.xsl and referenced programmatically by a Java servlet, which I'll show you how to write next.

14.2.2. XSLT from a Servlet

With your stylesheet ready for use, you need to add Java code to apply it to the XML data from the Foobar Public Library. This data is accessed easily by using Java's java.net.URL class to make an HTTP request to the library's system. Once you have this set up, all that is left is to actually apply the XSL transformation programmatically. Example 14-5 shows the Java servlet code that loads the XML data from the library, and indicates where the transformation code would be inserted.

Example 14-5. Java Servlet for transforming book listings into HTML

package com.techbooks;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import javax.servlet.*;
import javax.servlet.http.*;

public class ListBooksServlet extends HttpServlet {

    /** Host to connect to for books list */
    private static final String hostname = "newInstance.com";
    /** Port number to connect to for books list */
    private static final int portNumber = 80;
    /** File to request (URI path) for books list */
    private static final String file = "/cgi/supplyBooks.pl";

    /** Stylesheet to apply to XML */
    private static final String stylesheet =
        "/home/bmclaugh/javaxml/techbooks/XSL/computerBooks.xsl";

    public void service(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        res.setContentType("text/html");

        // Connect and get XML listing of books
        URL getBooksURL = new URL("http", hostname, portNumber, file);
        InputStream in = getBooksURL.openStream();

        // Transform XML for InputStream into HTML output
    }
}

This simple servlet requests the Foobar Public Library's application through an HTTP request, and gets the XML response in an InputStream.[22] This stream should then be used as a parameter to the XSLT processor, as well as the XSL stylesheet defined as a constant in the servlet.

[22] For more information on the URL class and Java I/O, see Java I/O by Elliotte Rusty Harold (O'Reilly).

There is currently no Java API that specifies how XSLT transformations can occur programmatically; however, each processor vendor should have classes that allow a transformation to be invoked from your Java code. I continue to look at using the Apache Xalan processor here; you should consult your processor's vendor for the method or methods to invoke in your own programs.

For Apache Xalan, the XSLTProcessor class is provided in the org.apache.xalan.xslt package for just this purpose. It takes as parameters an XSLTInputSource wrapping the XML file to process, an XSLTInputSource wrapping the XSL stylesheet to apply, and an XSLTResultTarget to use for output of the transformation. All three of these helper classes are in the org.apache.xalan.xslt package as well. They can conveniently be created by passing in an InputStream (to XSLTInputSource) or an OutputStream (to XSLTResultTarget). You have the XML document as an InputStream, you can wrap the XSL stylesheet within a FileInputStream, and the servlet API provides easy access to the ServletOutputStream object through the getOutputStream() method on the HttpServletResponse object. The last detail to address is obtaining an instance of XSLTProcessor. Because there are several underlying mechanisms that can be used for processing, this class is not instantiated directly, but rather obtained through the XSLTProcessorFactory class, also in the org.apache.xalan.xslt package. You should be familiar with factory classes by now, so all that is left is to import the classes you'll need and add the processing method calls to the servlet:

package com.techbooks;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import javax.servlet.*;
import javax.servlet.http.*;

// Import Xalan XSLT Processor components
import org.apache.xalan.xslt.XSLTInputSource;
import org.apache.xalan.xslt.XSLTProcessor;
import org.apache.xalan.xslt.XSLTProcessorFactory;
import org.apache.xalan.xslt.XSLTResultTarget;

public class ListBooksServlet extends HttpServlet {

    /** Host to connect to for books list */
    private static final String hostname = "newInstance.com";
    /** Port number to connect to for books list */
    private static final int portNumber = 80;
    /** File to request (URI path) for books list */
    private static final String file = "/cgi/supplyBooks.pl";

    /** Stylesheet to apply to XML */
    private static final String stylesheet =
        "/home/bmclaugh/javaxml/techbooks/XSL/computerBooks.xsl";

    public void service(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        res.setContentType("text/html");

        // Connect and get XML listing of books
        URL getBooksURL = new URL("http", hostname, portNumber, file);
        InputStream in = getBooksURL.openStream();

        // Transform XML for InputStream into HTML output
        try {
            XSLTProcessor processor = XSLTProcessorFactory.getProcessor();

            // Transform XML with XSL stylesheet
            processor.process(new XSLTInputSource(in),
                              new XSLTInputSource(
                                  new FileInputStream(stylesheet)),
                              new XSLTResultTarget(
                                  res.getOutputStream()));

        } catch (Exception e) {
            PrintWriter out = res.getWriter();
            out.println("Error: " + e.getMessage());
            out.close();
        }
    }
}
NOTE: I could have also used JAXP 1.1's TrAX API for performing this translation. However, JAXP 1.1 is still pretty new as of this writing, and I see few people adopting it (yet). Additionally, most servlet engines, especially Tomcat, still ship with JAXP 1.0, and many are relying on that default behavior rather than supplying a newer version of JAXP.

When this new servlet is requested, it in turn requests the XML data from the Foobar Public Library. This data (a listing of the newly available books) is then transformed and output to the screen as HTML. The response from the servlet should look similar to Figure 14-4.

Figure 14-4. HTML output from mytechbooks.com new book listing

Along with the menu links on the left (not implemented in this example), the newest book listings are printed in a very nice format, all with up-to-date information (thanks to the changes at the Foobar Public Library!) as well as links to buy the book with a few mouseclicks. Now mytechbooks.com customers can easily browse the new book listings online; all that is left is to push this information out to these customers, so they don't even have to type in a URL. I'll look at solving this difficult problem next.

Previous | Home | Next
14. Content Syndication | Book Index | 14.3. Push Versus Pull
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Push Versus Pull (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

14.3. Push Versus Pull

So far, I have looked at building applications assuming that the application clients would always pull data and content. In other words, a user had to type a URL into a browser (in the case of the mytechbooks.com new book listings), or an application like the mytechbooks.com servlet had to make an HTTP request for XML data (in the case of the Foobar Public Library). While this is not a problem, it is not always the best way for a company like mytechbooks.com to sell books. Clients pulling data have to remember to visit sites they would buy items from, and often don't revisit those sites for days, weeks, or even months. While those clients may often purchase a large number of goods and services when they do remember, on average, those purchases do not result in as much revenue as if small purchases were made more frequently.

Realizing this trend, mytechbooks.com wants to be able to push data to its clients. Pushing data involves letting the client know (without any client action) that new items are available or that specials are being run. This in turn allows the client to make more frequent purchases without having to remember to visit a web page. However, pushing data to clients is difficult in a web medium, as the Internet does not behave as a thick client: it is harder to send pop-up messages or generate alerts for users. What mytechbooks.com has discovered, though, is the popularity of personalized "start pages" like Netscape's My Netscape and Yahoo's My Yahoo pages. In talking with Netscape, mytechbooks.com has been hearing about a technology called Rich Site Summary (RSS), and thinks that may be the answer to its need to push data out to clients.

14.3.1. Rich Site Summary

Rich Site Summary (RSS) is a particular flavor of XML. It has its own DTD, and defines what is called a channel. A channel is a way to represent data about a specific subject, and provides for a title and description of the channel, an image or logo, and then several items within the channel. Each item, then, is something of particular interest about the channel, or a product or service available. Because the allowed elements of an item are fairly generic (title, description, hyperlink), almost anything can be represented as an item of a channel. An RSS channel is not intended to provide a complete site's content, but rather a short blurb about a company or service, suitable for display in a portal-style framework, or as a sidebar on a web site. In fact, the different "widgets" at Netscape's Netcenter are all RSS channels, and Netscape allows the creation of new RSS channels that can be registered with Netcenter. Netscape also has a built-in system for displaying RSS channels in an HTML format, which of course fits into its Netcenter start pages.

At this point, you may be a little concerned that RSS is to Netscape as Microsoft's XML parser is to Microsoft: difficult to integrate with other tools or vendors. Although originally developed by Netscape specifically for Netcenter, the XML structure of RSS has made it usable by any application that can read a DTD. In fact, many portal-style web sites and applications are beginning to use RSS, such as the Apache Jetspeed project (http://jakarta.apache.org/jetspeed), an open source Enterprise Information Portal system. Jetspeed takes the same RSS format that Netscape uses, and renders it in a completely different manner. Because of the concise grammar of RSS, this is easily done.

As many users have start pages, or homepages, or similar places on the Web that they frequent, mytechbooks.com would like to create an RSS channel that provides new book listings, and then allows interested clients to jump straight to buying an item that catches their eye. This is an effective means to push data, as products like Netcenter will automatically update RSS channel content as often as the user desires.

14.3.2. Creating an XML RSS Document

The first thing you need to do to use RSS is create an RSS file. This is almost too simple to be believed: other than referencing the correct DTD and following that DTD, there is nothing at all complicated about creating an RSS document. Example 14-6 shows a sample RSS file that mytechbooks.com has modeled.

Example 14-6. Sample RSS document for mytechbooks.com

<?xml version="1.0" encoding="UTF-8"?>

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://purl.org/rss/1.0/"
>
 <channel>
  <title>mytechbooks.com New Listings</title>
  <link>http://www.newInstance.com/javaxml2/techbooks</link>
  <description>
   Your online source for technical material, computers,
   and computing books!
  </description>

  <image rdf:resource="http://newInstance.com/javaxml2/logo.gif" />

  <items>
   <rdf:Seq>
    <rdf:li resource="http://www.newInstance.com/javaxml2/techbooks" />
   </rdf:Seq>
  </items>
 </channel>

  <image rdf:about="http://newInstance.com/javaxml2/logo.gif">
   <title>mytechbooks.com</title>
   <url>http://newInstance.com/javaxml2/logo.gif</url>
   <link>http://newInstance.com/javaxml2/techbooks</link>
  </image>

  <item rdf:about="http://www.newInstance.com/javaxml2/techbooks">
   <title>Java Servlet Programming</title>
   <link>
    http://newInstance.com/javaxml2/techbooks/buy.xsp?isbn=156592391X
   </link>
   <description>
    This book is a superb introduction to Java servlets
    and their various communications mechanisms.
   </description>
  </item>
</rdf:RDF>

The root element must be RDF, in the RDF namespace, as shown in the example. Within the root element, one single channel element must appear. This has elements that describe the channel (title, link, and description), an optional image that can be associated with the channel (as well as information about that image), and then as many as 15 item elements,[23] each detailing one item related to the channel. Each item has a title, link, and description element, all of which are self-explanatory. An optional text box and button to submit the information in the book can be added as well, although these are not included in the example. For complete details of allowed elements and attributes, visit the RSS 1.0 specification online at http://groups.yahoo.com/group/rss-dev/files/specification.html.

[23] This isn't a limit set by RSS 1.0, but is used for backwards compatibility with RSS 0.9 and 0.91.

NOTE: As in previous examples, actual RSS channel documents should avoid having whitespace within the link and url elements, but rather have all information on a single line. Again, the formatting in the example does not reflect this due to printing and sizing constraints.

There is one somewhat tricky thing to watch out for, though. You'll notice that the item element (or elements) is actually not nested within the channel element at all. To create a link between items in the document and the channel, you'll want to use some RDF (the Resource Description Framework, which RSS is a descendant of) constructs:

  <items>
   <rdf:Seq>
    <rdf:li resource="http://www.newInstance.com/javaxml/techbooks" />
   </rdf:Seq>
  </items>

Here, the items element is nested within the channel element. Then, the li construct, in the RDF-defined namespace, is assigned a URI through the resource attribute. In each item you want associated with this channel, supply the about attribute (again in the RDF namespace) and assign it the same URI you used in the channel's resource descriptor:

  <item rdf:about="http://www.newInstance.com/javaxml/techbooks">
    <!-- Item content -->
  </item>

For each item with this URI, an association can be made between that item and the channel with the same URI. In other words, you've just built a link between the channel in the RSS file and the items. The same approach applies for linking a channel to an image; you use the image element in the channel element, specifying the image URL as the value of the rdf:resource attribute. You should then define an image element, not within the channel element, supplying a URL, description, and link. Finally, use the rdf:about attribute (as in the item element) to specify the same URL as provided in the channel's image element. Did you follow all of that? This is all quite a bit different from RSS 0.9 and 0.91 (covered in the first edition of this book), so you'll need to be careful not to get things mixed up between the older specification and this newer one.

It is simple enough to create RSS files programmatically; the procedure is similar to how you generated the HTML for the mytechbooks.com web site. Half of the RSS file (the information about the channel as well as the image information) is static content; only the item elements must be generated dynamically. However, just as you were getting ready to open up vi and start creating another XSL stylesheet, another requirement was dropped into your lap: the machine that will house the RSS channel is a different server than that used in our last example, and has only very outdated versions of the Apache Xalan libraries available. Because of some of the high-availability applications that also run on that machine, such as the billing system, mytechbooks.com does not want to update those libraries until change control can be stepped through, a weeklong process. However, mytechbooks.com does have newer versions of the Xerces libraries available (as XML parsing is used in the billing system), so Java APIs for handling XML are available.[24] In this example, I use JDOM to convert the XML from the Foobar Public Library into an RSS channel format. Example 14-7 does just this.

[24] Yes, this is a bit of a silly case, and perhaps not so likely to really occur. However, it does afford me the opportunity to look at another alternative for creating XML programmatically. Don't sneer too much at the absurdity of the example; all of the examples in this book, including the silly ones, stem from actual experiences consulting for real-world companies. Laughing at this scenario might mean your next project has the same silly requirements!

Example 14-7. Java servlet to convert new book listings into an RSS channel document

package com.techbooks;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import javax.servlet.*;
import javax.servlet.http.*;

// JDOM
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class GetRSSChannelServlet extends HttpServlet {

    /** Host to connect to for books list */
    private static final String hostname = "newInstance.com";
    /** Port number to connect to for books list */
    private static final int portNumber = 80;
    /** File to request (URI path) for books list */
    private static final String file = "/cgi/supplyBooks.pl";

    public void service(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        res.setContentType("text/plain");
        PrintWriter out = res.getWriter();

        // Connect and get XML listing of books
        URL getBooksURL = new URL("http", hostname, portNumber, file);
        InputStream in = getBooksURL.openStream();

        try {
            // Request SAX Implementation and use default parser
            SAXBuilder builder = new SAXBuilder();

            // Create the document
            Document doc = builder.build(in);

            // Output XML
            out.println(generateRSSContent(doc));

        } catch (JDOMException e) {
            out.println("Error: " + e.getMessage());
        } finally {
            out.close();
        }
    }

    /**
     * <p>
     * This will generate an RSS XML document using the supplied
     *   JDOM <code>Document</code>.
     * </p.
     *
     * @param doc <code>Document</code> to use for input.
     * @return <code>String</code> - RSS file to output.
     * @throws <code>JDOMException</code> when errors occur.
     */
    private String generateRSSContent(Document doc) throws JDOMException {
        StringBuffer rss = new StringBuffer();

        rss.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
           .append("<rdf:RDF ")
           .append("xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n")
           .append("         xmlns=\"http://purl.org/rss/1.0/\"\n")
           .append(">\n")
           .append(" <channel>\n")
           .append("  <title>mytechbooks.com New Listings</title>\n")
           .append("  <link>http://www.newInstance.com/javaxml2/techbooks")
           .append("</link>\n")
           .append("  <description>\n")
           .append("   Your online source for technical material, computers, \n")
           .append("   and computing books!\n")
           .append("  </description>\n\n")
           .append("  <image ")
           .append("rdf:resource=\"http://newInstance.com/javaxml2/logo.gif\"")
           .append(" />\n\n")
           .append("  <items>\n")
           .append("   <rdf:Seq>\n")
           .append("    <rdf:li ")
           .append("resource=\"http://www.newInstance.com/javaxml2/techbooks\"")
           .append(" />\n")
           .append("   </rdf:Seq>\n")
           .append("  </items>\n")
           .append(" </channel>\n\n")
           .append("  <image ")
           .append("rdf:about=\"http://newInstance.com/javaxml2/logo.gif\">\n")
           .append("   <title>mytechbooks.com</title>\n")
           .append("   <url>http://newInstance.com/javaxml2/logo.gif</url>\n")
           .append("   <link>http://newInstance.com/javaxml2/techbooks</link>\n")
           .append("  </image>\n\n");

        // Add an item for each new title with Computers as subject
        List books = doc.getRootElement().getChildren("book");
        for (Iterator i = books.iterator(); i.hasNext(); ) {
            Element book = (Element)i.next();
            if (book.getAttribute("subject")
                    .getValue()
                     .equals("Computers")) {
                // Output an item
                rss.append("<item rdf:about=\"http://www.newInstance.com/")
                   .append("javaxml2/techbooks\">\n")
                    // Add title
                   .append(" <title>")
                   .append(book.getChild("title").getContent())
                   .append("</title>\n")
                    // Add link to buy book
                   .append(" <link>")
                   .append("http://newInstance.com/javaxml2")
                   .append("/techbooks/buy.xsp?isbn=")
                   .append(book.getChild("saleDetails")
                               .getChild("isbn")
                               .getContent())
                   .append("</link>\n")
                   .append(" <description>")
                    // Add description
                   .append(book.getChild("description").getContent())
                   .append("</description>\n")
                   .append("</item>\n");

            }
        }

        rss. append("</rdf:RDF>");

        return rss.toString();
    }
}

By this time, nothing in this code should be the least bit surprising to you; I've imported the JDOM and I/O classes needed, and accessed the Foobar Public Library application as in the ListBooksServlet. The resulting InputStream is used to create a JDOM Document, with the default parser (Apache Xerces) and the JDOM builder based on SAX doing the work.

Then, the JDOM Document is handed off to the generateRSSContentMethod() , which prints out all of the static content for the RSS channel. This method then obtains the book elements within the XML from the library and iterates through them, ignoring those without a subject attribute equal to "Computers".

NOTE: Again, I've done some rather different things simply for illustrative purposes. For example, this code directly outputs XML; you could just as easily create a JDOM tree and output it using XMLOutputter. Of course, you could also use DOM for the entire servlet. All these are viable and perfectly legitimate options.

Finally, each element that makes it through the comparison is added to the RSS channel. Nothing very exciting here, right? Figure 14-5 shows a sample output from accessing this servlet, saved as GetRSSChannelServlet.java, through a web browser.

Figure 14-5. RSS channel generated by the GetRSSChannelServlet

With this RSS channel ready for use, mytechbooks.com has made its content available by any service provider that supports RSS! To get the ball rolling on allowing clients to use its channel, mytechbooks.com would like to ensure its RSS document is valid, and see a sample HTML rendering of it (as would you, I imagine).

14.3.3. Taking a Test Drive

At this point, let's see this thing in action. Point your browser at http://www.redland.opensource.ac.uk/rss. This site has a nice, online test tool called the Redland RSS viewer that will take your RSS channel and validate it, as well as render it in HTML. You'll need to ensure that the RSS feed is available online somewhere, such as through the servlet just discussed. Enter in the URL for the servlet, or RSS feed, and then select "Yes" for the "Format output as a box" option. This instructs the viewer to render your channel as an HTML box, much like it could be seen on Netscape's Netcenter or on http://www.oreilly.com, which has several RSS feeds running. The output from the feed we just created is shown in Figure 14-6.

Figure 14-6. RSS formatted in HTM

You can also select several other RSS feeds from the viewer, and see how they would look formatted in HTML as well. The Meerkat channel is particularly interesting, as it contains almost all of the RSS options that are currently available for use. Additionally, if you have any errors in your RSS, this viewer will let you know what they are, which is helpful in debugging problems before putting your RSS channel into production.

I'm not including code that parses and formats the RSS in this chapter; in addition to it being a piece of cake for you by now, each site will need to provide different formatting for RSS feeds. For some fairly diverse views of RSS channels, you should check out http://www.servlets.com (down at the bottom right), http://www.oreilly.com, and http://www.xml.com, all of which have some pretty different formatting going on. In reading in an RSS channel, you'll probably want to treat it like XML, and use SAX, DOM, or JDOM to read the data in and format it however you need. In other words, there's nothing that requires you to treat an RSS feed any differently from any other XML document; you just know what the formatting will look like ahead of time. With that knowledge, you're ready to use RSS feeds in your own web sites.

14.3.4. What Happened to Netcenter?

Readers of the first edition may be curious as to where the section on viewing the RSS channel in Netscape Netcenter went. If you haven't seen that edition, Netscape provided a means of publishing your RSS feeds as channels capable of being added to Netscape's my.netscape.com site. This was cool, as you could easily format and view your RSS feeds in a slick-looking Netscape interface. However, when RSS 1.0 came out, replacing RS 0.91, Netscape removed all of its publishing links, and no longer allows this sort of thing to go on. So, if you're looking for a way to use your customized channels in your Netcenter homepage, you're out of luck.

You've gotten a good taste of RSS; most of all, you are probably realizing that like SOAP and web services, there are a ton of ways to interact with RSS feeds as well. Customize the examples and concepts in this chapter for your own applications, providing formatting and business logic specific to your business needs. Now you've got a better idea of how to do that, and will have more time for playing that guitar at night (um, that's me. . . well, you get the idea).

Previous | Home | Next
14.2. mytechbooks.com | Book Index | 14.4. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

14.4. What's Next?

You've seen the various techniques for interoperating, such as publishing content for clients, using SOAP to talk to remote applications, and B2B communication. Now I refocus on code internals. In the next chapter, I spend some time talking about XML data binding, allowing you an easier way to work with XML documents and Java objects. I also discuss how this affects object persistence and configuration, two common tasks in enterprise programming.

Previous | Home | Next
14.3. Push Versus Pull | Book Index | 15. Data Binding
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Data Binding (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 15. Data Binding

Contents:

First Principles
Castor
Zeus
JAXB
What's Next?

I've tried to run the gamut in what I've covered so far, and even in the order that I've covered it. When I started talking about Java in Chapter 2, "Nuts and Bolts", you had complete control. SAX provided the thinnest of veneers over XML, and basically provided a framework with which to write lots of callbacks. When I moved to DOM and JDOM, you got quite a bit more help, albeit with some loss of power. These tree-based, in-memory models were more convenient, although you had to pay a small price in terms of performance. Moving on to JAXP, you were placed higher up the chain of command, but gained another level of abstraction. At that point, you still had some control, but were working strictly in XML.

Then I shifted gears, and we looked at web publishing frameworks, XML-RPC, SOAP, and business-to-business and web services. This was a far cry from the down-and-dirty approach taken in the first half of the book. While you had immensely more convenience and specialization, you lost a lot of control over how XML was handled, and sometimes didn't see XML at all (like in XML-RPC). This may have left you (as it left me) missing getting your hands dirty a bit. You can't make the subtle tweaks in an XML document's values when working with SOAP, for example, that you could when using SAX or JDOM. However, the convenience of WSDL was nice, and had advantages over typing an element's name wrong in DOM. In short, it left me wishing for a happy medium.

Data binding provides that medium, and I want to take you through the extreme ends of the spectrum before talking about it. In this chapter, I show you a way to get most of the power of a low-level API with most of the convenience of an XML-based application framework (like SOAP). This will probably be something almost everyone can use at some point, and that many can use for a variety of tasks. First, I cover the basic principles that make data binding work. This will be a general overview, as the various data binding packages all have their own specific methodologies. Once you have some grounding, I take you on a quick tour of the two open source data binding APIs, and then Sun's emerging API, JAXB. So, buckle up and dive in.

What About Quick? What About JATO? What About...?

Some of you may be disappointed (or even upset) that I haven't covered your favorite API. However, I didn't make my decisions about which APIs to cover arbitrarily. I chose the two open source APIs that were compatible with JAXB and used the same principles. I selected open source APIs because they are free, and you can use them today; I don't expect you to shell out thousands of dollars for a product like Breeze XML Studio to run my examples[25]. And I selected Castor and Zeus because they use XML standards like XML Schema and DTDs for constraints.

If you're looking for coverage of JXQuick, you won't find it here. The schemas used by JXQuick to represent document constraints and Java classes (QIML, QJML, etc.) are not XML standards, and therefore much less useful in communicating information about your XML to other applications. For this reason, I haven't included it; you can find out more, though, online at http://quickutil.sourceforge.net/view/Main/JXQuick.

The same goes for JATO, located online at http://sourceforge.net/projects/jato, which is more of a scripting language for XML and Java mappings. Because this is less data binding, and more data mapping, it didn't fit well into this chapter or into a JAXB model of doing things. You can check both of these packages out online and make your own decisions. I always recommend you use what's best for your project.

[25]That's not meant to be a knock on Breeze, by the way. I just generally think you should use something that's free rather than the same something you have to pay for. If you want to try a commercial offering, go gor it.

15.1. First Principles

Before diving into specific packages and frameworks that handle data binding, you need to have a basic understanding of what XML data binding is. This turns out to be pretty simple, though, so you'll be coding in no time. First, take a plain old XML document, like the one shown in Example 15-1.

Example 15-1. Homespun tapes XML catalog

<?xml version="1.0"?>

<catalog xmlns="http://www.homespuntapes.com">
  <item id="VD-DOK-GT01" level="4">
    <title>Doc's Guitar Fingerpicking and Flatpicking</title>
    <teacher>Doc Watson</teacher>
    <guest>Pete Seeger</guest>
    <guest>Mike Seeger</guest>
    <guest>Jack Lawrence</guest>
    <guest>Kirk Sutphin</guest>
    <description>Doc Watson, a true master of traditional guitar styles, teaches,
      in detail, some of the most reuested fingerpicking and flatpicking tunes in
      his vast repertoire, for guitarists at all levels.</description>
  </item>
  <item id="VD-WLX-GT01" level="4">
    <title>The Guitar of David Wilcox</title>
    <teacher>David Wilcox</teacher>
    <description>Create fresh new guitar sounds with rich, ringing voicings! David
      even shows you how to invent your own tunings.</description>
  </item>
  <item id="VD-THI-MN01" level="3">
    <title>Essential Techniques for Mandolin</title>
    <teacher>Chris Thile</teacher>
    <description>Here's a lesson that will thrill and inspire mandolin players at
      all levels.</description>
  </item>
  <item id="CDZ-SM01" level="4">
    <title>Sam Bush Teaches Mandolin Repertoire and Techniques</title>
    <teacher>Sam Bush</teacher>
    <description>Learn complete solos to eight traditional and orignal tunes, each
      one jam-packed with licks, runs, and musical variations.</description>
  </item>
</catalog>

In previous chapters, you learned how to use SAX, DOM, JDOM, and JAXP to access this document. You could manipulate both its structure (the names and ordering of elements, attributes, and other lexical constructs) and its content (the actual data). However, many times you don't need access to the document's structure, and only want to work with its data.

In this latter case, it's overkill and a bit on the annoying side to have to write code that parses your document, extracts the data, and puts it into some format that you can use. It would be much nicer to run a program (or API. . . are you starting to get the picture here?) that did this for you, and produced usable Java classes. In fact, this is exactly what data binding does. With data binding, there are three distinct processes that can occur one after another, in differing order, or in completely unrelated processes. I'll cover each in turn.

15.1.1. Class Generation

The first process, class generation, provides a means to convert an XML document to Java. When data binding converts an XML document into a Java representation, it seeks to provide access to just the data in the document. Additionally, data binding provides some level of meaning to the information in the document. It does this by creating the Java representation with accessor and mutator[26] methods like getItem( ) and setTeacher( ), instead of getElement( ) and setAttribute( ). This makes dealing with documents like the one in Example 15-1 less about Java and more about business logic, which is obviously a good thing. However, these wonderfully Java classes must exist before an XML document can be turned into an instance of one, so I'm back to class generation.

[26]When I say "accessor," I'm referring to what most people call a "getter" method; when I say "mutator," I'm referring to what most people call a "setter" method. However, I also know that a "setter" is a dog, not a Java method, so I'm quick to tell my students not to use that term. Just an idiosyncrasy, I suppose!

Class generation is the process of taking a set of XML constraints and generating Java classes (and possibly interfaces) from those constraints. Think about it this way: XML constraints (like those found in a DTD or XML Schema) are equivalent to Java class definitions. They define the way that data is represented. On the other hand, an XML document is equivalent to an instance of these classes, in that it is simply a set of data that fulfills the contract defined by the document's constraints. Now, read this paragraph again, slowly, and you'll have it.

The data binding frameworks I talk about in this chapter all have a way of representing a document's constraints (usually through a DTD or an XML Schema, but there are also some other options, which I'll get to in the appropriate section). These constraints can then be run through some form of a class generation tool, and you get Java source code ready to compile. This code, once compiled, can be used to generate instance data based on an XML document. You end up with a process akin to that shown in Figure 15-1.

Figure 15-1. Class generation in XML data binding

Note that the final product here can be concrete classes, interfaces, interfaces and implementations, or any other permutation of Java classes. In the case of Example 15-1 (assuming that the constraints are represented in some arbitrary form), you might end up with a Catalog interface like this:

public interface Catalog {
    public List getItemList( );
    public void addItem(Item item);
    public void setItemList(List items);
}

Further, you might have an Item interface like this:

public interface Item {
    public String getID( );
    public void setID(String id);
    public int getLevel( );
    public void setLevel(int level);

    public String getTitle( );
    public void setTitle(String title);
    public String getTeacher( );
    public void setTeacher(String teacher);
    public List getGuests( );
    public void addGuest(String guest);
    public void setGuestList(List guests);
    public String getDescription( );
    public void setDescription( );
}

This is quite a bit more useful than writing hundreds of lines of SAX callbacks. It makes working with the document a piece of cake, instead of an exercise in your Java and XML API skills. These are just examples, and not necessarily representative of what you'll get using the APIs covered in this chapter. However, in the following sections, I'll show you exactly how to use the API and let you know what to expect.

15.1.2. Unmarshalling

Keep in mind that once you have a set of generated classes, you still don't have a great many uses for them. Sure, you could use an existing lower-level XML API to read in an XML document, pull out the data, create new instances of the generated classes, and populate them with the data from the XML. But data binding provides all this out of the box, so why bother with that? In fact, data binding frameworks provide for exactly that process. And in that context, unmarshalling is the process of converting an XML document into an instance of a Java class.

NOTE: I've seen, and even been a part of, a lot of confusion about the marshalling/unmarshalling terminology. I'm using the terminology as defined in Sun's latest version of the Java Architecture for Data Binding (JAXB) specification, which is certain to become the standardized vocabulary. In that specification, marshalling is moving from Java to XML, and unmarshalling is moving from XML to Java. I'd stick with those definitions if I were you, as well.

This turns out to be a pretty simple process; you get an XML document, pass it to some tool or class instance in your data binding framework, and you get back a Java object. This is usually a class instance of the top-level Java object representing your document. So, again using Example 15-1, you would get back an instance of the Catalog class. You'll typically need to cast from a java.lang.Object to the specific class that you're expecting, since the framework won't know anything about your classes (because they were generated). But after the class cast, you're ready to work with the object as a Catalog, not as an XML document. You can then use the various accessor and mutator methods to work with the data, and when you are ready to send the document back to the XML from whence it came, you marshal the document.

15.1.3. Marshalling

Marshalling is just the opposite of unmarshalling. It is the process of converting a Java object and its dependent objects into an XML representation. In many cases, marshalling is part of a repeated cycle of transformations to and from XML, and is paired with unmarshalling. As an example, check out Figure 15-2, which is a typical application flow.

Figure 15-2. XML data binding application flow

There are two distinct ways to marshal a Java object. The first is to invoke a marshal( ) method on an object; this method is usually generated along with the accessors and mutators during class generation. The method recursively calls the marshal( ) method on each of its dependent objects, until you end up with an XML document. Note that the target XML document does not have to be the same as the original XML; you can easily end up with a vast number of archived XML documents by supplying different filenames to the marshalling process.

A different approach to marshalling, and the one I favor, is having a standalone class that performs marshalling. Instead of invoking marshal( ) on a generated object, you invoke marshal( ) on the standalone class, and pass it the object to marshal. This is useful because it performs the same tasks as illustrated previously, but also allows classes that were not originally unmarshalled from XML to be converted to XML. Think about it this way: data binding, used like this, becomes a persistence framework. Any object with bean-like properties (setXXX( ) and getXXX( )) can easily be converted to XML! You get the power of data binding with the flexibility of persistence. This is a handy combination, and supported by some of the frameworks I talk about in this chapter.

I realize that if you're new to data binding, this may sound a bit confusing and vague; sort of like talking about chemistry. I'd much rather blow some things up (err. . . you know!), so in the rest of the chapter I show you how to use some data binding frameworks. Since I'm going to cover four of these, none of the examples are immensely complex; instead I focus on how to use the class generation, marshalling, and unmarshalling capabilities of each framework. That should be more than enough to get you going.

15.1.4. Use Cases

As a final explanation of data binding and why it's worthwhile, I will give you a small sampling of use-cases. Some of these are best suited for the lower-level APIs like SAX or DOM, and some are perfect for data binding. In Table 15-1, I've listed a common use case, the type of API that would work well, and a short reasoning behind my decision. This should help you see how data binding fits into the XML landscape.

Table 15-1. API use cases

Use case | Well-suited API | Reasoning
XML IDE | DOM or JDOM | Tree-based viewing of XML, as in an IDE, closely follows the tree-based models of DOM and JDOM.
XML messaging server | SAX | Since speed is the most important factor, SAX allows the fastest stream-based reading of messages.
Configuration data | Data binding | The content, rather than the model, is paramount. Data binding saves time and adds convenience to reading configuration data.
XML transformations | DOM or JDOM | Changing both structure and content requires modification of content (rules out SAX) and structure (rules out data binding).
XML messaging client | Data binding | When the message format is known ahead of time, the client can benefit from easy Java objects, as created by data binding.

Obviously, these are just a few common XML applications, but they should give you an idea of when to use a low-level API and when to use a higher-level one.

Previous | Home | Next
14.4. What's Next? | Book Index | 15.2. Castor
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Castor (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

15.2. Castor

The first data binding framework I will discuss is Castor, hosted online at http://castor.exolab.org. This framework has been around for quite a while, and the latest release as of this writing was Version 0.92. First, it should be made clear that Castor provides quite a bit more than just XML data binding. The package provides bindings in Java for more than just XML; you can also work with LDAP objects, OQL for mapping SQL queries to objects, as well as Java Data Objects (JDO), a fairly new specification from Sun dealing with Java-to-RDBMS (relational database management system) persistence. However, this is an XML book, so I'm only going to talk about the XML bindings.

15.2.1. Installation

To get ready to use Castor, you'll need to download a release from the download page: http://castor.exolab.org/download.html. That page has links to the Exolab FTP site (or you can FTP in manually, as I did), and lists the files available. I'd recommend getting the full release (in the event you want to play with OQL or JDO later), named castor-0.9.2.zip or castor-0.9.2.tgz. Extract the jar files from that archive, add them to your classpath, and you're ready to go.[27]

[27]Actually, there are two jar files within the distribution: castor-0.9.2.jar and castor-0.9.2-xml.jar. The first is a superset of the second, so you only need the first; or if you want a smaller archive, you can just use the second for XML binding.

NOTE: In this and subsequent examples, I've assumed that you still have a SAX-compliant XML parser, like Xerces, on your classpath in addition to the libraries discussed in this chapter. If you don't, add xerces.jar or your parser's jar file(s) to the classpath in addition to the data binding framework you are using.

15.2.2. Source Generation

Castor does provide for class generation, using an existing set of constraints to create Java classes. You will need to have an XML Schema that constrains your XML to use this. Example 15-2 provides just such a schema for the document I showed you earlier in Example 15-1.

Example 15-2. XML Schema for Example 15-1 (using Castor)

<?xml version="1.0"?>

<schema xmlns="http://www.w3.org/2000/10/XMLSchema"
        targetNamespace="http://www.homespuntapes.com">
  <element name="catalog">
    <complexType>
      <sequence>
        <element ref="item" minOccurs="1" maxOccurs="unbounded" />
      </sequence>
    </complexType>
  </element>

  <element name="item">
    <complexType>
      <sequence>
        <element name="title" type="string" />
        <element name="teacher" type="string" />
        <element name="guest" type="string" minOccurs="0" maxOccurs="unbounded" />
        <element name="description" type="string" />
      </sequence>
      <attribute name="id" type="string" />
      <attribute name="level">
        <simpleType>
          <restriction base="integer">
            <enumeration value="1" />
            <enumeration value="2" />
            <enumeration value="3" />
            <enumeration value="4" />
            <enumeration value="5" />
          </restriction>
        </simpleType>
      </attribute>
    </complexType>
  </element>
</schema>

Obviously, you may have XML schemas of your own that you want to try out; as long as they conform to the XML Schema specification, they should work with any examples in this section.

WARNING: At least as of this writing, Castor supported only the October 2000 XML Schema Candidate Recommendation, as opposed to the final version of that specification. This could require you to make some small modifications in your existing schemas to conform to that earlier specification if you're using Castor. Hopefully that framework will have caught up by the time you are reading this; you can verify the current level of compliance for XML Schema at http://castor.exolab.org/xmlschema.html.

Once you have your XML Schema defined, you are ready to generate classes for the constraints. I've named my schema from Example 15-1 catalog.xsd, as you'll see reflected in the example instructions coming up.

Once you've got your schema, generating classes with Castor is a piece of cake. You'll need to use the org.exolab.castor.builder.SourceGenerator class, as shown here:

java org.exolab.castor.builder.SourceGenerator -i castor/catalog.xsd
    -package javaxml2.castor

In this example, I'm running the command with my schema in a subdirectory of the current directory, called castor/. I specified the schema with the "-i" flag, and the package to generate the files within through the "-package" flag. There's a whole slew of other options you can check out by simply entering the class without any options. The class will spit out the various flags and options you can supply.

Once the command executes (you'll get errors if your schema has problems), you will get a directory path correlating to the package you entered. In my example, I ended up with a javaxml2 directory, and a castor directory within that. Within that directory, I ended up with a Catalog.java and CatalogDescriptor.java source file, and an Item.java and ItemDescriptor.java source file. For most situations, you'll only need to worry about working with the first of each of these pairs.

You should also get a subdirectory called types, with some additional files within it. These are generated because of the user-defined type in the XML Schema for the "level" attribute. The result is a class called LevelType. Since there are only five allowed values, Castor must create custom classes for this type to handle it. These type classes are a pain to work with, as there is no way, for example, to do this:

// Create a new type with a value of "1"
LevelType levelType = new LevelType(1);

Instead, you'll need to get the value you want to use and convert it to a String. You can then use the valueOf( ) method, which is static, to get an instance of LevelType with the correct value:

LevelType levelType = LevelType.valueOf("1");

Of course, once you get used to this, it's not such a big deal. If this seems a little fuzzy, you'll see how to use this class in a practical situation in the next section, so don't worry too much about it just yet. You can compile the type files, as well as the other Castor-generated sources, with this simple command:

javac -d . javaxml2/castor/*.java javaxml2/castor/types/*.java

At this point, you have classes that are ready to use. I won't show you the source for these files here, because it's quite long (and you can look at it yourself). I've listed the key methods for the Catalog class, though, so you'll get an idea of what to expect:

package javaxml2.castor;

public class Catalog {

    // Add a new Item
    public void addItem( );
    // Get the items as an Enumeration
    public Enumeration enumerateItem( );
    // Get all items
    public Item[] getItem( );
    // Get number of items
    public getItemCount( );
}

Notice that you can add items, as well as move through the items available. The names of two of these methods, enumerateItem( ) and getItem( ) , are a bit odd, so watch out for those. I did not expect getItem( ) to return an array, and looked for getItems( ) or getItemList( ) first, myself. Once you've got these generated classes, though, you're ready to use them in your application.

15.2.3. Marshalling and Unmarshalling

After you've compiled the classes Castor generates, add them to your classpath. You can then use them in your own applications. Example 15-3 shows a basic HTML form that allows a user to enter information about a new item.

Example 15-3. HTML form for adding items

<HTML>
 <HEAD><TITLE>Add New Item to Catalog</TITLE></HEAD>
 <BODY>
  <H2 ALIGN="CENTER">Add New Item</H2>
  <P ALIGN="CENTER">
   <FORM ACTION="/javaxml2/servlet/javaxml2.AddItemServlet" METHOD="POST">
    <TABLE WIDTH="80%" CELLSPACING="3" CELLPADDING="3" BORDER="3">
     <TR>
      <TD WIDTH="50%" ALIGN="right"><B>Item ID:</B></TD>
      <TD><INPUT TYPE="text" NAME="id" /></TD>
     </TR>
     <TR>
      <TD WIDTH="50%" ALIGN="right"><B>Item Level:</B></TD>
      <TD><INPUT TYPE="text" NAME="level" SIZE="1" MAXLENGTH="1" /></TD>
     </TR>
     <TR>
      <TD WIDTH="50%" ALIGN="right"><B>Title:</B></TD>
      <TD><INPUT TYPE="text" NAME="title" SIZE="20" /></TD>
     </TR>
     <TR>
      <TD WIDTH="50%" ALIGN="right"><B>Teacher:</B></B></TD>
      <TD><INPUT TYPE="text" NAME="teacher" /></TD>
     </TR>
     <TR><TD COLSPAN="2" ALIGN="CENTER"><B>Guests:</B></TD></TR>
     <TR>
      <TD COLSPAN="2" ALIGN="CENTER"><INPUT TYPE="text" NAME="guest" /></TD>
     </TR>
     <TR>
      <TD COLSPAN="2" ALIGN="CENTER"><INPUT TYPE="text" NAME="guest" /></TD>
     </TR>
     <TR>
      <TD COLSPAN="2" ALIGN="CENTER"><INPUT TYPE="text" NAME="guest" /></TD>
     </TR>
     <TR>
      <TD COLSPAN="2" ALIGN="CENTER"><INPUT TYPE="text" NAME="guest" /></TD>
     </TR>
     <TR><TD COLSPAN="2" ALIGN="CENTER"><B>Description:</B></TD></TR>
     <TR>
      <TD COLSPAN="2" ALIGN="CENTER">
       <TEXTAREA NAME="description" COLS="30" ROWS="10"></TEXTAREA>
      </TD>
     </TR>
     <TR>
      <TD COLSPAN="2" ALIGN="CENTER"><INPUT TYPE="submit" value="Add Item" /></TD>
     </TR>
    </TABLE>
   </FORM>
  </P>
 </BODY>
</HTML>

You should have Tomcat or another servlet engine running from some of the previous chapters, so I won't get into those details. Drop this form into one of your web applications, and then enter in and compile the servlet shown in Example 15-4.

Example 15-4. The AddItemServlet for Castor

package javaxml2;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Castor classes
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;

// Castor generated classes
import javaxml2.castor.Catalog;
import javaxml2.castor.Item;
import javaxml2.castor.types.LevelType;

public class AddItemServlet extends HttpServlet {

    private static final String CATALOG_FILE =
        "c:\\java\\tomcat\\webapps\\javaxml2\\catalog.xml";

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        PrintWriter out = res.getWriter( );
        res.setContentType("text/html");

        // Get input parameters
        String id = req.getParameterValues("id")[0];
        String levelString = req.getParameterValues("level")[0];
        String title = req.getParameterValues("title")[0];
        String teacher = req.getParameterValues("teacher")[0];
        String[] guests = req.getParameterValues("guest");
        String description = req.getParameterValues("description")[0];

        // Create new item
        Item item = new Item( );
        item.setId(id);
        item.setLevel(LevelType.valueOf(levelString));
        item.setTitle(title);
        item.setTeacher(teacher);
        if (guests != null) {
            for (int i=0; i<guests.length; i++) {
                if (!guests[i].trim( ).equals("")) {
                    item.addGuest(guests[i]);
                }
            }
        }
        item.setDescription(description);

        try {
            // Load current catalog
            File catalogFile = new File(CATALOG_FILE);
            FileReader reader = new FileReader(catalogFile);
            Catalog catalog =
                (Catalog)Unmarshaller.unmarshal(Catalog.class, reader);

            // Add item
            catalog.addItem(item);

            // Write back out modified catalog
            FileWriter writer = new FileWriter(catalogFile);
            Marshaller.marshal(catalog, writer);

            out.println("Item added.");
        } catch (Exception e) {
            out.println("Error loading/writing catalog: " + e.getMessage( ));
        } finally {
            out.close( );
        }
    }

    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        doPost(req, res);
    }
}

This servlet accepts the parameters from the form shown in Example 15-3. It first reads in the XML representing the current catalog (called catalog.xml and also in my servlet's web application folder). At this point, the servlet needs to access the current catalog; of course, I could write a bunch of SAX code here, but why? Castor does the job nicely. I use a FileReader to provide read access to the XML document, and a FileWriter to provide write access. The rest of the work is taken care of by Castor. Once the servlet gets the form values, it creates a new Item instance (using Castor's generated classes) and sets the various values on this class. You'll notice that because "level" is a custom type (remember the earlier discussion?), the servlet uses the static method LevelType.valueOf(String) to convert the String value for the item level into the correct instance of the LevelType class. This is one of Castor's minor drawbacks; the custom classes for user-defined types are a bit unwieldy until you get used to them.

Once the servlet has a ready-to-use instance of a new Item, it uses the org.exolab.castor.Unmarshaller class to get the current catalog. This couldn't be much simpler; the servlet passes in the class to unmarshal to, and access to the file (through the FileReader I just mentioned). The result is a Java Object, which can be cast to the class type supplied. At this point, adding the item is a piece of cake! You're working in Java, rather than in XML, and can simply invoke addItem( ) and pass in the newly created Item instance. Then, the process is reversed. Marshaller (in the same package as its sister Unmarshaller) is used via the static marshal( ) method to write the Catalog instance back to XML, using a FileWriter. Piece of cake, isn't it? Once this process completes, you get a new entry in the XML file (you may have to stop your servlet engine to get access to it), which should look something like this:

<item id="CD-KAU-PV99" level="1">
  <title>Parking Lot Pickers, Vol. 3</title>
  <teacher>Steve Kaufman</teacher>
  <guest>Debbie Barbra</guest>
  <guest>Donnie Barbra</guest>
  <description>This video teaches you what to play when the
   singing stops, bluegrass style!</description>
 </item>

And that's really it. There are quite a few more options to use with the Marshaller and Unmarshaller, so check out the API documentation. And, just to make sure you can follow along, here are the pertinent files and directories in my web application that make this all work:

$TOCAT_HOME/lib/xerces.jar
$TOMCAT_HOME/webapps/javaxml2/addItem.html
$TOMCAT_HOME/webapps/javaxml2/catalog.xml
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/lib/castor-0.9.2.jar
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/AddItemServlet.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/Catalog.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/Catalog.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    CatalogDescriptor.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    CatalogDescriptor$1.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/Item.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    ItemDescriptor.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    ItemDescriptor$1.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    ItemDescriptor$2.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    ItemDescriptor$3.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    ItemDescriptor$4.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    ItemDescriptor$5.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/
    ItemDescriptor$6.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/types/
    LevelType.class
$TOMCAT_HOME/webapps/javaxml2/WEB-INF/classes/javaxml2/castor/types/
    LevelTypeDescriptor.class

There's a lot more to Castor, as there will be to each of the packages I talk about. This short introduction should get you started, and the documentation provided will help you through the rest. Of particular interest is the ability to define mappings that allow you, for example, to convert an XML element named "item" to a Java variable named "inventory". This allows for different representations of the same data within Java and XML, and can also really help you convert between legacy Java classes. Imagine converting an old Java class to a new XML format, and then unmarshalling that new XML back into new Java classes. In two easy steps, all of your old Java code is updated to a new format! Pretty slick, huh? On the other hand, Castor's lack of support for the latest schema recommendation and generation of concrete classes instead of interfaces is a drawback. Try it out for yourself, though, and see how you like it. Each of the frameworks in this chapter has pros and cons, so pick the one that's right for you.

Previous | Home | Next
15. Data Binding | Book Index | 15.3. Zeus
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Zeus (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

15.3. Zeus

Next up is the Zeus data binding framework. This is a project over at Enhydra (http://www.enhydra.org), and is based on a series of articles I originally wrote for the folks at IBM's Developer Works (http://www.ibm.com/developerWorks). It was the direct result of my need for a simple solution for data binding (at the time, Castor seemed overly complex for a fairly straightforward data binding problem). Since then, it's developed into a complete project at Enhydra, and several other folks are working on it. You can get the complete story by checking out http://zeus.enhydra.org. I'll touch on the same things I dealt with in Castor, so you can use either competently in your applications.

15.3.1. Installation

Zeus is still in the fairly early stages of functional development (it's been around a bit, but some serious architecture is still going on). As a result, I'd recommend you get the latest version from CVS instead of downloading a binary. That way, you'll be sure to get the latest and greatest in features. You can find complete instructions on getting Zeus from CVS at the web site, but in short, you'll need to get CVS (from http://www.cvshome.org, for example). Simply use the following command:

cvs -d :pserver:anoncvs@enhydra.org:/u/cvs login

Enter "anoncvs" as the password. Then:

cvs -d :pserver:anoncvs@enhydra.org:/u/cvs co toolsTech/Zeus

You'll get everything you need. Change into the Zeus directory (which is created by the CVS download), and build the binaries:

bmclaugh@GANDALF ~/dev/Zeus
$ ./build.sh

Or, on Windows:

c:\dev\Zeus> build.bat

You should also build the samples, which you can do by appending "samples" as an argument to the build command:

bmclaugh@GANDALF ~/dev/Zeus
$ ./build.sh samples

At that point, you'll end up with zeus.jar in the build/ directory. Add this to your classpath, as well as jdom.jar and xerces.jar, both of which are in the lib directory. Finally, if you're going to use DTD generation, you should also add dtdparser113.jar to the classpath. And, to use the samples, add the build/classes directory itself, just to make things really easy. So, my classpath looks like this:

bmclaugh@GANDALF ~/dev/Zeus
$ echo $CLASSPATH
 /dev/Zeus/lib/jdom.jar:/dev/Zeus/lib/xerces.jar:/dev/Zeus/lib/dtdparser113.jar:
 /dev/Zeus/build/zeus.jar:/dev/Zeus/build/classes

Or, on Windows:

c:\dev\Zeus> echo %CLASSPATH%
 c:\dev\Zeus\lib\jdom.jar;c:\dev\Zeus\lib\xerces.jar;
 c:\dev\Zeus\lib\dtdparser113.jar;c:\dev\Zeus\build\zeus.jar;
 c:\dev\Zeus\build\classes

That's it. With your classpath set, and the XML from the earlier portions of the chapter, you're ready to go.

15.3.2. Class Generation

The primary difference between Zeus and frameworks like Castor and even JAXB, Sun's offering, is the way that class generation is handled. Remember from Figure 15-1 that the standard means of handling class generation is taking a set of constraints, reading through them, and streaming out Java source code. While this is handy, it makes it difficult to add support for other types of constraints, like DTDs or newer schema-alternatives such as Relaxx and TREX. To accommodate this, Zeus adds an intermediary step. What occurs is that a set of constraints (in schema form, DTD form, etc.) is converted into a set of Zeus bindings. These bindings are not tied to any specific means of constraint representation. In other words, if you modeled a set of constraints within a DTD, and then modeled the exact set of constraints again within an XML Schema, Zeus would convert both into an identical set of bindings. These bindings are used to generate Java source code. The result is a process like that modeled in Figure 15-3.

Figure 15-3. The Zeus methodology of class generation

What's nice about this, and the reasoning behind it, is that adding support for a new method of constraints like TREX becomes a very simple process. You could write a class, implementing the org.enhydra.zeus.Binder interface, that takes in a set of constraints and creates Zeus bindings from them. Class generation is already handled, so you don't have to deal with hundreds of annoying out.write( ) type statements. Zeus comes prepackaged with two binders, DTDBinder and SchemaBinder, both in the org.enhydra.zeus.binder package.

Other than that architectural change (which doesn't affect you when using existing binders), Castor and Zeus operate similarly in generating classes. Example 15-5 shows a DTD that constrains the original XML document from Example 15-1. I'll demonstrate using class generation from DTDs with this example.

Example 15-5. DTD for Example 15-1

<!ELEMENT catalog (item+)>
<!ATTLIST catalog
          xmlns   CDATA    #FIXED    "http://www.homespuntapes.com"
>

<!ELEMENT item (title, teacher, guest*, description)>
<!ATTLIST item
          id      CDATA    #REQUIRED
          level   CDATA    #REQUIRED
>

<!ELEMENT title (#PCDATA)>
<!ELEMENT teacher (#PCDATA)>
<!ELEMENT guest (#PCDATA)>
<!ELEMENT description (#PCDATA)>

Zeus is set up to allow applications to call the source generator, and therefore does not provide a static entry point for source generation within the API. Because that's a common task, though, a class within the samples (remember running the build program with the "samples" argument?) does allow easy class generation. If your classpath is set up like I showed you previously, all you need to do is create a new directory called "output". The samples program puts generated classes in there by default (programmatically, you can change that option). So, with an output directory in place, run the following command:

C:\javaxml2\build>java samples.TestDTDBinder
    -file=c:\javaxml2\ch14\xml\zeus\catalog.dtd
    -package=javaxml2.zeus
    -quiet=true

I have spaced things out for clarity. I've specified the file to generate source from, the package to put the source within, and that generation should occur quietly (without spitting out any debugging information). Generation from a schema is very similar. Since Zeus uses the final version of the XML Schema recommendation, you need to use a newer version of the XML Schema shown back in the Castor section; Example 15-6 updates that schema to a current version.

Example 15-6. Updating the Castor XML Schema

<?xml version="1.0"?>

<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.homespuntapes.com">
  <element name="catalog">
    <complexType>
      <sequence>
        <element ref="item" minOccurs="1" maxOccurs="unbounded" />
      </sequence>
    </complexType>
  </element>

  <element name="item">
    <complexType>
      <sequence>
        <element name="title" type="string" />
        <element name="teacher" type="string" />
        <element name="guest" type="string" minOccurs="0" maxOccurs="unbounded" />
        <element name="description" type="string" />
      </sequence>
      <attribute name="id" type="string" />
      <attribute name="level">
        <simpleType>
          <restriction base="integer">
            <enumeration value="1" />
            <enumeration value="2" />
            <enumeration value="3" />
            <enumeration value="4" />
            <enumeration value="5" />
          </restriction>
        </simpleType>
      </attribute>
    </complexType>
  </element>
</schema>

Once you've got that, use the following command for class generation:

C:\javaxml2\build>java samples.TestSchemaBinder
    -file=c:\javaxml2\ch14\xml\zeus\catalog.xsd
    -package=javaxml2.zeus
    -quiet=true

You'll get identical output using either a DTD or a schema.

WARNING: This is a slight overstatement. Actually, you will get identical output if you can represent the constraints in the DTD and XML Schema identically. In this example, the schema defines "level" as an integer, while the DTD must define it as PCDATA. The result is that using a schema, you get a typed level (a Java int), and using a DTD, you get a String. I've used the DTD version throughout this section, to show you that option.

Within the output directory, you'll find a javaxml2 directory, and then a zeus directory (matching up to the package hierarchy). You can take a look at the source files generated within.

Right off, you should notice that Zeus produces an interface (for example, Catalog.java) and a default implementation of that interface (for example, CatalogImpl.java). The reasoning behind this, as opposed to the concrete classes that Castor generates, is that you may want to have your own custom classes, perhaps already in existence, that implement the Catalog interface. You can therefore use your own implementation, but get the benefits of class generation. I commonly see developers using Zeus to generate classes and then creating their own implementation of the generated interfaces as Enterprise JavaBeans (EJBs). These EJBs are then persisted using Zeus, which is a piece of cake (as I'll show you in the next section). Without providing for user-defined implementations, this behavior would require modifying the generated source code, which could break marshalling and unmarshalling.

Another notable difference you should be aware of is the method names. For example, here's the Item interface (trimmed down to its simplest form):

package javaxml2.zeus;

public interface Item {

    public Title getTitle( );
    public void setTitle(Title title);

    public Teacher getTeacher( );
    public void setTeacher(Teacher teacher);

    public java.util.List getGuestList( );
    public void setGuestList(java.util.List guestList);
    public void addGuest(Guest guest);
    public void removeGuest(Guest guest);

    public Description getDescription( );
    public void setDescription(Description description);

    public String getLevel( );
    public void setLevel(String level);

    public String getId( );
    public void setId(String id);
}

Notice that the JavaBeans naming is used, and elements that can appear multiple times are returned as Java 2 Collections classes. In any case, take a look at this source, and then compile it:

javac -d . output/javaxml2/zeus/*.java

Now you're ready to look at marshalling and unmarshalling using Zeus.

15.3.3. Marshalling and Unmarshalling

The process of marshalling and unmarshalling in Zeus is very similar to that in Castor. At the heart of the operations are two classes, org.enhydra.zeus.Marshaller and org.enhydra.zeus.Unmarshaller. Like Castor, the methods you will be interested in are marshal( ) and unmarshal( ), on their respective classes. However, there are some differences. First, the Marshaller and Unmarshaller classes in Zeus don't provide static entry points. This is intentional, reminding the user to set options like the package to unmarshal XML to, and whether or not there are methods that should be ignored in marshalling. Additionally, Zeus uses the JAXP style of input and output, providing data binding flavors of the Source and Result classes I talked about in Chapter 9, "JAXP". Like JAXP, Zeus provides some default flavors of these in the org.enhydra.zeus.source and org.enhydra.zeus.result packages. Even with these changes, the process turns out to be very similar in both frameworks:

File catalogFile = new File("catalog.xml");
FileReader reader = new FileReader(catalogFile);
FileWriter writer = new FileWriter(catalogFile);

// Castor: Unmarshalling
Catalog catalog =
    (Catalog)org.exolab.castor.xml.Unmarshaller.unmarshal(Catalog.class, reader);

// Zeus: Unmarshalling
StreamSource source = new StreamSource(reader);
org.enhydra.zeus.Unmarshaller unmarshaller = new org.enhydra.zeus.Unmarshaller( );
Catalog catalog = (Catalog)unmarshaller.unmarshal(source);

// Castor: Marshalling
org.exolab.castorMarshaller.marshal(catalog, writer);

// Zeus: Marshalling
StreamResult result = new StreamResult(writer);
org.enhydra.zeus.Marshaller marshaller = new org.enhydra.zeus.Marshaller( );
marshaller.marshal(catalog, result);

While there's a bit more typing involved with using Zeus, the methodology should seem a lot more familiar to JAXP users, which was the goal. It also allows input and output of SAX streams and DOM trees using the appropriate Source and Result implementations. As an example of Zeus in action, check out Example 15-7. This program reads in the catalog supplied as the first argument on the command line, prints out the item names and IDs, modifies an existing item, and then marshals the catalog back to XML. There's very little user interaction here, but you should be able to see how Zeus works.

Example 15-7. Using Zeus for catalog manipulation

package javaxml2;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

// Zeus classes
import org.enhydra.zeus.Marshaller;
import org.enhydra.zeus.Unmarshaller;
import org.enhydra.zeus.source.StreamSource;
import org.enhydra.zeus.result.StreamResult;

// Zeus generated classes
import javaxml2.zeus.Catalog;
import javaxml2.zeus.Guest;
import javaxml2.zeus.GuestImpl;
import javaxml2.zeus.Item;
import javaxml2.zeus.ItemImpl;

public class CatalogViewer {

    public void view(File catalogFile) throws IOException {
        FileReader reader = new FileReader(catalogFile);
        StreamSource source = new StreamSource(reader);

        // Convert from XML to Java
        Unmarshaller unmarshaller = new Unmarshaller( );
        unmarshaller.setJavaPackage("javaxml2.zeus");
        Catalog catalog = (Catalog)unmarshaller.unmarshal(source);

        List items = catalog.getItemList( );
        for (Iterator i = items.iterator(); i.hasNext( ); ) {
            Item item = (Item)i.next( );
            String id = item.getId( );
            System.out.println("Item ID: " + id);
            String title = item.getTitle().getValue( );
            System.out.println("Item Title: " + title);

            // Modify an item
            if (id.equals("CDZ-SM01")) {
                item.getTitle( ).setValue("Sam Bush Teaches Mandolin " +
                    "Repertoire and Technique, 2nd edition");
                Guest guest = new GuestImpl( );
                guest.setValue("Bela Fleck");
                item.addGuest(guest);
            }
        }

        // Write back out
        FileWriter writer = new FileWriter(new File("newCatalog.xml"));
        StreamResult result = new StreamResult(writer);
        Marshaller marshaller = new Marshaller( );
        marshaller.marshal(catalog, result);
    }

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.out.println("Usage: java javaxml2.CatalogViewer " +
                    "[XML Catalog Filename]");
                return;
            }

            // Get access to XML catalog
            File catalogFile = new File(args[0]);
            CatalogViewer viewer = new CatalogViewer( );
            viewer.view(catalogFile);

        } catch (Exception e) {
            e.printStackTrace( );
        }
    }
}

This reads in the file specified, and then spits out the ID and title of each item in the catalog. It also checks for a certain ID ("CDZ-SM01"), and modifies that item to reflect a second edition (and all of us around here love second editions!). Finally, it writes the catalog back out to a new file, with the modified information. Try it out; just be sure that you already have the compiled Zeus-generated classes in your classpath.

Once you've learned how basic data binding works, you aren't in for too many surprises. You'll just need to find the framework that is most suitable for your specific application's functional needs. One thing that Zeus has going for it is the generation of interfaces and implementation classes, which allows you to use your existing classes with little modification. Additionally, Zeus provides for using DTDs, which many of you with hundreds of DTDs in older XML-based applications will love. On the other hand, Zeus is newer than Castor, and has less momentum and a smaller developer community at this point. So, check it out online, and use it if it helps you out.

Previous | Home | Next
15.2. Castor | Book Index | 15.4. JAXB
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JAXB (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

15.4. JAXB

Last but not least, I want to cover JAXB, Sun's Java Architecture for Data Binding. It might surprise you that I'm dealing with JAXB last, or that I've even included coverage of other APIs. However, Sun's offering has been very slow in coming, and is still in early access as I write this book. It will probably not be in a final form when you are reading this chapter, and it has a very limited feature set. Certainly, future versions will add functionality, but for now Sun is just trying to get a stable offering out to the public. You will find features in Castor and Zeus that simply aren't available in JAXB right now; for all these reasons, I want to let you see more than just the JAXB framework. And of course, I'm generally of the opinion that open source software is just cool!

15.4.1. Installation

To get started, you need to download JAXB from Sun at http://java.sun.com/xml/jaxb/index.html. I also recommend you get the documentation and specification.

WARNING: At the time of this writing, JAXB is in early release. Sun could easily change things before a final version, although that's not really common in EA (early access) software. Still, be warned that you may have to tweak the code shown here to work by the time you are checking JAXB out. Additionally, there are many errors in the documentation (at least in the early release). I've tried to steer clear of those here, so the examples in this section can be used as correct syntax if you run into errors using the JAXB instructions (as I did).

Once you've got the release downloaded, simply put the two included jar files, jaxb-xjc-1.0-ea.jar and jaxb-rt-1.0-ea.jar, in your classpath. Additionally, JAXB uses a script, xjc, to perform schema compilation. I have no idea why this isn't a Java class, but that's life. You can invoke the Java class manually, though, if you want to dig into the shell script. However, the early access release neglects to supply a script capable of running on Windows, as many of you may be. Example 15-8 is a Windows-compatible script to match the supplied Unix version.

Example 15-8. xjc.bat for Windows platforms

@echo off

echo JAXB Schema Compiler
echo --------------------

if "%JAVA_HOME%" == "" goto errorJVM
if "%JAXB_HOME%" == "" goto errorJAXB

set JAXB_LIB=%JAXB_HOME%\lib
set JAXB_CLASSES=%JAXB_HOME%\classes

echo %JAVA_HOME%\bin\java.exe -jar %JAXB_LIB%\jaxb-xjc-1.0-ea.jar %1 %2 %3 %4 %5
%JAVA_HOME%\bin\java.exe -jar %JAXB_LIB%\jaxb-xjc-1.0-ea.jar %1 %2 %3 %4 %5

goto end

:errorJVM

echo ERROR: JAVA_HOME not found in your environment.
echo Please, set the JAVA_HOME variable in your environment to match the
echo location of the Java Virtual Machine you want to use.
echo For example:
echo  set JAVA_HOME=c:\java\jdk1.3.1

goto end

:errorJAXB

echo ERROR: JAXB_HOME not found in your environment.
echo Please, set the JAXB_HOME variable in your environment to match the
echo location of the JAXB installation directory.
echo For example:
echo  set JAXB_HOME=c:\java\jaxb-1.0-ea

:end

Place this in your JAXB installation's bin directory, along with xjc; as you can see, I called mine xjc.bat. Finally, add an environment entry for both JAVA_HOME and JAXB_HOME (you only need JAVA_HOME if you are on Unix platforms). On my Windows system, I did this:

set JAVA_HOME=c:\java\jdk1.3.1
set JAXB_HOME=c:\javaxml2\jaxb-1.0-ea

Then you are ready to proceed.

15.4.2. Class Generation

While Castor supports only XML Schema, JAXB supports only DTDs. So, I'll use the DTD from Example 15-5 for class generation here. However, JAXB also requires a binding schema for class generation. A binding schema tells JAXB the specifics of how to convert the constraints in a DTD to a Java class (or classes). At its simplest, you need to specify the root element in the DTD (or multiple root elements, if your DTD defines them). Example 15-9 is the simplest possible binding schema for the catalog.xml document used in this chapter.

Example 15-9. Binding schema for the catalog document

<?xml version="1.0"?>

<xml-java-binding-schema version="1.0ea">
  <element name="catalog" type="class" root="true" />
</xml-java-binding-schema>

This specifies the document as a binding schema, and defines the root element. Save this file as catalog.xjc.

With a DTD (catalog.dtd from the last section on Zeus) and a binding schema, you're ready to generate Java classes. Execute the following command:

xjc c:\javaxml2\ch14\xml\jaxb\catalog.dtd c:\javaxml2\ch14\xml\jaxb\catalog.xjc

Or, on Unix or Cygwin:

xjc /javaxml2/ch14/xml/jaxb/catalog.dtd /javaxml2/ch14/xml/jaxb/catalog.xjc

Once that command completes, you get two classes in your current directory, Catalog.java and Item.java. These have the typical set of methods you would expect:

public class Item {
    public String getLevel( );
    public void setLevel( );

    public String getId( );
    public void setId(String id);

    public List getGuest( );
    public void deleteGuest( );
    public void emptyGuest( );

    // And so on...
}

Note how JAXB deals with lists. It's a step up from Castor (in my opinion), as it does provide access to the List of guests. However (also my opinion), it lacks the convenience of an addGuest( ) method that Zeus provides. You'll need to handle all list manipulation by getting the list and working on it directly:

// The Zeus way
item.addGuest(new Guest("Bela Bleck"));

// The JAXB way
List guests = item.getGuest( );
guests.add("Bela Fleck");

The generated classes are also all concrete classes, much like the model that Castor uses.

The binding schema does provide some nifty options (even if it's not XML). First, you can specify the package that generated classes should be in; this is done using the options element, with the package attribute. Add this line to your binding schema:

<?xml version="1.0"?>

<xml-java-binding-schema version="1.0ea">
  <options package="javaxml2.jaxb" />
  <element name="catalog" type="class" root="true" />
</xml-java-binding-schema>

Now, generated source will be placed in the javaxml2/jaxb directory with the same structure as the package hierarchy. Next, let's specify that the level attribute on the item element should be a number (instead of the default, a String):

<?xml version="1.0"?>

<xml-java-binding-schema version="1.0ea">
  <options package="javaxml2.jaxb" />
  <element name="catalog" type="class" root="true" />
  <element name="item" type="class">
    <attribute name="level" convert="int" />
  </element>
</xml-java-binding-schema>

As you can see, I first added an element declaration for the item element. This allows me to reference its level attribute using the attribute construct. To handle the datatype, I specified the type I wanted (int) with the convert attribute.

Continuing with the options that a binding schema supplies, here's a really nice feature. You can actually change the name of a property from what it is in the DTD. For example, I hate methods like getId( ). Instead, I really prefer getID( ), which looks much better. So, what I really want is to name the id property from my DTD as ID in Java. This turns out to be simple with JAXB:

<?xml version="1.0"?>

<xml-java-binding-schema version="1.0ea">
  <options package="javaxml2.jaxb" />
  <element name="catalog" type="class" root="true" />
  <element name="item" type="class">
    <attribute name="level" convert="int" />
    <attribute name="id" property="ID" />
  </element>
</xml-java-binding-schema>

Once you've made all of these various changes, run the schema compiler (xjc) again. You'll get the modified classes I've been talking about, and now can compile those:

javac -d . javaxml2/jaxb/*.java

If you have any problems, ensure that you still have jaxb-rt-1.0-ea.jar in your classpath.

There are quite a few more options for the binding schema than those discussed here; in fact, many of these were undocumented, and I found them by looking at the xjc.dtd included with JAXB. I suggest you do the same, in addition to reading the supplied documentation. Once you've got your classes generated, it's on to marshalling and unmarshalling.

15.4.3. Marshalling and Unmarshalling

The process of marshalling and unmarshalling turns out to be the same song, third verse, for this chapter. Since that is the case, I will get right to some code, shown in Example 15-10.

Example 15-10. The Categorizer class

package javaxml2;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

// JAXB Classes
import javax.xml.bind.UnmarshalException;

// JAXB Generated Classes
import javaxml2.jaxb.Catalog;
import javaxml2.jaxb.Item;

public class Categorizer {

    public void categorize(File catalogFile) throws IOException,
        UnmarshalException {

        // Convert from XML to Java
        FileInputStream fis = new FileInputStream(catalogFile);
        Catalog catalog = new Catalog( );
        try {
            catalog = Catalog.unmarshal(fis);
        } finally {
            fis.close( );
        }

        // Create new catalogs for the different categories
        Catalog fingerpickingCatalog = new Catalog( );
        Catalog flatpickingCatalog = new Catalog( );
        Catalog mandolinCatalog = new Catalog( );

        List items = catalog.getItem( );
        for (Iterator i = items.iterator(); i.hasNext( ); ) {
            Item item = (Item)i.next( );
            String teacher = item.getTeacher( );
            if ((teacher.equals("Doc Watson")) ||
                (teacher.equals("Steve Kaufman"))) {
                flatpickingCatalog.getItem( ).add(item);
            } else if (teacher.equals("David Wilcox")) {
                fingerpickingCatalog.getItem( ).add(item);
            } else if ((teacher.equals("Sam Bush")) ||
                       (teacher.equals("Chris Thile"))) {
                mandolinCatalog.getItem( ).add(item);
            }
        }

        // Write back out to XML
        FileOutputStream fingerOutput =
            new FileOutputStream(new File("fingerpickingCatalog.xml"));
        FileOutputStream flatpickOutput =
            new FileOutputStream(new File("flatpickingCatalog.xml"));
        FileOutputStream mandolinOutput =
            new FileOutputStream(new File("mandolinCatalog.xml"));
        try {
            // Validate the catalogs
            fingerpickingCatalog.validate( );
            flatpickingCatalog.validate( );
            mandolinCatalog.validate( );

            // Output the catalogs
            fingerpickingCatalog.marshal(fingerOutput);
            flatpickingCatalog.marshal(flatpickOutput);
            mandolinCatalog.marshal(mandolinOutput);
        } finally {
            fingerOutput.close( );
            flatpickOutput.close( );
            mandolinOutput.close( );
        }
    }

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.out.println("Usage: java javaxml2.Categorizer " +
                    "[XML Catalog Filename]");
                return;
            }

            // Get access to XML catalog
            File catalogFile = new File(args[0]);
            Categorizer categorizer = new Categorizer( );
            categorizer.categorize(catalogFile);

        } catch (Exception e) {
            e.printStackTrace( );
        }
    }

}

There is not a whole lot of interest here; you've seen this several times. However, JAXB does do a few things differently. First, the marshal( ) and unmarshal( ) methods are on the generated classes themselves, rather than on static Marshaller and Unmarshaller classes:

// Convert from XML to Java
FileInputStream fis = new FileInputStream(catalogFile);
Catalog catalog = new Catalog( );
try {
    catalog = Catalog.unmarshal(fis);
} finally {
    fis.close( );
}

The generated classes provide static methods that allow for marshalling and unmarshalling. These static methods return an instance of the class with the data from the supplied file filled in. However, you must be sure to assign this return value to an instance variable! An extremely frustrating mistake to make is this:

// Convert from XML to Java
FileInputStream fis = new FileInputStream(catalogFile);
Catalog catalog = new Catalog( );
try {
    catalog.unmarshal(fis);
} finally {
    fis.close( );
}

Notice the bolded line: if you try to access the instance variables of the catalog instance after this code snippet, you will get no data, regardless of what's in the supplied XML file. That's because the unmarshal( ) method is static, and returns a live instance of the Catalog class; since that value is never assigned here, it's lost. This can be quite annoying, so watch out! That very issue is actually a case for the external Marshaller and Unmarshaller classes, as used in Castor and Zeus.

In the example, once I have an instance of the XML catalog, I iterate through it. Depending on the teacher, the code adds the item to one of three new catalogs: a flat picking one, a finger picking one, or a mandolin one. Then, each of these new catalogs is marshalled back out to a new XML document. As an example, here's what I got for my mandolinCatalog.xml document:

<?xml version="1.0" encoding="UTF-8"?>

<catalog>
  <item level="3" id="VD-THI-MN01">
    <title>Essential Techniques for Mandolin</title>
    <teacher>Chris Thile</teacher>
    <description>Here's a lesson that will thrill and inspire mandolin players at
    all levels.</description></item>
  <item level="4" id="CDZ-SM01">
    <title>Sam Bush Teaches Mandolin Repertoire and Techniques</title>
    <teacher>Sam Bush</teacher>
    <description>Learn complete solos to eight traditional and orignal tunes,
    each one jam-packed with licks, runs, and musical variations.</description>
</item></catalog>

The spacing is, as always, added in production, so your line feeds may not match up. However, JAXB's marshalling and unmarshalling is that simple to use. Once you get past the static method issue I just mentioned, it's almost identical to the other two frameworks.

While I encourage you to check out JAXB today, be leery of using it in production until a final release becomes available. There are several undocumented features still floating around that could easily be changed before a final release. Additionally, JAXB does not yet support taking arbitrary objects (ones not generated by JAXB) and marshalling them; this is something the other frameworks do allow for, and might be a drawback for your applications. JAXB also, as mentioned, has no XML Schema support, as well as no namespace support. On the upside, Sun will obviously put a lot of resources into JAXB, so expect it to shape up in the coming months.

In any case, you should have a pretty good taste of what data binding provides by now. The ins and outs of a particular framework are left for you to discover, but with these basics you could pick up any one (or all) of the projects mentioned here and put them to work. Be sure to let whichever project you choose to work with know about what works and what doesn't; it can really affect future development, particularly the open source ones.

Previous | Home | Next
15.3. Zeus | Book Index | 15.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

15.5. What's Next?

We're getting to the latter pages of this book, so it should be no surprise to you that I'm beginning to wrap things up. In the next chapter, I'm going to do a little hocus-pocus and let you know what I think is worth watching out for over the next year or so. Chapter 16, "Looking Forward" is my "Looking Forward" chapter, included for two reasons. The first is the obvious one: I want you to be ahead of the curve. The second, more interesting reason is to point out how quickly things change. This is particularly true in Java and XML, and I anticipate you and I will laugh at a few of the suggestions in the next chapter just a short year from now. In any case, it will help you think about what might be coming on the horizon, and give you some preparation for those important new technologies when they surface.

Previous | Home | Next
15.4. JAXB | Book Index | 16. Looking Forward
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Looking Forward (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Chapter 16. Looking Forward

Contents:

XLink
XPointer
XML Schema Bindings
And the Rest. . .
What's Next?

It's almost time to wrap up the journey through Java and XML. I hope you've had fun. Before I leave you to mull over all the possibilities, I want to finish up with a little crystal-ball gazing. Like any good programmer, I always try and outguess the technology space and be ahead of the curve. This usually involves knowing more than just cursory information about a whole lot of technologies, so I can easily get up to speed when something breaks. In this chapter, I'm going to point out some of the interesting things coming up over the horizon, and let you in on some extra knowledge on each. I'll be the first to admit that some of these guesses may be completely off; others may be the next big thing. Take a look at each, and then be ready to react when you see where they might fit into your applications.[28]

[28]Many of the sections within this chapter are based in whole or in part on articles and tips I have written for the IBM DeveloperWorks online publication, located at http://www.ibm.com/developer. Thanks to Nancy Dunn and the kind folks at IBM for letting me update and reprint parts of those articles.

16.1. XLink

First on my list of up-and-coming stars in the XML world is XLink. XLink defines an XML linking mechanism for referring to other documents. For those of you who are HTML authors, this may sound like the "a" element you are used to:

<a href="http://www.nickelcreek.com">Check out Nickel Creek!</a>.

However, XLink offers much more than simply unidirectional (one-way) linking. Using XLink, you can create bidirectional links, define how links are processed, and most importantly, allow linking from any XML element (instead of just the "a" element). For all these reasons, it's worth getting into here.

Example 16-1 is a small XML document, representing a few of my guitars.

Example 16-1. XML document using XLink

<?xml version="1.0"?>

<guitars xmlns="http://www.newInstance.com/about/guitars"
         xmlns:xlink="http://www.w3.org/1999/xlink">
 <guitar luthier="Bourgeois"
          xlink:type="simple"
          xlink:href="http://www.newInstance.com/about/guitars/bourgeoisOM">
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougOM_front_full.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
  This is a real beauty in a small body. Although this is an OM, I use it for
  flatpicking bluegrass as well as for producing some beautiful fingerstyle sounds.
  </description>
 </guitar>
 <guitar luthier="Bourgeois"
          xlink:type="simple"
          xlink:href="http://www.newInstance.com/about/guitars/bourgeoisD150">
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougd150_con_rim2.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
   Here's the holy grail in process. Dana Bourgeois is building this Brazilian
  rosewood and adirondack bluegrass monster right now... you'll know it's
  finished when you hear a run and your windows shatter!
  </description>
 </guitar>
</guitars>

First, you'll notice that I reference the XLink namespace, so that the document has access to the XLink attributes and features. Second, I'm going to cover only XLinks of the "simple" type, specified by the xlink:type attribute. That's because browser support for XLinks is minimal, found only in Mozilla and Netscape 6 (I haven't been able to test IE 6.0, but 5.5 has no support for them), so keeping to the basic aspects will serve you well.

Once the formalities are out of the way, XLinking just requires using some attributes on the elements that have links. Take my document's guitar element. It specifies a luthier for each guitar (that's a guitar maker for those of you who are missing out on guitar playing!). I already mentioned the use of the xlink:type attribute, which is set to the value "simple". It then specifies a URL to link to using XLink. To specify this URL, it uses the xlink:href attribute. So far, this looks a lot like HTML. No big deal, right? By default (assuming browser support, of course), this will set the link up to replace the current window when clicked upon. If you want the target of the link to open in a new window, you can add the xlink:show attribute, and give it a value of "new"; the default is "replace", which is normal HTML behavior.

Of course, this only covers basic linking. Things get more interesting when you want to access remote locations as resources, such as linking in images. Look at the description element; this sets the value of the xlink:show attribute to "embed". The resource, in this case an image file showing the guitar being described, should be processed inline within the page. This instructs an XLink-aware browser to insert the specified document inline within the XML. It becomes really interesting when you consider this could be another XML document, and not just an image.

Taking things even further, you can specify when the resource should be shown. This is handled by the xlink:actuate attribute. It defines when the resource is read and shown. When the value is "onLoad", as it is in Example 16-1, the resource should be loaded when the initial document is loaded. You could also specify the value "onRequest", which means that until the link is clicked, the resource is not shown. This is handy for keeping bandwidth low, allowing the user to view only the resources that they want to see.

XLink could definitely have major impact on the next generation of XML documents. For the complete specification, check out http://www.w3.org/TR/xlink. I'd also keep an eye on the latest browsers and versions to see when complete XLink support shows up.

Previous | Home | Next
15.5. What's Next? | Book Index | 16.2. XPointer
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPointer (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

16.2. XPointer

XPointer is another XML linking technology, and in fact builds on XLink's capabilities. XLink, while useful in and of itself, only allows you to refer to another document. However, there are many times when you may want to refer to a specific part of another document. This is a very common task, and is somewhat analogous to using named anchors in HTML. It is made possible by using XPointer on top of XLink; the specs build very naturally on each other, and are intended to work together. First, you want to take a look at the target document you are going to link to. If you can, ensure that this document uses id attributes in it. This will make the linking and pointing much easier. Example 16-2 shows the listing of some of the guitars that luthier Dana Bourgeois makes, and has IDs for each type.

Example 16-2. A listing of Bourgeois guitars

<?xml version="1.0"?>

<guitarTypes xmlns="http://www.bourgeoisguitars.com">
 <type model="OM" ID="OM">
  <picture url="http://www.bourgeoisguitars.com/images/vvOM.jpg"/>
  <description>Small bodied orchestra model.</description>
 </type>
 <type model="D" ID="D">
  <picture
   url="http://www.bourgeoisguitars.com/images/ricky%20skaggs%20model.jpg"/>
  <description>Bluegrass powerhouse in a standard dreadnought shape.</description>
 </type>
 <type model="slopeD" ID="slopeD">
  <picture
   url="http://www.bourgeoisguitars.com/images/slope%20d,%20custom%20version.jpg"/>
  <description>
    Slope shouldered dreadnought, perfect for vocal accompaniment.
  </description>
 </type>
</guitarTypes>

For the sake of discussion, assume that this document is available at http://www.bourgeoisguitars.com/guitars.xml. Instead of just referencing the entire document, which doesn't help a whole lot, XPointer allows for linking to specific parts of the document. Remember the xlink:href attribute? The value supplied to that attribute was the target of an XLink. But you can add a pound sign (#), and then an XPointer expression to these URLs. For example, the expression xpointer(id("slopeD")) refers to an element in a document with the ID "slopeD". So, to refer to the XML shown in Example 16-2, and then to the Slope D model guitar described in that document, the URL http://www.bourgeoisguitars.com/guitars.xml#xpointer(id("slopeD")) would be used. Easy enough. Let me show you a modified version of the XML document I introduced in the XLink section (Example 16-1), which describes my guitars, using some XPointer references. (Forgive the awkward formatting; I had a lot to fit on some lines.) Take a look at Example 16-3.

Example 16-3. My guitars in XML using XPointer

<?xml version="1.0"?>

<guitars xmlns="http://www.newInstance.com/about/guitars"
         xmlns:xlink="http://www.w3.org/1999/xlink">
 <guitar luthier="Bourgeois"
          xlink:type="simple"
          xlink:href=
         "http://www.bourgeoisguitars.com/guitars.xml#xpointer(id(&apos;OM&apos;))">
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougOM_front_full.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
   This is a real beauty in a small body. Although this is an OM, I use it for
   flatpicking bluegrass as well as for producing some beautiful
   fingerstyle sounds.
  </description>
 </guitar>
 <guitar luthier="Bourgeois"
         xlink:type="simple"
         xlink:href=
          "http://www.bourgeoisguitars.com/guitars.xml#xpointer(id(&apos;D&apos;))">
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougd150_con_rim2.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
  Here's the holy grail in process. Dana Bourgeois is building this Brazilian
  rosewood and adirondack bluegrass monster right now... you'll know it's
  finished when you hear a run and your windows shatter!
  </description>
 </guitar>
</guitars>

Now my document can reference the XML content that Dana Bourgeois keeps about his guitars. If he changes this information, I don't have to worry; my document stays current because it simply links to his information. Notice that I had to "escape" the quotation marks within the XPointer expression by using &amp; instead of an ampersand (&). However, this makes for a rather long URL to link to. Long URLs, in my experience, lead to annoying typos (and annoying formatting in a book!). Luckily, XPointer allows a handy shorthand form when linking to an element with an ID tag. Instead of using the xpointer(id("D")) form, you can simply use the value of the ID to target. In this case, that would simply be "D". I can simply combine the document in Example 16-3 to that shown in Example 16-4. Makes for a much cleaner link syntax.

Example 16-4. Using XPointer shorthand to simplify Example 16-3

<?xml version="1.0"?>

<guitars xmlns="http://www.newInstance.com/about/guitars"
         xmlns:xlink="http://www.w3.org/1999/xlink">
 <guitar luthier="Bourgeois"
          xlink:type="simple"
          xlink:href="http://www.bourgeoisguitars.com/guitars.xml#OM" >
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougOM_front_full.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
   This is a real beauty in a small body. Although this is an OM, I use it for
   flatpicking bluegrass as well as for producing some beautiful
   fingerstyle sounds.
  </description>
 </guitar>
 <guitar luthier="Bourgeois"
         xlink:type="simple"
         xlink:href="http://www.bourgeoisguitars.com/guitars.xml#D" >
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougd150_con_rim2.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
   Here's the holy grail in process. Dana Bourgeois is building this Brazilian
  rosewood and adirondack bluegrass monster right now... you'll know it's
  finished when you hear a run and your windows shatter!
  </description>
 </guitar>
</guitars>

In addition to this direct listing, you can point to elements relative to other elements. As an example of this, my description elements in Example 16-5 have been changed to point to the image specified in the bourgeois.xml file from Example 16-2.

NOTE: For the sake of getting this lengthy URL into the code space of an O'Reilly book, I've abbreviated the URL http://www.bourgeoisguitars.com as simply http://bg.com. This isn't a valid URL, but it works for the example.

Example 16-5. Using relative links

<?xml version="1.0"?>

<guitars xmlns="http://www.newInstance.com/about/guitars"
         xmlns:xlink="http://www.w3.org/1999/xlink">
 <guitar luthier="Bourgeois"
          xlink:type="simple"
          xlink:href=
 "http://bg.com/guitars.xml#xpointer(id(&apos;OM&apos;))/descendant::picture[@url]">
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougOM_front_full.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
   This is a real beauty in a small body. Although this is an OM, I use it for
   flatpicking bluegrass as well as for producing some beautiful
   fingerstyle sounds.
  </description>
 </guitar>
 <guitar luthier="Bourgeois"
         xlink:type="simple"
         xlink:href=
  "http://bg.com/guitars.xml#xpointer(id(&apos;D&apos;))/descendant::picture[@url]" >
  <descripton xlink:type="simple"
              xlink:href="http://www.newinstance.com/pics/bougd150_con_rim2.jpg"
              xlink:actuate="onLoad" xlink:show="embed">
  Here's the holy grail in process. Dana Bourgeois is building this Brazilian
  rosewood and adirondack bluegrass monster right now... you'll know it's
  finished when you hear a run and your windows shatter!
  </description>
 </guitar>
</guitars>

Here, you can see that once the element referred to by the ID is found, the descendant of that element (specified by the descendant keyword) named "picture" is found. Then, the value of the attribute of that element named "url" is the final target of the link. I know that's a mouthful, but if you take it step by step, it turns out to be fairly straightforward. For more information on the huge variety of options that XPointer offers, check out the XPointer specification online at http://www.w3.org/TR/xptr.

NOTE: Notice that I did not use the shorthand form of ID links I talked about in the last section. That's because using that form of ID linking allows for only a direct link; no further linking (such as the child-traversing reference in Listing 4) is allowed without the longer form of ID linking.

XLink and XPointer stand to change how XML is linked and authored in major ways. I expect to see a variety of support in Java APIs for this once the specifications are fully supported by browsers as well, so keep an eye out.

Previous | Home | Next
16. Looking Forward | Book Index | 16.3. XML Schema Bindings
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Schema Bindings (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

16.3. XML Schema Bindings

Moving more into the Java side of things, one major aspect of XML programming I expect to see is a set of datatypes defined in Java that represent XML Schema constructs. This is somewhat similar to the DOM Level 2 HTML bindings I talked about in Chapter 6, "Advanced DOM". I consider these infinitely more useful. Because an XML Schema is itself an XML document, it can be parsed and dealt with like any other XML document. However, trying to work with an XML Schema as just any old XML document turns out to be a bit of a pain. You can't, for example, query an element definition and determine if it is a complex type. Instead, you have to get the element, see if it has any children, determine if one of those children is named complexType, and so on. This gets even worse when things like sequencing are used; suddenly the complex type definition appears nested two levels deep.

What I expect to see (and in fact, already hear rumblings of) is a grammar and set of Java objects built to specifically match up with XML Schema datatypes. This would presumably be built on an existing object-based API, like DOM or JDOM. So, for the sake of example, assume that DOM Level 4 or JDOM 1.1 define such objects. You might see code like this:

// THIS CODE IS PURELY HYPOTHETICAL
XSDDocumentParser schemaParser =
    new org.apache.xerces.parsers.XSDParser( );
parser.parse(mySchema);
XSDDocument doc = parser.getXSDDocument( );

Now, instead of working with root elements and attributes in XML, you would deal with this document (where all the classes are prefixed by XSD, for XML Schema Datatypes) using schema concepts, as shown here:

// Get "root" element
XSDSchema schema = doc.getXSDSchema( );

// Get target namespace for this document
String targetNamespaceURI = schema.getTargetNamespace().getURI( );

// Get some named element definition
XSDElementDef def = schema.getElementDef("movie");
if (def.isComplexType( )) {
    List attributeDefs = def.getAttributeDefs( );
    List nestedElementDefs = def.getElementDefs( );
} else {
    XSDType elementType = def.getType( );
}

Obviously this is a bit contrived, because I'm making up syntax as I go. However, it's clear that my Java code is working on an XML Schema, and taking advantage of schema semantics. I'm not working with basic XML semantics (although if these classes extended basic DOM or JDOM classes, you could also work in that medium), but using what the XML Schema specification says about legal schemas to work a bit smarter. Hopefully the third edition of this book will have details about this API, because it would be very useful.

Previous | Home | Next
16.2. XPointer | Book Index | 16.4. And the Rest. . .
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


And the Rest. . . (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

16.4. And the Rest. . .

There's a lot more that I want to see, and that I could talk about. However, this book would take another six months to complete if I put them all in, and then it would be time for another revision! Instead, I'll give you a quick rundown of hits. These are blurbs of information that may be helpful or may bore you, but will almost certainly have an impact on Java and XML in the next few years:

  • Scalable Vector Graphics (SVG) and Apache Batik (http://xml.apache.org)

  • MathML (the Math Markup Language, an extension of XML)

  • Specifications related to and built upon ebXML (http://www.ebXML.org)

  • Xerces 2.0 (http://xml.apache.org)

  • JAXM, the Java API for XML Messaging (http://java.sun.com/xml/xml_jaxm.html)

  • JAXRPC, the Java API for XML-based RPC (http://java.sun.com/xml/xml_jaxrpc.html)

There they are, my quick picks for what will be important a year from now (if not sooner). I'll probably be recanting a few in the next edition, but that's life in the fast lane, and XML is definitely the fast lane.

Previous | Home | Next
16.3. XML Schema Bindings | Book Index | 16.5. What's Next?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's Next? (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

16.5. What's Next?

Appendixes. The index. Then some information about me, and a colophon. And probably some advertising for other great O'Reilly books.

Seriously, I've covered quite a bit of information at this point. Taking a few days to let the material sink in, and then applying your new XML skills on a project for work or something personal should help you polish your XML knowledge. Soon you'll be an XML wizard, and find your applications' values increasing as they are more flexible, configurable, and productive. Finally, you'll see your value to your boss (and potential bosses at other companies) rise dramatically as you code maintainable and performance-driven applications. Have fun, and stay extensible. I'll see you online.

Previous | Home | Next
16.4. And the Rest. . . | Book Index | A. API Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


API Reference (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Appendix A. API Reference

Contents:

SAX 2.0
DOM Level 2
JAXP 1.1
JDOM 1.0 (Beta 7)

This appendix is an API reference for the four lower-level Java and XML APIs covered in this book, SAX, DOM, JDOM, and JAXP. It is broken down into sections based on the API being documented.

A.1. SAX 2.0

SAX 2.0 provides a sequential look into an XML document. Detailed in Chapter 3, "SAX" and Chapter 4, "Advanced SAX ", SAX defines a set of interfaces that can be implemented and will be invoked as callbacks during the XML parsing process. The SAX packages are detailed here, with the classes and interfaces listed alphabetically. In the org.xml.sax.helpers package, most of the methods in the helper classes are implementations of interfaces already defined in the core SAX package (org.xml.sax).

A.1.1. Package: org.xml.sax

This package contains the core interfaces and classes for SAX 2.0. Most of the interfaces defined are intended to be implemented by you, the Java developer, with the exception of the actual XMLReader and Attributes implementation. These interfaces should be implemented by your vendor's XML parsing software. In addition, several exceptions that SAX methods are allowed to throw are defined. Several of the interfaces defined here are part of the SAX 1.0 and 2.0 alpha distributions, and are now deprecated.

A.1.1.1. AttributeList [deprecated]

This interface was defined in SAX 1.0, and is now deprecated. The Attributes interface should be used instead of AttributeList for SAX 2.0 implementations.

public interface AttributeList {
    public abstract int getLength( );
    public abstract String getName(int i);
    public abstract String getType(int i);
    public abstract String getValue(int i);
    public abstract String getType(String name);
    public abstract String getValue(String name);
}

A.1.1.2. Attributes

This interface represents a listing of XML attributes. It is reported to the callbacks associated with the start of element (startElement( ) in ContentHandler), and is somewhat analogous to a Java Vector. The number of attributes represented can be obtained, as well as various views of the attributes' names (local, namespace prefix and URI, and raw) and values. Additionally, methods are available for locating the index of an attribute given its name. The primary difference between this interface and its predecessor, AttributeList, is that this interface is namespace-aware.

public interface Attributes {
    public abstract int getLength( );
    public abstract String getURI(int index);
    public abstract String getLocalName(int index);
    public abstract String getQName(int index);
    public abstract String getType(int index);
    public abstract String getValue(int index);
    public int getIndex(String uri, String localName);
    public int getIndex(String qName);
    public abstract String getType(String uri, String localName);
    public abstract String getType(String qName);
    public abstract String getValue(String uri, String localName);
    public abstract String getValue(String qName);
}

A.1.1.3. ContentHandler

This interface defines the callback methods available to an application that deal with the content of the XML document being parsed. These include notification of the start and end of parsing (which precede and follow all other handler callbacks, respectively), processing instructions, and entities that may be skipped by nonvalidating parsers. Element callbacks, complete with namespace mappings, are also made available.

public interface ContentHandler {
    public void setDocumentLocator(Locator locator);
    public void startDocument( ) throws SAXException;
    public void endDocument( ) throws SAXException;
    public void startPrefixMapping(String prefix, String uri)
        throws SAXException;
    public void endPrefixMapping(String prefix)
        throws SAXException;
    public void startElement(String namespaceURI, String localName,
                              String qName, Attributes atts)
        throws SAXException;
    public void endElement(String namespaceURI, String localName,
                              String qName)
        throws SAXException;
    public void characters(char ch[], int start, int length)
        throws SAXException;
    public void ignorableWhitespace(char ch[], int start, int length)
        throws SAXException;
    public void processingInstruction(String target, String data)
        throws SAXException;
    public void skippedEntity(String name)
        throws SAXException;
}

A.1.1.4. DocumentHandler

This interface was defined in SAX 1.0, and is now deprecated. The ContentHandler interface should be used instead of DocumentHandler for SAX 2.0 implementations.

public interface DocumentHandler {
    public abstract void setDocumentLocator(Locator locator);
    public abstract void startDocument( ) throws SAXException;
    public abstract void endDocument( ) throws SAXException;
    public abstract void startElement(String name, AttributeList atts)
               throws SAXException;
    public abstract void endElement(String name)
               throws SAXException;
    public abstract void characters(char ch[], int start, int length)
               throws SAXException;
    public abstract void ignorableWhitespace(char ch[], int start, int length)
               throws SAXException;
    public abstract void processingInstruction (String target, String data)
               throws SAXException;
}

A.1.1.5. DTDHandler

This interface defines callbacks that are invoked in the process of parsing a DTD. Note that this interface does not provide information about the constraints within the DTD, but instead about references to unparsed entities and NOTATION declarations, indicating items that are generally unparsed data.

public interface DTDHandler {
    public abstract void notationDecl(String name, String publicId,
                                      String systemId)
                 throws SAXException;
    public abstract void unparsedEntityDecl(String name, String publicId,
                                      String systemId,
                                      String notationName)
           throws SAXException;
}

A.1.1.6. EntityResolver

This interface allows applications to intervene in the process of referencing external entities, such as an XML document that references a DTD or stylesheet. By implementing this interface, a modified or even completely different SAX InputSource can be returned to the calling program. Additionally, null can be returned to indicate that a normal URI connection should be opened to the specified system ID.

public interface EntityResolver {
    public abstract InputSource resolveEntity(String publicId,
                                    String systemId)
             throws SAXException, IOException;
}

A.1.1.7. ErrorHandler

This interface allows custom behavior to be attached to the three types of problem conditions that can occur within the lifecycle of XML parsing. Each receives the SAXParseException indicating what problem initiated the callback. The SAXException is provided to allow a means of throwing an exception that could stop parsing altogether.

public interface ErrorHandler {
    public abstract void warning(SAXParseException exception)
               throws SAXException;
    public abstract void error(SAXParseException exception)
               throws SAXException;
    public abstract void fatalError(SAXParseException exception)
               throws SAXException;
}

A.1.1.8. HandlerBase

This helper class provides empty implementations of all the SAX 1.0 core handler interfaces, and can be extended to allow the quick addition of handlers by overriding methods with application-defined behavior. This class was defined in SAX 1.0, and is now deprecated. The org.xml.sax.helpers.DefaultHandler class should be used instead of HandlerBase for SAX 2.0 implementations.

public class HandlerBase implements EntityResolver, DTDHandler,
                                    DocumentHandler, ErrorHandler {

        // EntityResolver implementation
        public InputSource resolveEntity(String publicId, String systemId);

        // DTDHandler implementation
        public void notationDecl(String name, String publicId,
                             String systemId);
    public void unparsedEntityDecl(String name, String publicId,
                         String systemId, String notationName);

        // DocumentHandler implementation
        public void setDocumentLocator(Locator locator);
    public abstract void startDocument( ) throws SAXException;
    public abstract void endDocument( ) throws SAXException;
    public abstract void startElement(String name, AttributeList atts)
               throws SAXException;
    public abstract void endElement(String name)
               throws SAXException;
    public abstract void characters(char ch[], int start, int length)
               throws SAXException;
    public abstract void ignorableWhitespace(char ch[], int start,
                                                        int length)
               throws SAXException;
    public abstract void processingInstruction(String target,
                                               String data)
               throws SAXException;

       // ErrorHandler implementation
    public abstract void warning(SAXParseException exception)
               throws SAXException;
    public abstract void error(SAXParseException exception)
               throws SAXException;
    public abstract void fatalError(SAXParseException exception)
               throws SAXException;
}

A.1.1.9. InputSource

This class encapsulates all information about a resource used in XML processing. This can be as little as a String or InputStream used for locating input, or as complex as an entity with a public ID and system ID as well as a URI reference (such as a DTD publicly defined). This class is the preferred wrapper for passing input into a SAX parser.

public class InputSource {
    public InputSource( );
    public InputSource(String systemId);
    public InputSource(InputStream byteStream);
    public InputSource(Reader characterStream);
    public void setPublicId(String publicId);
    public String getPublicId( );
    public void setSystemId(String systemId);
    public String getSystemId( );
    public void setByteStream(InputStream byteStream);
    public InputStream getByteStream( );
    public void setEncoding(String encoding);
    public String getEncoding( );
    public void setCharacterStream(Reader characterStream);
    public Reader getCharacterStream( );
}

A.1.1.10. Locator

This class is a complement to an XML document or other parsed construct, as it provides the document's system ID and public ID as well as information about the location within the file being processed. This is particularly helpful for use in IDE applications and for identifying where errors occur in parsing.

public interface Locator {
    public abstract String getPublicId( );
    public abstract String getSystemId( );
    public abstract int getLineNumber( );
    public abstract int getColumnNumber( );
}

A.1.1.11. Parser

This interface was defined in SAX 1.0, and is now deprecated. The XMLReader interface should be used instead for SAX 2.0 implementations.

public interface Parser {
    public abstract void setLocale(Locale locale) throws SAXException;
    public abstract void setEntityResolver(EntityResolver resolver);
    public abstract void setDTDHandler(DTDHandler handler);
    public abstract void setDocumentHandler(DocumentHandler handler);
    public abstract void setErrorHandler(ErrorHandler handler);
    public abstract void parse(InputSource source)
              throws SAXException, IOException;
    public abstract void parse(String systemId)
              throws SAXException, IOException;
}

A.1.1.12. SAXException

This is the core exception thrown by SAX callbacks and parser implementations. Because it is often thrown as a result of other exceptions, it has a constructor that allows the passing in of a lower-level Exception as well as an accessor method to retrieve the originating Exception. It is also the base class for all other SAX Exception classes.

public class SAXException extends Exception {
     public SAXException(String message);
       public SAXException(Exception e);
       public SAXException(String message, Exception e);
       public String getMessage( );
       public Exception getException( );
       public String toString( );
}

A.1.1.13. SAXNotRecognizedException

This class provides a means for an XMLReader implementation to throw an error when an unrecognized identifier is received. This is most common in the setProperty( ) and setFeature( ) methods (as well as their accessor counterparts) when a URI is supplied about which the parser has no information.

public class SAXNotRecognizedException extends SAXException {
    public SAXNotRecognizedException(String message);
}

A.1.1.14. SAXNotSupportedException

This class provides a means for an XMLReader implementation to throw an error when an unsupported (but recognized) identifier is received. This is most common in the setProperty( ) and setFeature( ) methods (as well as their accessor counterparts) when a URI is supplied for which the parser has no supporting code.

public class SAXNotSupportedException extends SAXException {
    public SAXNotSupportedException(String message)
}

A.1.1.15. SAXParseException

This class represents exceptions that can occur during the parsing process. Information about the location of the error within the XML document is available through this class's accessor methods. The preferred means of supplying this information to the class is through a Locator, but the line and column number where problems occurred can be supplied directly through overloaded constructors. The system ID and public ID of the document with the problem are also made available to the class through various means in the constructors.

public class SAXParseException extends SAXException {
    public SAXParseException(String message, Locator locator);
    public SAXParseException(String message, Locator locator,
                              Exception e);
       public SAXParseException(String message, String publicId,
                            String systemId, int lineNumber,
                                  int columnNumber);
    public SAXParseException(String message, String publicId,
                             String systemId, int lineNumber,
                             int columnNumber, Exception e);
    public String getPublicId( );
    public String getSystemId( );
    public int getColumnNumber( );
}

A.1.1.16. XMLFilter

This class is analogous to an XMLReader, but it obtains its events from another XMLReader rather than a static document or network resource. These filters can also be chained on each other. Their primary use is in modifying the output from a lower-level XMLReader in the chain, providing filtering of the data reported to callback methods before the final application receives notification of the data.

public interface XMLFilter extends XMLReader {
    public abstract void setParent(XMLReader parent);
    public abstract XMLReader getParent( );
}

A.1.1.17. XMLReader

This is the core interface that defines parsing behavior in SAX 2.0. Each vendor's XML parsing software package must include at least one implementation of this interface. It replaces the SAX 1.0 Parser interface by adding support for namespaces in a document's elements and attributes. In addition to providing an entry into parsing (with either a system ID or InputSource as input), it allows registering of the various handler interfaces that SAX 2.0 provides. The features and properties available to a SAX parser implementation are also set through this interface. A complete list of SAX core features and properties is contained in Appendix B, "SAX 2.0 Features and Properties".

public interface XMLReader {
    public boolean getFeature(String name)
        throws SAXNotRecognizedException, SAXNotSupportedException;
    public void setFeature(String name, boolean value)
              throws SAXNotRecognizedException, SAXNotSupportedException;
    public Object getProperty(String name)
              throws SAXNotRecognizedException, SAXNotSupportedException;
    public void setProperty(String name, Object value)
              throws SAXNotRecognizedException, SAXNotSupportedException;
    public void setEntityResolver(EntityResolver resolver);
    public EntityResolver getEntityResolver( );
    public void setDTDHandler(DTDHandler handler);
    public DTDHandler getDTDHandler( );
    public void setContentHandler(ContentHandler handler);
    public ContentHandler getContentHandler( );
    public void setErrorHandler(ErrorHandler handler);
    public ErrorHandler getErrorHandler( );
    public void parse(InputSource input)
              throws IOException, SAXException;
    public void parse(String systemId)
              throws IOException, SAXException;
}

A.1.2. Package: org.xml.sax.ext

This package provides extensions to the SAX core classes and interfaces. Specifically, additional handlers are defined for less common processing within the SAX parsing process. XMLReader implementations are not required to support these extension handlers.

A.1.2.1. DeclHandler

This interface defines callbacks that give specific information about DTD declarations. Element and attribute definitions invoke the appropriate callback with their names (and the element names for attributes) as well as constraint information. While this is a fairly rigid set of data for attributes, elements only receive a String with the constrained model as pure text. Additionally, internal and external entity reference notifications are defined.

public interface DeclHandler {
    public abstract void elementDecl(String name, String model)
              throws SAXException;
    public abstract void attributeDecl(String eName, String aName,
                                      String type, String valueDefault,
                                      String value)
              throws SAXException;
    public abstract void internalEntityDecl(String name, String value)
              throws SAXException;
    public abstract void externalEntityDecl(String name, String publicId,
                                               String systemId)
              throws SAXException;
}

A.1.2.2. LexicalHandler

This interface defines callbacks for various events that are at a document level in terms of processing, but do not affect the resulting data within the XML document. For example, the handling of a DTD declaration, comments, and entity references would invoke callbacks in implementations of this interface. Additionally, a callback is defined to signal when a CDATA section is started and ended (although the reported data will always remain the same).

public interface LexicalHandler {
    public abstract void startDTD(String name, String publicId,
                                  String systemId)
              throws SAXException;
    public abstract void endDTD( )
              throws SAXException;
    public abstract void startEntity(String name)
              throws SAXException;
    public abstract void endEntity(String name)
              throws SAXException;
    public abstract void startCDATA( )
              throws SAXException;
    public abstract void endCDATA( )
              throws SAXException;
    public abstract void comment(char ch[], int start, int length)
              throws SAXException;
}

A.1.3. Package: org.xml.sax.helpers

This package provides extensions to the SAX core classes and interfaces. Specifically, additional handlers are defined for less common processing within the SAX parsing process. XMLReader implementations are not required to support these extension handlers.

NOTE: In the classes in this package that are default implementations of core org.xml.sax interfaces, I have left out the repeated methods for brevity. Instead, I've simply added a comment indicating what interface's methods are implemented.

A.1.3.1. AttributeListImpl

This class provides a default implementation of the org.xml.sax.AttributeList interface, and is deprecated in SAX 2.0. It allows addition and removal of attributes as well as a clearing of the list.

public class AttributeListImpl implements AttributeList {
       public AttributeListImpl( );
       public AttributeListImpl(AttributeList atts);

       // Implementation of AttributeList interface

       // Additional methods
       public void setAttributeList(AttributeList atts);
       public void addAttribute(String name, String type, String value);
       public void removeAttribute(String name);
       public void clear( );

}

A.1.3.2. AttributesImpl

This class provides a default implementation of the org.xml.sax.Attributes interface. It allows addition and removal of attributes as well as a clearing of the list.

public class AttributesImpl implements Attributes {
       public AttributesImpl( );
       public AttributesImpl(Attributes atts);

       // Implementation of Attributes interface

       // Additional methods
    public void addAttribute(String uri, String localName,
                             String qName, String type, String value);
    public void setAttribute(int index, String uri, String localName,
                              String qName, String type, String value);
       public void clear( );
}

A.1.3.3. DefaultHandler

This helper class provides empty implementations of all the SAX 2.0 core handler interfaces, and can be extended to allow for quick addition of handlers by only overriding methods with application-defined behavior. This replaces the SAX 1.0 org.xml.sax.HandlerBase class.

public class DefaultHandler implements EntityResolver, DTDHandler,
                                       ContentHandler, ErrorHandler {

       // (Empty) Implementation of EntityResolver interface

       // (Empty) Implementation of DTDHandler interface

       // (Empty) Implementation of ContentHandler interface

       // (Empty) Implementation of ErrorHandler interface
}

A.1.3.4. LocatorImpl

This class provides a default implementation of the org.xml.sax.Locator interface. It also provides a means of directly setting the line and column numbers.

public class LocatorImpl implements Locator {
       public LocatorImpl( );
       public LocatorImpl(Locator locator);

       // Implementation of Locator interface

       // Additional methods
       public void setPublicId(String publicId);
       public void setSystemId(String systemId);
       public void setLineNumber(int lineNumber);
       public void setColumnNumber(int columnNumber);
}

A.1.3.5. NamespaceSupport

This encapsulates namespace behavior, allowing applications to not have to implement the behavior on their own (unless desired for performance reasons). It allows handling of namespace contexts in a stack fashion, and also provides the ability to process XML 1.0 names, retrieving their "namespace-aware" counterparts.

public class NamespaceSupport {
       public NamespaceSuport( );
       public void reset( );
    public void pushContext( );
    public void popContext( );
    public boolean declarePrefix(String prefix, String uri);
    public String [] processName(String qName, String parts[],
                                 boolean isAttribute);
    public String getURI(String prefix);
    public Enumeration getPrefixes( );
    public Enumeration getDeclaredPrefixes( );
}

A.1.3.6. ParserAdapter

This helper class wraps a SAX 1.0 Parser implementation and makes it behave like a 2.0 XMLReader implementation (making namespace support available). The only callback that does not behave normally is skippedEntity( ) in the ContentHandler interface; it is never invoked.

public class ParserAdapter implements XMLReader, DocumentHandler {
       public ParserAdapter( ) throws SAXException;
       public ParserAdapter(Parser parser);

       // Implementation of XMLReader interface

       // Implementation of DocumentHandler interface
}

A.1.3.7. ParserFactory

This class contains methods that dynamically create an instance of a Parser implementation from a specified class name, or if none is supplied, from a system property named "org.xml.sax.driver".

public class ParserFactory {
    public static Parser makeParser( ) throws ClassNotFoundException,
                IllegalAccessException, InstantiationException,
                NullPointerException, ClassCastException;
    public static Parser makeParser(String className)
                throws ClassNotFoundException, IllegalAccessException,
                InstantiationException, ClassCastException;
}

A.1.3.8. XMLFilterImpl

This class provides a default implementation of the org.xml.sax.XMLFilter interface.

public class XMLFilterImpl implements XMLFilter, EntityResolver,
                                      DTDHandler, ContentHandler,
                                      ErrorHandler {
       public XMLFilterImpl( );
       public XMLFilterImpl(XMLReader parent);

       // Implementation of XMLFilter interface

       // Implementation of XMLReader interface

       // Implementation of EntityResolver interface

       // Implementation of DTDHandler interface

       // Implementation of ContentHandler interface

       // Implementation of ErrorHandler interface
}

A.1.3.9. XMLReaderAdapter

This helper class wraps a SAX 2.0 XMLReader implementation and makes it behave like a 1.0 Parser implementation (making namespace support unavailable). The namespaces feature (http://xml.org/sax/features/namespaces) must be supported, or errors in parsing will occur.

public class XMLReaderAdapter implements Parser, ContentHandler {
       public XMLReaderAdapter ( ) throws SAXException;
       public XMLReaderAdapter (XMLReader xmlReader);

    // Implementation of Parser interface

       // Implementation of ContentHandler interface
}

A.1.3.10. XMLReaderFactory

This class contains methods that dynamically create an instance of an XMLReader implementation from a specified class name, or if none is supplied, from a system property named "org.xml.sax.driver".

final public class XMLReaderFactory {
    public static XMLReader createXMLReader( ) throws SAXException;
    public static XMLReader createXMLReader(String className)
              throws SAXException;
}
Previous | Home | Next
16.5. What's Next? | Book Index | A.2. DOM Level 2
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DOM Level 2 (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

A.2. DOM Level 2

DOM provides a complete, in-memory representation of an XML document. Developed by the W3C, DOM provides detail about the structure of a document after it has been completely parsed. While DOM Level 3 will specify an API for getting the DOM Document object, there is currently nothing in DOM that defines this behavior. Like SAX, most of the core DOM package is made up of interfaces that define structures within an XML document, and map those structures to the Java language (these same mappings apply to CORBA, JavaScript, and other languages as well).

A.2.1. Package: org.w3c.dom

This package contains the core interfaces and classes for DOM Level 2. Typically a vendor's parsing software provides an implementation of those interfaces that are implicitly used by your application software.

A.2.1.1. Attr

This interface represents an XML attribute (on an element) within Java. It provides access to the name and value of the attribute, and allows the setting of the value (for mutability).[29] The getSpecified( ) method indicates if the attribute (and its value) was explicitly noted in the XML document, or if a value was not specified but the document's DTD assigned a default value to the attribute. Finally, the "owning" element can be obtained from this interface.

[29]In this and other setXXX( ) methods in DOM, a DOMException results when a modification is attempted on a node that is read-only.

public interface Attr extends Node {
    public String getName( );
    public boolean getSpecified( );
    public String getValue( );
    public void setValue(String value) throws DOMException;
    public Element getOwnerElement( );
}

A.2.1.2. CDATASection

This interface does not define any methods of its own; instead it inherits all of the Text interface's methods. However, by having its own interface (and thus its own node type), a distinction can be drawn between text within XML CDATA sections and simple text (not in a CDATA section) within an element.

public interface CDATASection extends Text {
}

A.2.1.3. CharacterData

This interface is the "super" interface for all textual Node types in DOM (Text, Comment, and indirectly CDATASection). It defines methods for accessing and setting the data within a textual node, as well as a set of methods for dealing with the textual data directly as characters: obtaining the length, appending, inserting, and deleting data, and replacing all or part of the data. All of these methods throw DOMExceptions when the node is read-only.

public interface CharacterData extends Node {
    public String getData( ) throws DOMException;
    public void setData(String data) throws DOMException;
    public int getLength( );
    public String substringData(int offset, int count)
        throws DOMException;
    public void appendData(String arg) throws DOMException;
    public void insertData(int offset, String arg) throws DOMException;
    public void deleteData(int offset, int count) throws DOMException;
    public void replaceData(int offset, int count, String arg)
        throws DOMException;
}

A.2.1.4. Comment

This interface provides a Java representation for an XML comment. Similar to CDATASection, it adds no methods of its own but does allow a distinction (based on the type of the interface) to distinguish between text and comments in an XML document.

public interface Comment extends CharacterData {
}

A.2.1.5. Document

This interface is the DOM representation of a complete XML document. It is also the key for creating new XML elements, attributes, PIs, and other constructs. In addition to allowing retrieval of the DTD declaration (getDocType( )) and root element (getDocumentElement( )), this allows searching through the tree in a pre-order fashion for a specific element (getElementsByTagName( )). Because the DOM model requires that all Node implementations be tied to a DOM Document object, this provides methods for creating the various types of DOM Nodes. Each createXXX( ) method has a complement that supports namespaces through createXXXNS( ). Additionally, Nodes can be imported into this Document through importNode( ); the boolean value indicates if the children of the imported Node should be recursively imported as well.

public interface Document extends Node {
    public DocumentType getDoctype( );
    public DOMImplementation getImplementation( );
    public Element getDocumentElement( );
    public Element createElement(String tagName) throws DOMException;
    public DocumentFragment createDocumentFragment( );
    public Text createTextNode(String data);
    public Comment createComment(String data);
    public CDATASection createCDATASection(String data)
        throws DOMException;
    public ProcessingInstruction
        createProcessingInstruction(String target, String data)
        throws DOMException;
    public Attr createAttribute(String name) throws DOMException;
    public EntityReference createEntityReference(String name)
        throws DOMException;
    public NodeList getElementsByTagName(String tagname);
    public Node importNode(Node importedNode, boolean deep)
        throws DOMException;
    public Element createElementNS(String namespaceURI,
                                   String qualifiedName)
        throws DOMException;
    public Attr createAttributeNS(String namespaceURI,
                                  String qualifiedName)
        throws DOMException;
    public NodeList getElementsByTagNameNS(String namespaceURI,
                                           String localName);
    public Element getElementById(String elementId);
}

A.2.1.6. DocumentFragment

This interface provides for dealing with only a portion of a complete Document object at one time. It is useful for manipulating portions of a DOM tree without having to store the entire tree in memory.

public interface DocumentFragment extends Node {
}

A.2.1.7. DocumentType

This interface represents an XML document's DOCTYPE declaration. The name is the element name immediately following <!DOCTYPE, and the system ID and public ID of any referenced DTD are available as well. Additionally, if any inline entities or notations are present, they can be obtained through the appropriate getXXX( ) methods.

public interface DocumentType extends Node {
    public String getName( );
    public NamedNodeMap getEntities( );
    public NamedNodeMap getNotations( );
    public String getPublicId( );
    public String getSystemId( );
    public String getInternalSubset( );
}

A.2.1.8. DOMException

This class provides an Exception for DOM interfaces to throw when problems occur. It also provides a set of error codes that represent the various problems that occur using DOM and might result in the Exception being thrown.

public class DOMException extends RuntimeException {
       public DOMException(short code, String message);

    // Exception codes
    public static final short INDEX_SIZE_ERR;
    public static final short DOMSTRING_SIZE_ERR;
    public static final short HIERARCHY_REQUEST_ERR;
    public static final short WRONG_DOCUMENT_ERR;
    public static final short INVALID_CHARACTER_ERR;
    public static final short NO_DATA_ALLOWED_ERR;
    public static final short NO_MODIFICATION_ALLOWED_ERR;
    public static final short NOT_FOUND_ERR;
    public static final short NOT_SUPPORTED_ERR;
    public static final short INUSE_ATTRIBUTE_ERR;
    public static final short INVALID_STATE_ERR;
    public static final short SYNTAX_ERR;
    public static final short INVALID_MODIFICATION_ERR;
    public static final short NAMESPACE_ERR;
    public static final short INVALID_ACCESS_ERR;
}

A.2.1.9. DOMImplementation

This interface attempts to provide a standard entry point for accessing vendor- specific DOM implementations, and allowing the creation of a DocumentType and Document within those vendor implementations.[30] It also provides a method (hasFeature( )) for querying the implementation for a specific feature support, like the DOM Level 2 Traversal or Range modules.

[30]Unfortunately, to obtain an instance of a DOMImplementation, you must have a Document object and use getDOMImplementation( ), or directly load the vendor's classes. This tends to result in a chicken-and-egg scenario; see Chapter 5, "DOM" and Chapter 6, "Advanced DOM" for more details.

public interface DOMImplementation {
    public boolean hasFeature(String feature, String version);
    public DocumentType createDocumentType(String qualifiedName,
                                           String publicId,
                                           String systemId)
        throws DOMException;
    public Document createDocument(String namespaceURI,
                                   String qualifiedName,
                                   DocumentType doctype)
        throws DOMException;
}

A.2.1.10. Element

This interface provides a Java representation of an XML element. It provides methods to get its name and attributes, as well as to set these values. It also supplies several flavors of access to the XML attributes, including namespace-aware versions of the getXXX( ) and setXXX( ) methods.

public interface Element extends Node {
    public String getTagName( );
    public String getAttribute(String name);
    public void setAttribute(String name, String value)
        throws DOMException;
    public void removeAttribute(String name) throws DOMException;
    public Attr getAttributeNode(String name);
    public Attr setAttributeNode(Attr newAttr) throws DOMException;
    public Attr removeAttributeNode(Attr oldAttr) throws DOMException;
    public NodeList getElementsByTagName(String name);
    public String getAttributeNS(String namespaceURI, String localName);
    public void setAttributeNS(String namespaceURI, String qualifiedName,
                               String value)
        throws DOMException;
    public void removeAttributeNS(String namespaceURI, String localName)
                                  throws DOMException;
    public Attr getAttributeNodeNS(String namespaceURI, String localName);
    public Attr setAttributeNodeNS(Attr newAttr) throws DOMException;
    public NodeList getElementsByTagNameNS(String namespaceURI,
                                           String localName);
    public boolean hasAttribute(String name);
    public boolean hasAttributeNS(String namespaceURI, String localName);
}

A.2.1.11. Entity

This provides a Java representation of an entity (parsed or unparsed) in an XML document. Access to the system ID and public ID as well as the notation for the entity (from the DTD) is provided through accessor methods.

public interface Entity extends Node {
    public String getPublicId( );
    public String getSystemId( );
    public String getNotationName( );
}

A.2.1.12. EntityReference

This interface represents the resulting value from an entity reference once the entity has been resolved. This interface assumes that character and predefined entity references have already occurred when this interface is exposed to the application client.

public interface EntityReference extends Node {
}

A.2.1.13. NamedNodeMap

This interface defines a list, much like NodeList, but requires that each Node in the list be a named Node (such as an Element or Attr). Because of this requirement, methods can be provided to access members of the list by their names (with or without namespace support). The list also provides for removal and modification of its members. These methods all throw DOMExceptions when the referenced Node is read-only.

public interface NamedNodeMap {
    public Node getNamedItem(String name);
    public Node setNamedItem(Node arg) throws DOMException;
    public Node removeNamedItem(String name) throws DOMException;
    public Node item(int index);
    public int getLength( );
    public Node getNamedItemNS(String namespaceURI, String localName);
    public Node setNamedItemNS(Node arg) throws DOMException;
    public Node removeNamedItemNS(String namespaceURI, String localName)
        throws DOMException;
}

A.2.1.14. Node

This is the central interface for all DOM objects. It provides a robust set of methods for accessing information about a Node in the DOM tree. It also allows for handling of a Node's children (if they exist). While most of the methods are self-explanatory, there are several methods worth noting: getAttributes( ) only returns non-null data if the Node is an Element; cloneNode( ) provides for a shallow or deep copy of a Node; normalize( ) moves all text into non-adjacent Text nodes (no two Text nodes are adjacent, and all resolved textual entity references are consolidated into Text nodes); and isSupported( ) provides information about the feature set of the Node. Namespace-aware methods are also provided (getNamespaceURI( ), getPrefix( ), and getLocalName( )). Finally, a set of constants is provided for identifying the type of a Node by comparing the constants against the result of getNodeType( ).

public interface Node {
    public String getNodeName( );
    public String getNodeValue( ) throws DOMException;
    public void setNodeValue(String nodeValue) throws DOMException;
    public short getNodeType( );
    public Node getParentNode( );
    public NodeList getChildNodes( );
    public Node getFirstChild( );
    public Node getLastChild( );
    public Node getPreviousSibling( );
    public Node getNextSibling( );
    public NamedNodeMap getAttributes( );
    public Document getOwnerDocument( );
    public Node insertBefore(Node newChild,  Node refChild)
        throws DOMException;
    public Node replaceChild(Node newChild, Node oldChild)
                             throws DOMException;
    public Node removeChild(Node oldChild) throws DOMException;
    public Node appendChild(Node newChild) throws DOMException;
    public boolean hasChildNodes( );
    public Node cloneNode(boolean deep);
    public void normalize( );
    public boolean isSupported(String feature, String version);
    public String getNamespaceURI( );
    public String getPrefix( );
    public void setPrefix(String prefix) throws DOMException;
    public String getLocalName( );
    public boolean hasAttributes( );

    // Node Type Constants
    public static final short ELEMENT_NODE;
    public static final short ATTRIBUTE_NODE;
    public static final short TEXT_NODE;
    public static final short CDATA_SECTION_NODE;
    public static final short ENTITY_REFERENCE_NODE;
    public static final short ENTITY_NODE;
    public static final short PROCESSING_INSTRUCTION_NODE;
    public static final short COMMENT_NODE;
    public static final short DOCUMENT_NODE;
    public static final short DOCUMENT_TYPE_NODE;
    public static final short DOCUMENT_FRAGMENT_NODE;
    public static final short NOTATION_NODE;
}

A.2.1.15. NodeList

This interface is a DOM structure analogous to a Java Vector or List. It is the return value of any method that supports multiple Node implementations as a result. This allows iteration through the items as well as providing the ability to get a Node at a specific index.

public interface NodeList {
    public Node item(int index);
    public int getLength( );
}

A.2.1.16. Notation

This interface represents a NOTATION construct in a DTD, used to declare the format of an unparsed entity or for declaration of PIs. This provides access to both the system ID and public ID within the declaration. Both return null if they are not present.

public interface Notation extends Node {
    public String getPublicId( );
    public String getSystemId( );
}

A.2.1.17. ProcessingInstruction

This interface represents an XML processing instruction (PI). It provides methods for getting the target and the data of the PI. Note that there is no means of accessing the "name/value" pairs within the PI individually. The data can also be set for the PI.

public interface ProcessingInstruction extends Node {
    public String getTarget( );
    public String getData( );
    public void setData(String data) throws DOMException;
}

A.2.1.18. Text

This interface provides a Java representation of an XML element's textual data. The only method it adds to those defined in CharacterData is one that will split the node into two nodes. The original Text node contains text up to the specified offset, and the method returns a new Text node with the text after the offset. Like other mutability methods, a DOMException is thrown when the node is read-only.

public interface Text extends CharacterData {
    public Text splitText(int offset) throws DOMException;
}
Previous | Home | Next
A. API Reference | Book Index | A.3. JAXP 1.1
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JAXP 1.1 (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

A.3. JAXP 1.1

JAXP provides an abstraction layer over the process of getting a vendor's implementation of a SAX or DOM parser, as well as providing transformations in a vendor-neutral way.

A.3.1. Package: javax.xml.parsers

This is the single package used in JAXP, and details the classes needed for the JAXP abstraction and pluggability layer over XML parsing.

A.3.1.1. DocumentBuilder

This class is the wrapper over an underlying parser implementation class. It allows parsing to occur in a vendor-neutral way.

public abstract class DocumentBuilder {
    public Document parse(InputStream stream)
        throws SAXException, IOException, IllegalArgumentException;
    public Document parse(InputStream stream, String systemID)
        throws SAXException, IOException, IllegalArgumentException;
    public Document parse(String uri)
        throws SAXException, IOException, IllegalArgumentException;
    public Document parse(File file)
        throws SAXException, IOException, IllegalArgumentException;
    public abstract Document parse(InputSource source)
        throws SAXException, IOException, IllegalArgumentException;

    public abstract Document newDocument( );
    public abstract boolean isNamespaceAware( );
    public abstract boolean isValidating( );
    public abstract void setEntityResolver(EntityResolver er);
    public abstract void setErrorHandler(ErrorHandler eh);
    public DOMmplementation getDOMImplementation( );
}

A.3.1.2. DocumentBuilderFactory

This class is the factory used to create instances of the DocumentBuilder class, and allows namespace and validation features to be set for the production of those instances.

public abstract class DocumentBuilderFactory {
    public static DocumentBuilderFactory newInstance( );
    public abstract DocumentBuilder newDocumentBuilder( )
        throws ParserConfigurationException;

    public void setAttribute(String name, Object value);
    public void setCoalescing(boolean coalescing);
    public void setExpandEntityReferences(boolean expand);
    public void setIgnoringComments(boolean ignoreComments);
    public void setIgnoringElementContentWhitespace(boolean ignoreWhitespace);
    public void setNamespaceAware(boolean aware);
    public void setValidating(boolean validating);

    public boolean isCoalescing( );
    public boolean isExapandEntityReferences( );
    public boolean isIgnoringComments( );
    public boolean isIgnoreingElementContentWhitespace( );
    public boolean isNamespaceAware( );
    public boolean isValidating( );
    public Object getAttribute(String name);
}

A.3.1.3. FactoryConfigurationError

This defines an Error that is thrown if a factory instance cannot be created.

public class FactoryConfigurationException extends Error {
    public FactoryConfigurationError( );
    public FactoryConfigurationError(String msg);
    public FactoryConfigurationError(Exception e);
    public FactoryConfigurationError(Exception e, String msg);
}

A.3.1.4. ParserConfigurationException

This defines an Exception that is thrown if a parser is requested but cannot be constructed with the specified validation and namespace-awareness settings.

public class ParserConfigurationException extends Exception {
    public ParserConfigurationException( );
    public ParserConfigurationException(String msg);
}

A.3.1.5. SAXParser

This class is the wrapper over an underlying SAX 1.0/2.0 parser implementation class, and allows parsing to occur in a vendor-neutral way. It essentially has a pair of each method: one for SAX 1.0, and one for SAX 2.0.

public abstract class SAXParser {
    public void parse(InputStream stream, HandlerBase base)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(InputStream stream, HandlerBase base, String systemID)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(String uri, HandlerBase base)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(File file, HandlerBase base)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(InputSource source, HandlerBase base)
        throws SAXException, IOException, IllegalArgumentException;

    public void parse(InputStream stream, DefaultHandler dh)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(InputStream stream, DefaultHandler dh, String systemID)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(String uri, DefaultHandler dh)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(File file, DefaultHandler dh)
        throws SAXException, IOException, IllegalArgumentException;
    public void parse(InputSource source, DefaultHandler dh)
        throws SAXException, IOException, IllegalArgumentException;

    public Parser getParser( ) throws SAXException;
    public XMLReader getXMLReader( ) throws SAXException;

    public Object getProperty(String name);
    public void setProperty(String name, Object value);
    public boolean isNamespaceAware( );
    public boolean isValidating( );
}

A.3.1.6. SAXParserFactory

This class is the factory used to create instances of the SAXParser class, and allows namespace and validation features to be set for the production of those instances.

public abstract class SAXParserFactory {
    public static SAXParserFactory newInstance( );
    public SAXParser newSAXParser( )
        throws ParserConfigurationException, SAXException;

    public void setNamespaceAware(boolean aware);
    public void setValidating(boolean validating);
    public void setFeature(String name, boolean value);
    public boolean isNamespaceAware( );
    public boolean isValidating( );
    public boolean getFeature(String name);
}

A.3.2. Package: javax.xml.transform

This is the package used in JAXP for transforming XML documents. It allows these transformations to be pluggable and vendor-neutral, provided they use the TrAX (Transformations API for XML) interfaces defined here.

A.3.2.1. ErrorListener

This interface is analogous to ErrorHandler in SAX, and provides error notification for transformations. Implement it in your own applications using TrAX.

public interface ErrorListener {
    public void warning(TransformerException exception);
    public void error(TransformerException exception);
    public void fatalError(TransformerException exception);
}

A.3.2.2. OutputKeys

This class is just a holder for several static constants used in the rest of the TrAX API.

public class OutputKeys {
    public static final String CDATA_SECTION_ELEMENTS;
    public static final String DOCTYPE_PUBLIC;
    public static final String DOCTYPE_SYSTEM;
    public static final String ENCODING;
    public static final String INDENT;
    public static final String MEDIA_TYPE;
    public static final String METHOD;
    public static final String OMIT_XML_DECLARATION;
    public static final String STANDALONE;
    public static final String VERSION;
}

A.3.2.3. Result

This interface provides for output of XML transformations. Default implementations of this interface are provided in the JAXP javax.xml.transform.* packages.

public interface Result {
    public static final String PI_DISABLE_OUTPUT_ESCAPING;
    public static final String PI_ENABLE_OUTPUT_ESCAPING;

    public String getSystemId( );
    public void setSystemId( );
}

A.3.2.4. Source

This interface provides for input of XML transformations. Default implementations of this interface are provided in the JAXP javax.xml.transform.* packages.

public interface Source {
    public String getSystemId( );
    public void setSystemId( );
}

A.3.2.5. SourceLocator

This interface is analogous to the SAX Locator interface, and details location information about an input to TrAX. Like ErrorListener, it's most useful in error handling and reporting.

public interface SourceLocator {
    public int getColumnNumber( );
    public int getLineNumber( );
    public String getPublicId( );
    public String getSystemId( );
}

A.3.2.6. Templates

This interface is provided to perform a means of optimal transformations using the same stylesheet. Its only methods allow for generation of Transformer instances, and viewing its current set of output properties.

public interface Tempaltes {
    public Properties getOutputProperties( );
    public Transformer newTransformer( );
}

A.3.2.7. Transformer

This is the core (abstract) class for providing XML transformation facilities through TrAX and JAXP. In addition to setting the various properties and objects on the interface, you can perform the actual transformation with the transform( ) method.

public class Transformer {
    public void setErrorListener(ErrorListener errorListener);
    public ErrorListener getErrorListener( );
    public void setURIResolver(URIResolver resolver);
    public URIResolver getURIResolver( );

    public void setOutputProperties(Properties properties);
    public Properties getOutputProperties( );
    public void setOutputProperty(String name, String value);
    public String getOutputProperty(String name);
    public void clearParmaters( );
    public void setParameter(String name, String value);
    public Object getParameter(String name);

    public void transform(Source xmlSource, Result outputTarget);
}

A.3.2.8. TransformerFactory

This is the other "half" of the transformation engine in JAXP. You can specify the stylesheet to use for transformation, and then obtain new instances of a Transformer instance. You can also use this to generate a new Templates object for multiple transformations using the same stylesheet.

public class TransformerFactory {
    public TransformerFactory newInstance( );
    public Transformer newTemplates(Source stylesheet);
    public Transformer newTransformer(Source stylesheet);
    public Transformer newTransformer( );

    public Source getAssociatedStylesheet(Source source, String media,
                                          String title, String charset);
    public ErrorListener getErrorListener( );
    public void setErrorListener(ErrorListener errorListener);
    public URIResolver getURIResolver( );
    public void setURIResolver(URIResolver uriResolver);
    public Object getAttribute(String name);
    public void setAttribute(String name, String value);
    public boolean getFeature(String name);
}

A.3.2.9. URIResolver

This is the interface responsible for URI resolution, and is analogous to the SAX EntityResolver interface.

public interface URIResolver {
    public Source resolve(String href, String base);
}

A.3.3. Package: javax.xml.transform.dom

This package provides two classes: DOMResult and DOMSource . These are implementations of the Result and Source interfaces, and are used when DOM trees should be the input and output of a transformation. Because these are simple implementation classes, their methods are not detailed here; however, their usage is covered in detail in Chapter 9, "JAXP".

A.3.4. Package: javax.xml.transform.sax

This package provides two classes: SAXResult and SAXSource. These are implementations of the Result and Source interfaces, and are used when SAX events should be the input and output of a transformation. Because these are simple implementation classes, their methods are not detailed here; however, their usage is covered in detail in Chapter 9, "JAXP".

A.3.5. Package: javax.xml.transform.stream

This package provides two classes: StreamResult and StreamSource. These are implementations of the Result and Source interfaces, and are used when I/O streams should be the input and output of a transformation. Because these are simple implementation classes, their methods are not detailed here; however, their usage is covered in detail in Chapter 9, "JAXP".

Previous | Home | Next
A.2. DOM Level 2 | Book Index | A.4. JDOM 1.0
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JDOM 1.0 (Beta 7) (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

A.4. JDOM 1.0 (Beta 7)

JDOM 1.0 (beta 7), detailed in Chapter 7, "JDOM" and Chapter 8, "Advanced JDOM", provides a complete view of an XML document within a tree model. Although this model is similar to DOM, it is not as rigid a representation; this allows the content of an Element, for example, to be set directly, instead of setting the value of the child of that Element. Additionally, JDOM provides concrete classes rather than interfaces, allowing instantiation of objects directly rather than through the use of a factory. SAX and DOM are only used in JDOM for the construction of a JDOM Document object from existing XML data, and are detailed in the org.jdom.input package.

A.4.1. Package: org.jdom

This package contains the core classes for JDOM 1.0[31]. These consist of XML objects modeled in Java and a set of Exceptions that can be thrown when errors occur.[32]

[31]Please note that while the JDOM API is fairly stable, it is still in beta. Minor changes may occur after the publication of this book. Please consult http://www.jdom.org for the latest JDOM classes.

[32]To avoid complete boredom in this section, I've left out all the JDOM exceptions aside from the core one, JDOMException. I'd rather focus on the classes rather than the odd exceptional condition.

A.4.1.1. Attribute

Attribute defines behavior for an XML attribute, modeled in Java. Methods allow the user to obtain the value of the attribute as well as namespace information about the attribute. An instance can be created with the name of the attribute and its value, or the Namespace and local name, as well as the value, of the attribute. Several convenience methods are also provided for automatic data conversion of the attribute's value.

public class Attribute {
    public Attribute(String name, String value);
    public Attribute(String name, String value, Namespace ns);

    public Element getParent( );
    public String getName( );
    public Namespace getNamespace( );
    public void setNamespace(Namespace ns);
    public String getQualifiedName( );
    public String getNamespacePrefix( );
    public String getNamespaceURI( );
    public String getValue( );
    public void setValue(String value);

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );

    // Convenience Methods for Data Conversion
    public String get StringValue(String default Value);
    public int getIntValue( ) throws DataConversionException;
    public long getLongValue( ) throws DataConversionException;
    public float getFloatValue( ) throws DataConversionException;
    public double getDoubleValue( ) throws DataConversionException;
    public boolean getBooleanValue( ) throws DataConversionException;
}

A.4.1.2. CDATA

The CDATA class defines behavior for XML CDATA sections.

public class CDATA {
    public CDATA(String text);

    public String getText( );

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.3. Comment

Comment is a simple representation of an XML comment, and contains the text within the XML comment.

public class Comment {
    public Comment(String text);

    public Document getDocument( );
    public Element getParent( );
    public String getText( );
    public void setText(String text);

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.4. DocType

DocType represents a DOCTYPE declaration within an XML document. It includes information about the element name being constrained, as well as the public ID and system ID of the external DTD reference, if one is present.

public class DocType {
    public DocType(String elementName, String publicID, String systemID);
    public DocType(String elementName, String systemID);
    public DocType(String elementName);

    public Document getDocument( );
    public String getElementName( );
    public String getPublicID( );
    public DocType setPublicID(String publicID);
    public String getSystemID( );
    public DocType setSystemID(String systemID);

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.5. Document

Document models an XML document in Java. Document requires that it be created with a root Element, although that Element can be replaced with setRootElement( ). The getContent( ) method returns all the content of the Document, which includes the root Element and any Comments that may exist at the document level in the XML document.

public class Document {
    public Document(Element rootElement, DocType docType);
    public Document(Element rootElement);
    public Document(List content);
    public Document(List content, DocType docType);

    public Document addContent(Comment comment);
    public Document removeContent(Comment comment);
    public Document addContent(ProcessingInstruction pi);
    public Document removeContent(ProcessingInstruction pi);
    public Element getRootElement( ) throws NoSuchElementException;
    public Document setRootElement(Element rootElement);
    public DocType getDocType( );
    public Document setDocType(DocType docType);
    public List getContent( );
    public void setMixedContent(List content);

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.6. Element

Element is a Java representation of an XML element. It is completely namespace-aware, so all methods take in a single element name as an argument, as well as optional namespace information. The result of calls to getText( ) is always a String, either the textual content of the XML element or an empty String. An Element is considered to have mixed content when it has a combination of textual data and nested elements, as well as optional comments, entities, and processing instructions. This complete List of content can be obtained with getContent( ), and the results in the List evaluated through instanceof against a String, Element, or Comment.

The addXXX( ) methods are designed to be chained together, and therefore return the modified Element:

Element element = new Element("root");
element.addChild(new Element("child")
    .addChild(new Element("grandchild")
        .addAttribute("name", "value")
        .setContent("Hello World!"))
    .addChild(new Element("anotherChild"))
);

This would result in the following XML document fragment:

<root>
  <child>
    <grandchild name="value">
      Hello World!
    </grandchild>
  </child>
  <anotherChild />
</root>

Here's the API listing:

public class Element {
    public Element(String name);
    public Element(String name, String uri);
    public Element(String name, String prefix, String uri);
    public Element(String name, Namespace ns);

    public Document getDocument( );
    public Element getParent( );
    public Element detach( );
    public String getName( );
    public void setName(String name);
    public Namespace getNamespace( );
    public Namespace getNamespace(String prefix);
    public void setNamespace(Namespace ns);
    public String getNamespacePrefix( );
    public String getNamespaceURI( );
    public String getQualifiedName( );
    public void addNamespaceDeclaration(Namespace additionalNS);
    public void removeNamespaceDeclaration(Namespace additionalNS);
    public List getAdditionalNamespaces( );

    public List getContent( );
    public Element setMixedContent(List mixedContent);
    public Element addContent(CDATA cdata);
    public Element addContent(Comment comment);
    public Element addContent(Element element);
    public Element addContent(EntityRef entityRef);
    public Element addContent(ProcessingInstruction pi);
    public Element addContent(String text);
    public boolean removeContent(CDATA cdata);
    public boolean removeContent(Comment comment);
    public boolean removeContent(Element element);
    public boolean removeContent(EntityRef entityRef);
    public boolean removeContent(ProcessingInstruction pi);

    public boolean hasChildren( );
    public Element getChild(String name);
    public Element getChild(String name, Namespace ns);
    public List getChildren( );
    public List getChildren(String name);
    public List getChildren(String name, Namespace ns);
    public boolean removeChild(String name);
    public boolean removeChild(String name, Namespace ns);
    public boolean removeChildren( );
    public boolean removeChildren(String name);
    public boolean removeChildren(String name, Namespace ns);
    public Element setChildren(List children);

    public Attribute getAttribute(String name);
    public Attribute getAttribute(String name, Namespace ns);
    public List getAttributes( );
    public String getAttributeValue(String name);
    public String getAttributeValue(String name, Namespace ns);
    public boolean removeAttribute(String name);
    public boolean removeAttribute(String name, Namespace ns);
    public Element setAttribute(Attribute attribute);
    public Element setAttributes(List attributes);

    public String getChildText(String name);
    public String getChildText(String name, Namespace ns);
    public String getChildTextTrim(String name);
    public String getChildTextTrim(String name, Namespace ns);
    public String getText( );
    public String getTextNormalize( );
    public String getTextTrim( );
    public Element setText(String text);

    public boolean isRootElement( );

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.7. EntityRef

This class defines a JDOM model for entity references in XML documents. It allows for setting and accessing of the reference's name, public ID, and system ID.

public class EntityRef {
    public EntityRef(String name);
    public EntityRef(String name, String publicID, String systemID);

    public Document getDocument( );
    public Element getParent( );
    public String getName( );
    public EntityRef setName(String name);
    public String getPublicID( );
    public void setPublicID(String publicID);
    public String getSystemID( );
    public void setSystemID(String systemID);
    public EntityRef detach( );

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.8. JDOMException

This is the core JDOM Exception that other JDOM Exception classes subclass. It provides for error messages as well as the wrapping of a root cause Exception, in the case that a JDOMException needs to wrap a lower-level Exception.

public class JDOMException extends Exception {
    public JDOMException( );
    public JDOMException(String message);
    public JDOMException(String message, Throwable rootCause);

    public Throwable getCause( );
    public String getMessage( );
}

A.4.1.9. Namespace

The Namespace class handles namespace mappings used in JDOM objects.

public class Namespace {
    public static Namespace getNamespace(String uri);
    public static Namespace getNamespace(String prefix, String uri);

    public String getPrefix( );
    public String getURI( );

    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.10. ProcessingInstruction

ProcessingInstruction defines behavior for an XML processing instruction, modeled in Java. It allows specific handling for the target as well as the raw data for the target. Additionally, as many PIs use data in the form of name-value pairs (much like attributes), this allows retrieval and addition of name-value pairs. For example, in the <?cocoon-process type="xslt"?> processing instruction, invoking getValue("type") on the ProcessingInstruction representing that XML PI would return "xslt".

public class ProcessingInstruction {
    public ProcessingInstruction(String target, Map data);
    public ProcessingInstruction(String target, String data);

    public ProcessingInstruction detach( );
    public Document getDocument( );
    public Element getParent( );
    public String getTarget( );
    public String getData( );
    public ProcessingInstruction setData(String data);
    public ProcessingInstruction setData(Map data);
    public String getValue(String name);
    public ProcessingInstruction setValue(String name, String value);
    public boolean removeValue(String name);

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.1.11. Text

This class represents character data "owned" by a JDOM Element. It is generally invisible to the user, as the Element class converts this class to a simple String when the value is requested. It is only exposed through an Element's getContent( ) method.

public class Text {
    public Text(String stringValue);

    public Element getParent( );
    public void append(String stringValue);
    public String getValue( );
    public void setValue(String stringValue);

    public Object clone( );
    public boolean equals(Object obj);
    public int hashCode( );
    public String toString( );
}

A.4.2. Package: org.jdom.adapters

This package contains adapters that allow a standard interface for obtaining a DOM Document object from any DOM parser (including DOM Level 1 parsers). Adapters can be easily added for any parser that desires to have JDOM support.

A.4.2.1. AbstractDOMAdapter

This class provides default behavior for the version of getDocument( ) that takes in a filename by wrapping the file in a FileOutputStream and delegating invocation to getDocument(InputStream).

public abstract class AbstractDOMAdapter implements DOMAdapter {
    public abstract Document getDocument(InputStream in, boolean validate)
        throws IOException;
    public abstract Document getDocument(File filename, boolean validate)
        throws IOException;
    public abstract Document createDocument(DocType docType)
        throws IOException;
}

A.4.2.2. DOMAdapter

This class defines the interface that adapters must implement. This includes a means to produce a DOM Document from a filename or an InputStream, as well as a means of obtaining a new, empty DOM Document object.

public interface DOMAdapter {
    public abstract Document getDocument(InputStream in, boolean validate)
        throws IOException;
    public abstract Document getDocument(File filename, boolean validate)
        throws IOException;
    public abstract Document createDocument(DocType docType)
        throws IOException;
}

Specific adapter classes are not detailed here, as additions and modifications may be made during publication of the book. As of this writing, functional adapters are provided for the following parsers:

  • Oracle Version 1 XML Parser

  • Oracle Version 2 XML Parser

  • Sun Project X Parser

  • Sun/Apache Crimson Parser

  • Apache Xerces Parser

  • IBM XML4J Parser

A.4.3. Package: org.jdom.input

This package defines the classes for building a JDOM Document object from various input sources, such as SAX streams and existing DOM trees. It also provides an interface for using customized versions of the JDOM classes, like user-defined subclasses of Element and Attribute.

A.4.3.1. BuilderErrorHandler

This is the default error handler used for JDOM document construction.

public class BuilderErrorHandler
    implements org.xml.sax.ErrorHandler {

    public void warning(SAXParserException exception);
    public void error(SAXParserException exception);
    public void fatalError(SAXParserException exception);
}

A.4.3.2. DOMBuilder

This class provides the ability to create a JDOM Document object from an XML input source using a parser that supports DOM, the Document Object Model. It uses the various adapters in org.jdom.adapters, so if a parser is requested for which there is no adapter, errors occur. Additionally, a method is provided for building a JDOM Document object from an existing DOM tree (org.w3c.dom.Document). When the DOMBuilder is constructed, validation can be requested, as can the class name of the adapter to use. If neither is supplied, the default behavior occurs: no validation takes place and the Apache Xerces parser is used.

You can also set the factory to use (see the JDOMFactory entry) for generating JDOM classes in the build process.

public class DOMBuilder {
    public DOMBuilder(String adapterClass, boolean validate);
    public DOMBuilder(String adapterClass);
    public DOMBuilder(boolean validate);
    public DOMBuilder( );

    public Document build(InputStream in);
    public Document build(File file);
    public Document build(URL url);
    public Document build(org.w3c.dom.Document domDocument);
    public Element build(org.w3c.dom.Element domElement);

    public void setValidation(boolean validate);
    public void setFactory(JDOMFactory factory);
}

A.4.3.3. JDOMFactory

This interface allows users to provide their own factories that produce JDOM constructs (Element, Attribute, etc.). When a factory implementation is passed to a builder using the setFactory( ) method, this is used for creating new JDOM constructs. It allows complete customization of the JDOM object creation process.

For the sake of brevity and clarity, I'm not going to include the very extensive list of methods available to this factory, but instead refer you to the Javadoc. Every possible construction of every JDOM class is included in this class, and all these methods return the type of object being constructed.

A.4.3.4. SAXBuilder

This class provides the ability to create a JDOM Document object from an XML input source using a parser that supports SAX, the Simple API for XML. It can use any SAX parser implementation that is SAX 2.0-compliant. When the SAXBuilder is constructed, validation can be requested, as well as the class name of the SAX driver to use. If neither is supplied, the default behavior occurs: no validation takes place and the Apache Xerces parser is used.

You can also set the factory to use (see the JDOMFactory entry) for generating JDOM classes in the build process.

public class SAXBuilder {
    public SAXBuilder(String saxDriverClass, boolean validate);
    public SAXBuilder(String saxDriverClass);
    public SAXBuilder(boolean validate);
    public SAXBuilder( );

    public Document build(InputStream in);
    public Document build(InputStream in, String systemID);
    public Document build(InputSource inputSource);
    public Document build(Reader characterStream);
    public Document build(Reader characterStream, String systemID);
    public Document build(File file);
    public Document build(URL url);
    public Document build(org.w3c.dom.Document domDocument);
    public Element build(org.w3c.dom.Element domElement);

    public void setDTDHandler(DTDHandler dtdHandler);
    public void setEntityResolver(EntityResolver entityResolver);
    public void setErrorHandler(ErrorHandler errorHandler);
    public void setExpandEntities(boolean expandEntities);
    public void setXMLFilter(XMLFilter xmlFilter);
    public void setIgnoringElementContentWhitespace(boolean ignore);
    public void setValidation(boolean validate);
    public void setFactory(JDOMFactory factory);
}

A.4.4. Package: org.jdom.output

This package defines behavior for output of JDOM Document objects. Of particular note is the SAXOutputter class, which allows a JDOM Document to fire SAX events off to an application expecting SAX behavior, and DOMOutputter, which converts from JDOM to DOM structures. And, of course, XMLOutputter is by far the most common output class for JDOM objects. Like some of the classes in the org.jdom.input package, these three classes have, literally, hundreds of methods between them. Rather than fill ten pages with this rather boring information, I'll again refer you to the Javadoc online at http://www.jdom.org. That will have the most current options for using any of the JDOM outputter classes.

Previous | Home | Next
A.3. JAXP 1.1 | Book Index | B. SAX 2.0 Features and Properties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX 2.0 Features and Properties (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

Appendix B. SAX 2.0 Features and Properties

Contents:

Core Features
Core Properties

This appendix describes the SAX 2.0 standard features and properties. Although a vendor's parsing software can add additional features and properties for vendor-specific functionality, this list represents the core set of functionality that any SAX 2.0-compliant parser implementation should support.

B.1. Core Features

The core set of features supported by SAX 2.0 XMLReader implementations is listed here. These features can be set through setFeature(), and the value of a feature can be obtained through getFeature(). Any feature can be read-only or read/write; features also may be modifiable only when parsing is occurring, or only when parsing is not occurring. For more information on SAX features and properties, refer to Chapter 2, "Nuts and Bolts" and Chapter 3, "SAX".

B.1.1. Namespace Processing

This feature instructs a parser to perform namespace processing, which causes namespace prefixes, namespace URIs, and element local names to be available through the SAX namespace callbacks (startPrefixMapping() and endPrefixMapping(), as well as certain parameters supplied to startElement() and endElement()). When this feature is true, the processing will occur. When false, namespace processing will not occur (this implies that the namespace prefix reporting feature is on). The default in most parsers is true.

URI: http://xml.org/sax/features/namespaces
Access: Read-only when parsing, read/write when not parsing

B.1.2. Namespace Prefix Reporting

This feature instructs a parser to report the attributes used in namespace declarations, such as the xmlns:[namespace URI] attributes. When this feature is not on (false), namespace-related attributes are not reported, as the parser consumes them in order to discover a namespace prefix to URI mappings, and they are generally not of value to the wrapping application in that context. In addition, when namespace processing is turned on, generally namespace prefix mapping is turned off. The default in most parsers is false.

URI: http://xml.org/sax/features/namespace-prefixes
Access: Read-only when parsing, read/write when not parsing

B.1.3. String Interning

This feature dictates that all element raw and local names, namespace prefixes, and namespace URIs are interned using java.lang.String.intern(). When not on (false), all XML components are left as is. Newer, high-performance parsers usually have this set to false by default, so they can perform their own optimizations for dealing with character data.

URI: http://xml.org/sax/features/string-interning
Access: Read-only when parsing, read/write when not parsin

B.1.4. Validation

This feature requests that validation occur and that any errors as a result of broken constraints be reported through the SAX ErrorHandler interface (if an implementation is registered). When set to false, no validation occurs, which is generally the default setting. You'll need to check your parser vendor's documentation to determine if this feature applies to both DTD and XML Schema validation.

URI: http://xml.org/sax/features/validation
Access: Read-only when parsing, read/write when not parsing

B.1.5. Process External Entities (General)

This feature requests that all general (textual) entities be processed within an XML document. Generally set to true in most parsers by default.

URI: http://xml.org/sax/features/external-general-entities
Access: Read-only when parsing, read/write when not parsing

B.1.6. Process External Entities (Parameter)

This feature requests that all external parameters be parsed, including those in any external DTD's subset. By default, this is also usually true in most parsers.

URI: http://xml.org/sax/features/external-parameter-entities
Access: Read-only when parsing, read/write when not parsing

Previous | Home | Next
A.4. JDOM 1.0 | Book Index | B.2. Core Properties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Core Properties (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book

B.2. Core Properties

Properties provide a way to deal with objects used in the parsing process, particularly when dealing with handlers such as LexicalHandler and DeclHandler that are not in the core set of SAX 2.0 handlers (EntityResolver, DTDHandler, ContentHandler, and ErrorHandler). Any property can be read-only or read/write; features also may be modifiable only when parsing is occurring, or only when parsing is not occurring.

B.2.1. Lexical Handler

This property allows the setting and retrieval of a LexicalHandler implementation to be used for handling of comments and DTD references within an XML document.

URI: http://xml.org/sax/properties/lexical-handler
Datatype: org.xml.sax.ext.LexicalHandler
Access: Read/write when parsing, read/write when not parsing

B.2.2. Declaration Handler

This property allows the setting and retrieval of a DeclHandler implementation to be used for handling of constraints within a DTD.

URI: http://xml.org/sax/properties/declaration-handler
Datatype: org.xml.sax.ext.DeclHandler
Access: Read/write when parsing, read/write when not parsing

B.2.3. DOM Node

When parsing is occurring, this property retrieves the current DOM node (if a DOM iterator is being used). When parsing is not occurring, it retrieves the root DOM node. Most of the parsers I used in testing for this book did not support this property except in very special cases; I wouldn't rely on it providing useful information in the general case.

URI: http://xml.org/sax/properties/dom-node
Datatype: org.w3c.dom.Node
Access: Read-only when parsing, read/write when not parsing

B.2.4. Literal (XML) String

This retrieves the literal characters in the XML document that triggered the event in process when this property is used. Like the DOM node feature, I found little use and little support for this property. Don't depend on it, particularly across parsers.

URI: http://xml.org/sax/properties/xml-string
Datatype: java.lang.String
Access: Read-only when parsing, read-only when not parsing
Previous | Home | Next
B. SAX 2.0 Features and Properties | Book Index | Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Java & XML: Index

Java <!-- **REPLACE WITH BANNER-MAP** -->amp; XML, 2nd Ed.Search this book

Previous Next

Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Symbols & Numbers

& (ampersand): 2.1.1.4. Entity references and constants
16.2. XPointer
&amp: 16.2. XPointer
as entity reference: 2.1.1.4. Entity references and constants
&apos (entity reference): 2.1.1.4. Entity references and constants
&quot (entity reference): 2.1.1.4. Entity references and constants
&gt (entity reference): 2.1.1.4. Entity references and constants
&lt (entity reference): 2.1.1.4. Entity references and constants
@ (attribute name prefix): 2.3.3.2. Looping
\ (backslash), *nix: 7.2.3. Creating XML with JDOM
/ (forward slash): 2.1.1.2. Elements
. (period), splitting property names on: 7.2.3. Creating XML with JDOM
* (recurrence modifier): 2.2.1.1. Elements
; (semicolon): 2.1.1.4. Entity references and constants
<! (opening , DTD tag): 2.2.1.1. Elements

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

AbstractDOMAdapter class: A.4.2.1. AbstractDOMAdapter
abstraction layers: 9.1. API or Abstraction
acceptNode( ): 6.3.2.1. NodeIterator
addBook.pl: 14.1.2. Entering the Books
14.1.3. Providing a Listing of Available Books
addBooks.html: 14.1.2. Entering the Books
addContent( ): 7.2.3. Creating XML with JDOM
7.3.1. Storing XML
addEvent( ): 11.3.1. A Shared Handler
11.3.1. A Shared Handler
Thread, starting: 11.3.1. A Shared Handler
addHandler( ): 11.2.3. Writing the Server
AddItemServlet: 15.2.3. Marshalling and Unmarshalling
Apache Axis: 12.2.1.3. What's Axis?
Apache Cocoon (see Cocoon)
Apache SOAP: 12.2. Setting Up
WSDL, limited functionality with: 13.3. WSDL
Apache Xerces: 1.3.2. A Parser
parser: 3.1.1. Obtaining a Parser
APIs (application program interfaces): 1.2.1. Low-Level APIs
use cases: 15.1.4. Use Cases
appendChild( ): 6.1.1. Creating a New DOM Tree
apply-templates construct: 2.3.3.1. Template matching
architecture, JDOM: 8.1. Helpful JDOM Internals
associations: 4.1.1. Setting Properties and Features
ATTLIST keyword: 2.2.1.2. Attributes
Attr interface: A.2.1.1. Attr
Attribute class: A.4.1.1. Attribute
statement: 7.2.3. Creating XML with JDOM
Attribute class, JDOM: 7.2.3. Creating XML with JDOM
AttributeList interface: A.1.1.1. AttributeList [deprecated]
AttributeListImpl class: A.1.3.1. AttributeListImpl
attributes: 2.1.1.3. Attributes
2.2.1.2. Attributes
9.3.2.1. Working with the factory
quotation marks, and: 2.1.1.3. Attributes
vs. elements: 2.1.1.3. Attributes
Attributes instance: 3.3.5. Element Callbacks
Attributes interface: 3.3.5. Element Callbacks
A.1.1.2. Attributes
AttributesImpl class: A.1.3.2. AttributesImpl
Axis SOAP toolkit: 12.2.1.3. What's Axis?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

baseName variable: 7.3.2. Loading XML
BeanSerializer class: 12.4.1. Custom Parameter Types
binding element: 13.3. WSDL
browser-dependent styling: 10.3.3. Browser-Dependent Styling
builder classes: 7.1.3. Input and Output
BuilderErrorHandler class: A.4.3.1. BuilderErrorHandler
buildTree( ): 3.2.1. Instantiating a Reader
3.2.3. Using an InputSource
throwable exceptions: 3.2.2. Parsing the Document

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

callback methods: 3.2.2. Parsing the Document
documents, start and end: 3.3.2. The Beginning and the End of a Document
cards: 10.3.4. WAP and XML
case sensitivity, elements: 2.1.1.2. Elements
Castor: 15.2. Castor
AddItemServlet: 15.2.3. Marshalling and Unmarshalling
classes, generating: 15.2.2. Source Generation
installation: 15.2.1. Installation
marshalling and unmarshalling: 15.2.3. Marshalling and Unmarshalling
CD class: 12.4.1. Custom Parameter Types
CD Orderer client: 13.4.5. Writing a Client
testing: 13.4.5. Writing a Client
CDAdder class: 12.3.3. An RPC Client
updated version: 12.4.1. Custom Parameter Types
CDATA attribute: 2.2.1.2. Attributes
CDATA class: A.4.1.2. CDATA
CDATA section marker: 2.1.1.5. Unparsed data
CDATASection interface: A.2.1.2. CDATASection
CDCatalog class: 12.3.2.1. Code artifacts
updated: 12.4.1. Custom Parameter Types
CDCatalog deployment descriptors: 12.3.2.2. Deployment descriptors
CDLister class: 12.3.3. An RPC Client
CDs-R-Us Messaging Service: 13.4.1. A Messaging Service
imported classes: 13.4.1. A Messaging Service
channel element: 14.3.2. Creating an XML RSS Document
channels: 14.3.1. Rich Site Summary
chapterTen.xml: 10.4.1. Creating an XSP Page
CharacterData interface: A.2.1.3. CharacterData
characters( ): 3.3.6. Element Data
character arrays and: 3.5.4. Getting Ahead of the Data
whitespace reported by: 3.3.6. Element Data
class instances, sharing: 11.4.2. Shared Instances
classes: 0.6. Acknowledgments
(see also interfaces)
AbstractDOMAdapter: A.4.2.1. AbstractDOMAdapter
Attribute: 7.2.3. Creating XML with JDOM
7.2.3. Creating XML with JDOM
A.4.1.1. Attribute
AttributeListImpl: A.1.3.1. AttributeListImpl
AttributesImpl: A.1.3.2. AttributesImpl
BeanSerializer: 12.4.1. Custom Parameter Types
builder classes: 8.2.2. Building with Custom Classes
BuilderErrorHandler: A.4.3.1. BuilderErrorHandler
builders: 7.1.3. Input and Output
CD: 12.4.1. Custom Parameter Types
CDAdder: 12.3.3. An RPC Client
updated version: 12.4.1. Custom Parameter Types
CDATA: A.4.1.2. CDATA
CDCatalog: 12.3.2.1. Code artifacts
updated: 12.4.1. Custom Parameter Types
CDLister: 12.3.3. An RPC Client
Comment: 7.3.1. Storing XML
A.4.1.3. Comment
DataWriter: 4.3.2. XMLWriter
DefaultHandler: 4.2.3. The DefaultHandler Class
A.1.3.3. DefaultHandler
DefaultMutableTreeNode: 3.2. SAX Readers
DefaultsHandler: 9.3.1.1. The road to SAX 2.0
DefaultTreeModel: 3.2. SAX Readers
DocType: A.4.1.4. DocType
Document: A.4.1.5. Document
DocumentBuilder: 13.4.5. Writing a Client
A.3.1.1. DocumentBuilder
DocumentBuilderFactory: 9.3.1. Updating the Standards
A.3.1.2. DocumentBuilderFactory
DOMAdapter: A.4.2.2. DOMAdapter
DOMBuilder: 7.1.3. Input and Output
A.4.3.2. DOMBuilder
performance concerns: 7.5.3. DOMBuilder
7.5.3. DOMBuilder
DOMException: A.2.1.8. DOMException
DOMResult: A.3.3. Package: javax.xml.transform.dom
DOMSerializer: 5.2.3. DOMSerializer
warning: 5.2.4. The Results
DOMSource: A.3.3. Package: javax.xml.transform.dom
Element: A.4.1.6. Element
SetAttribute( ): 7.2.3. Creating XML with JDOM
subclassing: 8.2. JDOM and Factories
EntityRef: 8.1.2. The EntityRef Class
A.4.1.7. EntityRef
Enumeration: 11.3.1. A Shared Handler
FactoryConfigurationError: A.3.1.3. FactoryConfigurationError
FormattingNodeFilter: 6.3.2.1. NodeIterator
generating: 15.1.1. Class Generation
HandlerBase: 9.2.1. Starting with SAX
9.2.1. Starting with SAX
A.1.1.8. HandlerBase
handlers, XML-RPC: 11.2.2. Writing the Handler
Hashtable: 7.2.1. Java Properties Files
HelloServer: 11.2.3. Writing the Server
InputSource: 3.2.3. Using an InputSource
A.1.1.9. InputSource
ItemSearcher: 6.3.2.1. NodeIterator
JDOM, for: 7.1. The Basics
JDOMException: A.4.1.8. JDOMException
JTreeContentHandler: 3.3. Content Handlers
LexicalHandler: 4.4.1. LexicalHandler
Locator: 3.3.1. The Document Locator
A.1.1.10. Locator
LocatorImpl: A.1.3.4. LocatorImpl
MalformedURLException: 11.2.4. Writing the Client
Namespace: 8.1.3. The Namespace Class
A.4.1.9. Namespace
NamespaceSupport: A.1.3.5. NamespaceSupport
OrderProcessor: 13.4.5. Writing a Client
OutputKeys: A.3.2.2. OutputKeys
ParserAdapter: 3.5.1. My Parser Doesn't Support SAX 2.0
A.1.3.6. ParserAdapter
ParserConfigurationExeption: A.3.1.4. ParserConfigurationException
ParserFactory: 9.2.1. Starting with SAX
9.2.1.1. A look at the SAXParserFactory class
A.1.3.7. ParserFactory
ProcessingInstruction: A.4.1.10. ProcessingInstruction
PropsToXML: 7.2. PropsToXML
SAX API: 3.1.2. Getting the SAX Classes and Interfaces
SAXBuilder: 7.1.3. Input and Output
A.4.3.4. SAXBuilder
SAXException: 3.3.2. The Beginning and the End of a Document
A.1.1.12. SAXException
SAXNotRecognizedException: A.1.1.13. SAXNotRecognizedException
SAXNotSupportedException: A.1.1.14. SAXNotSupportedException
SAXParseException: A.1.1.15. SAXParseException
SAXParser: 9.2.1.2. Working with the SAXParser class
A.3.1.5. SAXParser
SAXParserFactory: 9.3.1. Updating the Standards
A.3.1.6. SAXParserFactory
SAXTreeViewer: 3.2. SAX Readers
3.2.1. Instantiating a Reader
Scheduler: 11.3.1. A Shared Handler
SchedulerClient: 11.3.3. A Useful Client
SerializerTest: 5.2.1. Getting a DOM Parser
SimpleDateFormat: 11.3.1. A Shared Handler
SimpleXPathViewer: 8.3.4. Endgame
SOAPMappingRegistry: 12.4.1. Custom Parameter Types
StreamResult: 9.3.2.2. Transforming XML
StreamSource: 9.3.2.2. Transforming XML
TestXMLProperties: 7.3.4. Backtracking
Text: 8.1.1. The Text Class
A.4.1.11. Text
Thread: 11.3.1. A Shared Handler
Transformer: A.3.2.7. Transformer
TransformerFactory: 9.3.2.1. Working with the factory
A.3.2.8. TransformerFactory
UpdateItemServlet: 6.1.1. Creating a New DOM Tree
6.3.3. Range
XML parsers: 3.1.1. Obtaining a Parser
XMLFilter: 4.3.1. XMLFilters
A.1.1.16. XMLFilter
XMLFilterImpl: 4.3.1. XMLFilters
A.1.3.8. XMLFilterImpl
XMLOutputter: 7.2.4. Outputting XML with JDOM
XMLProperties: 7.3. XMLProperties
incompatible versions: 7.3.3. Taking a Test Drive
skeleton: 7.3. XMLProperties
testing: 7.3.3. Taking a Test Drive
XMLReaderAdapter: A.1.3.9. XMLReaderAdapter
XMLReaderFactory: A.1.3.10. XMLReaderFactory
XmlRpc: 11.2.3. Writing the Server
XmlRpcClient: 11.2.4. Writing the Client
11.2.4. Writing the Client
XmlRpcServer: 11.2.2. Writing the Handler
XMLUtils: 13.4.5. Writing a Client
XMLWriter: 4.3.2. XMLWriter
XPathDisplayNode: 8.3.3. Providing Support for XPath
XSLTProcessor: 14.2.2. XSLT from a Servlet
XSLTProcessorFactory: 14.2.2. XSLT from a Servlet
ClassNotFoundException, setDriver( ): 11.2.3. Writing the Server
client application, XML-RPC: 11.2.4. Writing the Client
closing tags: 2.1.1.2. Elements
Cocoon: 6.3.5. HTML
10.1.4. Making the Decision
browser-dependent styling: 10.3.3. Browser-Dependent Styling
configuration: 10.2.2. Configuring the Servlet Engine
online resources, source and binaries: 10.2.1. Source Code or Binaries
production usage: 10.1.3. Production Presence
Tomcat configuration: 10.2.2. Configuring the Servlet Engine
context: 10.2.2. Configuring the Servlet Engine
WML, outputting: 10.3.4. WAP and XML
cocoon.properties file: 10.3.3. Browser-Dependent Styling
code artifacts: 12.3.2.1. Code artifacts
Comment class: 7.3.1. Storing XML
A.4.1.3. Comment
Comment interface: A.2.1.4. Comment
compareDocumentOrder( ): 6.4.2. Node Comparisons
compareTreePosition( ): 6.4.2. Node Comparisons
6.4.2. Node Comparisons
constant type data values: 2.1.1.4. Entity references and constants
constraints: 2.2. Constraints
constructors, JDOM Element class: 8.2.1. Creating a Factory
content handlers: 3.3. Content Handlers
content models: 2.2.1.1. Elements
ContentHandler instance: 3.5.3. The Misplaced Locator
ContentHandler interface: A.1.1.3. ContentHandler
implementation: 3.3. Content Handlers
contents.xml: 3.1.3. Have an XML Document on Hand
10.3.3. Browser-Dependent Styling
10.3.3. Browser-Dependent Styling
WAP stylesheet reference: 10.3.4. WAP and XML
copy-of construct: 2.3.3.3. Copying
core handler interfaces: 3.3. Content Handlers
createDocType( ): 6.1.1. Creating a New DOM Tree
createDocument( ): 6.1.1. Creating a New DOM Tree
6.2. Namespaces
createElement( ): 6.1.1. Creating a New DOM Tree
createElementNS( ): 6.2. Namespaces
createRange( ): 6.3.3. Range
createTextNode( ): 6.1.1. Creating a New DOM Tree
createXMLRepresentation( ): 7.2.3. Creating XML with JDOM
7.2.3. Creating XML with JDOM
creation methods and namespaces: 6.2. Namespaces
CVS, online resources: 10.2.1. Source Code or Binaries

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

data
elements, contained in: 3.3.6. Element Data
textual: 3.3.6. Element Data
XML document headers, in: 2.1.1. XML 1.0
data binding: 1.2.2. High-Level APIs
15. Data Binding
Castor framework: 15.2. Castor
classes, generating: 15.2.2. Source Generation
installation: 15.2.1. Installation
marshalling and unmarshalling: 15.2.3. Marshalling and Unmarshalling
class generation: 15.1.1. Class Generation
JAXB: 15.4. JAXB
marshalling and unmarshalling: 15.1.2. Unmarshalling
use cases: 15.1.4. Use Cases
Zeus framework: 15.3. Zeus
classes, generating: 15.3.2. Class Generation
installation: 15.3.1. Installation
marshalling and unmarshalling: 15.3.3. Marshalling and Unmarshalling
data structures, URI mappings, storage in: 3.3.4. Namespace Callbacks
data types, user-defined in SOAP: 12.1.2. Encoding
DataWriter class: 4.3.2. XMLWriter
deck: 10.3.4. WAP and XML
DeclHandler interface: 4.4.2. DeclHandler
A.1.2.1. DeclHandler
decorators: 8.3. Wrappers and Decorators
default namespaces, JDOM: 8.1.3. The Namespace Class
DefaultHandler class: 4.2.3. The DefaultHandler Class
9.3.1.1. The road to SAX 2.0
A.1.3.3. DefaultHandler
DefaultMutableTreeNode class: 3.2. SAX Readers
DefaultTreeModel class: 3.2. SAX Readers
deferred DOM: 5.4.1. Memory, Performance, and Deferred DOMs
deleteContents( ): 6.3.3. Range
deployment descriptors: 12.3.2.2. Deployment descriptors
DocType class: A.4.1.4. DocType
Document class: A.4.1.5. Document
Document interface: 5.2.3.3. Working with nodes
A.2.1.5. Document
document locator: 3.3.1. The Document Locator
Document node: 5.2.3.3. Working with nodes
Document Object Model (see DOM)
DocumentBuilder class: 13.4.5. Writing a Client
A.3.1.1. DocumentBuilder
DocumentBuilderFactory class: A.3.1.2. DocumentBuilderFactory
JAXP v. 1.1: 9.3.1. Updating the Standards
DocumentFragment interface: A.2.1.6. DocumentFragment
DocumentHandler interface: A.1.1.4. DocumentHandler
DocumentRange interface: 6.3.3. Range
DocumentType interface: A.2.1.7. DocumentType
doGet( ): 6.1.2. Modifying a DOM Tree
DOM (Document Object Model): 1.2.1. Low-Level APIs
5. DOM
advanced topics: 6. Advanced DOM
common problems: 6.5. Gotcha!
deferred DOM: 5.4.1. Memory, Performance, and Deferred DOMs
HTML, and: 6.3.5. HTML
JAXP
methods, Level 2: 9.3.1.3. Updates to the DOM
Version 1.0, using with: 9.2.2. Dealing with DOM
versions supported under: 9.1.2. The Old and the New
Level 3: 6.4. DOM Level 3
bootstrapping: 6.4.3. Bootstrapping
Level 2 modules: 6.3. DOM Level 2 Modules
Events: 6.3.4.1. Events
online resources: 6.3.6. Odds and Ends
Range: 6.3.3. Range
Style: 6.3.4.3. Style
Traversal: 6.3.2. Traversal
Views: 6.3.4.2. Views
memory requirements with XML documents: 5.4.1. Memory, Performance, and Deferred DOMs
module specifications, Levels 1 and 2: 6.3.1. Branching Out
namespace declarations, and: 6.2. Namespaces
namespace support, Level 2: 6.2. Namespaces
online resources: 5.1. The Document Object Model
organization in levels: 5.1. The Document Object Model
overloading options, lack of: 6.2. Namespaces
parsers: 5.2.1. Getting a DOM Parser
Level 2, online sources: 6.3. DOM Level 2 Modules
output: 5.2.2. DOM Parser Output
throwing SAX exceptions: 5.4.3. DOM Parsers Throwing SAX Exceptions
risks, memory and data: 5.4.1. Memory, Performance, and Deferred DOMs
SAX, contrasted with: 5.1. The Document Object Model
5.1.3.1. SAX is sequential
standardization: 7.4.2. SAX and DOM as Standards
structures, comparing: 6.4.2. Node Comparisons
trees
creating: 6.1.1. Creating a New DOM Tree
memory requirements: 5.1.3.3. Why use SAX at all?
modifying: 6.1.2. Modifying a DOM Tree
mutability: 6.1. Changes
serialization: 5.2. Serialization
unappended nodes: 6.5.2. Creating, Appending, and Inserting
web publishing frameworks, support by: 10.1.2. Integration with Other XML Tools and APIs
wrong document exception: 6.5.1. The Dreaded WRONG DOCUMENT Exception
dom-node property: 4.1.2.4. DOM nodes and literal strings
DOMAdapter class: A.4.2.2. DOMAdapter
DOMBuilder class: 7.1.3. Input and Output
A.4.3.2. DOMBuilder
performance concerns: 7.5.3. DOMBuilder
DOMException class: A.2.1.8. DOMException
DOMFaultListener: 12.4.2. Better Error Handling
DOMImplementation interface: A.2.1.9. DOMImplementation
DOMOutputter: 7.1.3. Input and Output
8.1.3. The Namespace Class
A.4.4. Package: org.jdom.output
DOMResult class: A.3.3. Package: javax.xml.transform.dom
DOMSerializer class: 5.2.3. DOMSerializer
skeleton: 5.2.3.1. Getting started
warning: 5.2.4. The Results
DOMSerializer.java: 5.2.3.1. Getting started
DOMSource class: A.3.3. Package: javax.xml.transform.dom
doPost( ): 6.1.1. Creating a New DOM Tree
6.3.3. Range
namespaces, updating: 6.2. Namespaces
DTD (Document Type Definition): 2.2.1. DTDs
additional constraint models: 2.2.2.2. Multiple namespaces
attributes: 2.2.1.2. Attributes
contrasted with validation: 4.1.2.1. Validation
ELEMENT definitions: 2.2.1.1. Elements
entities: 2.2.1.3. Entities
recurrence modifiers: 2.2.1.1. Elements
RSS and: 14.3.1. Rich Site Summary
DTDHandler interface: 4.2.2. Using a DTDHandler
A.1.1.5. DTDHandler
and validation: 4.5.2. DTDHandler and Validation
dynamic content generation: 10. Web Publishing Frameworks

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

Electric XML: 1.3.2. A Parser
Element class: A.4.1.6. Element
setAttribute( ), JDOM: 7.2.3. Creating XML with JDOM
subclassing: 8.2. JDOM and Factories
Element constructor: 7.2.3. Creating XML with JDOM
Element interface: A.2.1.10. Element
ELEMENT keyword: 2.2.1.1. Elements
element methods, JDOM: 8.2.1. Creating a Factory
elements: 2.1.1.2. Elements
attributes: 2.1.1.3. Attributes
case sensitivity: 2.1.1.2. Elements
child vs. parent: 3.3.6. Element Data
consolidation of opening and closing tags: 2.1.1.2. Elements
data contained in: 3.3.6. Element Data
empty: 2.1.1.2. Elements
2.2.1.1. Elements
JDOM, creation with: 7.2.3. Creating XML with JDOM
mandatory closure: 2.1.1.2. Elements
naming conventions: 2.1.1.2. Elements
prefixes and namespaces: 6.2. Namespaces
Q names: 3.3.5. Element Callbacks
root elements: 2.1.1.1. The root element
SAX callbacks: 3.3.5. Element Callbacks
vs. attributes: 2.1.1.3. Attributes
EMPTY keyword: 2.2.1.1. Elements
encoding, SOAP: 12.1.2. Encoding
endDocument( ): 3.3.2. The Beginning and the End of a Document
entities: 2.2.1.3. Entities
entity handling: 4.1.2.3. Interning and entities
Entity interface: A.2.1.11. Entity
ENTITY keyword: 2.2.1.3. Entities
entity references: 2.1.1.4. Entity references and constants
JDOM builders, expansion under: 8.1.2. The EntityRef Class
EntityRef class: 8.1.2. The EntityRef Class
A.4.1.7. EntityRef
EntityReference interface: A.2.1.12. EntityReference
EntityResolver interface: 3.3. Content Handlers
4.2.1. Using an EntityResolver
A.1.1.6. EntityResolver
enumerateItem( ): 15.2.2. Source Generation
envelope, SOAP: 12.1.1. The Envelope
equals( ): 8.1.3. The Namespace Class
equalsNode( ): 6.4.2. Node Comparisons
error handling, SOAP: 12.4.2. Better Error Handling
ErrorHandler interface: 3.4. Error Handlers
A.1.1.7. ErrorHandler
ErrorListener interface: 9.3.2.1. Working with the factory
A.3.2.1. ErrorListener
errors
document not well-formed: 3.4.4. Breaking the Data
during parsing: 3.3.2. The Beginning and the End of a Document
fatal and non-fatal: 3.4.2. Nonfatal Errors
version of XML and: 3.4.4. Breaking the Data
escaping characters: 2.1.1.4. Entity references and constants
Events module: 6.3.4.1. Events
eventsSorted, boolean member variable: 11.3.1. A Shared Handler
exceptions, catching: 4.1.2.2. Namespaces
exclude-result-prefixes: 10.3.4. WAP and XML
Exolab FTP site: 15.2.1. Installation
expandEntities flag: 8.1.2. The EntityRef Class
Extensible Markup Language (see XML)
Extensible Stylesheet Language (see XSL)
Extensible Stylesheet Language Transformations (see XSLT)
external-general-entities: 4.1.2.3. Interning and entities
external-parameter-entities: 4.1.2.3. Interning and entities
extractContents( ): 6.3.3. Range

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

factories, JDOM: 8.2. JDOM and Factories
creating: 8.2.1. Creating a Factory
FactoryConfigurationError class: A.3.1.3. FactoryConfigurationError
fatal errors: 3.4.3. Fatal Errors
fault listeners: 12.3.2.2. Deployment descriptors
fault.getString( ): 12.4.2. Better Error Handling
features: 4.1.1. Setting Properties and Features
external-general-entities: 4.1.2.3. Interning and entities
external-parameter-entities: 4.1.2.3. Interning and entities
string-interning: 4.1.2.3. Interning and entities
validation: 4.1.2.1. Validation
file path specification, parsing documents: 3.2.2. Parsing the Document
filters: 4.3.1. XMLFilters
Foobar Public Library: 14.1. The Foobar Public Library
for-each construct: 2.3.3.2. Looping
formatting objects: 2.3.1.2. Formatting objects
FormattingNodeFilter class: 6.3.2.1. NodeIterator
forward slash (/): 2.1.1.2. Elements

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

generateRSSContentMethod( ): 14.3.2. Creating an XML RSS Document
getAttribute( ): 9.3.2.1. Working with the factory
getAttributes( ): 5.2.3.3. Working with nodes
getAttributeValue( ): 7.3.4. Backtracking
getBody( ): 13.4.1. A Messaging Service
getChild( ): 7.2.3. Creating XML with JDOM
getChildNodes( ): 5.2.3.3. Working with nodes
getChildren( ): 7.3.2. Loading XML
getColumnNumber( ): 3.3.1. The Document Locator
getContent( ): 7.3.4. Backtracking
7.3.4. Backtracking
getDetailEntries( ): 12.4.2. Better Error Handling
getDocument( ): 5.2.2. DOM Parser Output
getDocumentElement( ): 5.2.3.3. Working with nodes
getElementsByTagNameNS( ): 6.2. Namespaces
getException( ): 3.3.2. The Beginning and the End of a Document
getFeature( ): 4.1.1. Setting Properties and Features
9.3.1.2. Changes in SAX classes
getFirstChild( ): 6.1.2. Modifying a DOM Tree
getInstance( ): 11.4.2. Shared Instances
getItem( ): 15.2.2. Source Generation
getLineNumber( ): 3.3.1. The Document Locator
getListOfEvents( ): 11.3.1. A Shared Handler
getLocalName(int index) method: 3.3.5. Element Callbacks
getNamespace( ), JDOM: 8.1.3. The Namespace Class
getNamespaceURI( ): 6.2. Namespaces
getNodeName( ): 5.2.3.3. Working with nodes
getNodeType( ): 5.2.3.3. Working with nodes
getNodeValue( ): 5.4.2. Polymorphism and the Node Interface
textual data, printing: 5.2.3.3. Working with nodes
getParent( ): 4.3.1. XMLFilters
getPrefix( ): 6.2. Namespaces
6.2. Namespaces
getProperty( ): 4.1.1. Setting Properties and Features
7.2.3. Creating XML with JDOM
9.3.1.2. Changes in SAX classes
getRootElement( ): 7.3.2. Loading XML
GetRSSChannelServlet.java: 14.3.2. Creating an XML RSS Document
getText( ): 7.3.2. Loading XML
getTextTrim( ): 7.3.2. Loading XML
getURI(int index) method: 3.3.5. Element Callbacks
getXMLReader( ): 9.3.1.1. The road to SAX 2.0
getXPath( ): 8.3.3. Providing Support for XPath
Glue: 13.4.4. WSDL

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

HandlerBase class: 9.2.1. Starting with SAX
9.2.1. Starting with SAX
A.1.1.8. HandlerBase
handlers: 3.3. Content Handlers
DTDHandler: 4.2.2. Using a DTDHandler
EntityResolver: 4.2.1. Using an EntityResolver
implementation classes, registering: 3.3. Content Handlers
instance, adding to: 11.4.2. Shared Instances
Scheduler class: 11.3.1. A Shared Handler
XML-RPC: 11.2.2. Writing the Handler
11.3.1. A Shared Handler
Hashtable class: 7.2.1. Java Properties Files
headers: 2.1.1. XML 1.0
HelloClient.java: 11.2.4. Writing the Client
HelloHandler class, registration: 11.2.3. Writing the Server
HelloServer class: 11.2.3. Writing the Server
helma.xmlrpc.XmlRpcClient class: 11.2.4. Writing the Client
helma.xmlrpc.XmlRpcServer class: 11.2.2. Writing the Handler
high-level APIs: 1.3.3. APIs
HTML (Hyper-Text Markup Language) and DOM: 6.3.5. HTML
HTML interface for XML data: 14.1.2. Entering the Books
HTTP listener, creating: 11.2.3. Writing the Server

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

IBM SOAP4J: 12.2.1.1. What about IBM SOAP4J?
IBM XML4J: 1.3.2. A Parser
ignorableWhitespace( ): 3.3.6. Element Data
IIOP (Internet Inter-ORB Protocol): 11.1.1. What Is RMI?
indentLevel variable: 5.2.3.2. Launching serialization
InputSource class: 3.2.3. Using an InputSource
A.1.1.9. InputSource
instances, sharing: 11.4.2. Shared Instances
interfaces: 0.6. Acknowledgments
(see also classes)
APIs: 1.2.1. Low-Level APIs
Attr: A.2.1.1. Attr
AttributeList: A.1.1.1. AttributeList [deprecated]
Attributes: 3.3.5. Element Callbacks
A.1.1.2. Attributes
CDATASection: A.2.1.2. CDATASection
CharacterData: A.2.1.3. CharacterData
Comment: A.2.1.4. Comment
ContentHandler: 3.3. Content Handlers
A.1.1.3. ContentHandler
core handlers: 3.3. Content Handlers
DeclHandler: 4.4.2. DeclHandler
A.1.2.1. DeclHandler
Document: 5.2.3.3. Working with nodes
A.2.1.5. Document
DocumentFragment: A.2.1.6. DocumentFragment
DocumentHandler: A.1.1.4. DocumentHandler
DocumentRange: 6.3.3. Range
DocumentType: A.2.1.7. DocumentType
DOMImplementation: A.2.1.9. DOMImplementation
DTDHandler: 4.2.2. Using a DTDHandler
4.5.2. DTDHandler and Validation
A.1.1.5. DTDHandler
Element: A.2.1.10. Element
Entity: A.2.1.11. Entity
EntityReference: A.2.1.12. EntityReference
EntityResolver: 4.2.1. Using an EntityResolver
A.1.1.6. EntityResolver
ErrorHandler: 3.4. Error Handlers
A.1.1.7. ErrorHandler
ErrorListener: 9.3.2.1. Working with the factory
A.3.2.1. ErrorListener
handler interfaces: 0.6. Acknowledgments
3.3. Content Handlers
HTML for XML data: 14.1.2. Entering the Books
JDOMFactory: 8.2.1. Creating a Factory
A.4.3.3. JDOMFactory
JDOMNode: 8.3.1. JDOMNode
LexicalHandler: A.1.2.2. LexicalHandler
NameNodeMap: A.2.1.13. NamedNodeMap
Node: 5.2.3.3. Working with nodes
A.2.1.14. Node
NodeFilter: 6.3.2.1. NodeIterator
NodeIterator: 6.3.2.1. NodeIterator
NodeList: A.2.1.15. NodeList
Notation: A.2.1.16. Notation
org.xml.sax.Parser: 3.2. SAX Readers
org.xml.sax.XMLReader: 3.2. SAX Readers
3.2.1. Instantiating a Reader
Parser: A.1.1.11. Parser
ProcessingInstruction: A.2.1.17. ProcessingInstruction
Result: A.3.2.3. Result
SAXParser: 9.3.1.1. The road to SAX 2.0
Source: A.3.2.4. Source
SourceLocator: A.3.2.5. SourceLocator
Templates: A.3.2.6. Templates
Text: A.2.1.18. Text
TreeWalker: 6.3.2.2. TreeWalker
URIResolver: 9.3.2.1. Working with the factory
A.3.2.9. URIResolver
XMLReader: 3.2.2. Parsing the Document
4.1.1. Setting Properties and Features
A.1.1.17. XMLReader
Internet Inter-ORB Protocol (IIOP): 11.1.1. What Is RMI?
interoperability: 13.1. Web Services
invocation in SOAP: 12.1.3. Invocation
IOException: 5.4.3. DOM Parsers Throwing SAX Exceptions
isNamespaceAware( ): 9.2.1.2. Working with the SAXParser class
isSameNode( ): 6.4.2. Node Comparisons
isValidating( ): 9.2.1.2. Working with the SAXParser class
items: 14.3.1. Rich Site Summary
ItemSearcher class: 6.3.2.1. NodeIterator

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

Jakarta Tomcat (see Tomcat)
James ClarkÕs XP parser, online resources: 11.2.3. Writing the Server
Java
Castor: 15.2. Castor
compatible operating systems: 1.3.1. An Operating System and Java
datatypes supported in XML-RPC: 11.2.2. Writing the Handler
DOM and: 5.1.1. Language Bindings
language bindings: 5.1.1. Language Bindings
ouput forms: 5.2.3. DOMSerializer
platforms: 0.3. Software and Versions
properties files: 7.2.1. Java Properties Files
XML, conversion to: 7.2.2. Converting to XML
SerializerTest.java, source files: 5.2.1. Getting a DOM Parser
SOAP implementation projects: 12.2. Setting Up
XML, and, future developments: 16. Looking Forward
Java & XML, changes since first edition: 2. Nuts and Bolts
Java API for XML Parsing (see JAXP)
Java Document Object Model, see JDOM: 0.6. Acknowledgments
Javadocs, string-interning: 4.1.2.3. Interning and entities
java.net.MalformedURLException class: 11.2.4. Writing the Client
java.text.SimpleDateFormat class: 11.3.1. A Shared Handler
java.util.Enumeration class: 11.3.1. A Shared Handler
JavaXML.explorer-html.xsl: 10.3.3. Browser-Dependent Styling
JavaXML.fo.xsl: 10.4.1. Creating an XSP Page
JavaXML.html.xsl: 10.3.3. Browser-Dependent Styling
JavaXML.wml.xsl: 10.3.4. WAP and XML
javax.xml.parsers package: A.3.1. Package: javax.xml.parsers
javax.xml.transform package: A.3.2. Package: javax.xml.transform
javax.xml.transform.dom package: A.3.3. Package: javax.xml.transform.dom
javax.xml.transform.sax package: A.3.4. Package: javax.xml.transform.sax
JAXB (Java Architecture for Data Binding): 15.4. JAXB
JAXP (Java API for XML Parsing): 9. JAXP
A.3. JAXP 1.1
classes: 9.1. API or Abstraction
compared to other APIs: 9.1. API or Abstraction
download site: 9.1. API or Abstraction
parsing capability, lack of: 9.1. API or Abstraction
TrAX API: 9.3.2. The TrAX API
version 1.1: 9.3. JAXP 1.1
changes from v. 1.0: 9.3. JAXP 1.1
DefaulstHandler class: 9.3.1.1. The road to SAX 2.0
download site: 9.3. JAXP 1.1
versions and supported parsers: 9.1.2. The Old and the New
JCP (Java Community Process): 7.4.1. JDOM as a JSR
JDK (Java Development Kit), versions: 1.3.1. An Operating System and Java
JDOM (Java Document Object Model): 8. Advanced JDOM
architecture: 8.1. Helpful JDOM Internals
Attribute class: 7.2.3. Creating XML with JDOM
builder classes: 8.2.2. Building with Custom Classes
classes: 7.1. The Basics
document viewer implementation: 8.3.4. Endgame
DOM, contrasted with: 7. JDOM
7.5.1. JDOM isn't DOM
Element class, subclassing: 8.2. JDOM and Factories
element methods: 8.2.1. Creating a Factory
EntityRef class: 8.1.2. The EntityRef Class
factories: 8.2. JDOM and Factories
creating: 8.2.1. Creating a Factory
input/output model: 7.1.3. Input and Output
interfaces, absence of: 8.3. Wrappers and Decorators
iteration using Text class: 8.1.1. The Text Class
Java Collections support: 7.1.1. Java Collections Support
JDOM Beta 7: 8.1. Helpful JDOM Internals
JDOMNode interface: 8.3.1. JDOMNode
Namespace class: 8.1.3. The Namespace Class
null values: 7.5.2. Null Return Values
PropsToXML class: 7.2. PropsToXML
SimpleXPathViewer class: 8.3.4. Endgame
standardization: 7.4. Is JDOM a Standard?
subclassing: 8.4.1. More on Subclassing
Text class: 8.1.1. The Text Class
Version 1.0: A.4. JDOM 1.0 (Beta 7)
XML output, formatting methods: 7.2.4. Outputting XML with JDOM
XML, outputting: 7.2.4. Outputting XML with JDOM
XML, storing: 7.3.1. Storing XML
XML to RSS, for conversion of: 14.3.2. Creating an XML RSS Document
XML trees, and: 7.1. The Basics
XMLProperties class: 7.3. XMLProperties
XPathDisplayNode class: 8.3.3. Providing Support for XPath
JDOMException class: A.4.1.8. JDOMException
JDOMFactory interface: 8.2.1. Creating a Factory
A.4.3.3. JDOMFactory
JDOMNode interface: 8.3.1. JDOMNode
JRMP: 11.1.1. What Is RMI?
JSP (Java Server Pages): 10.4. XSP
limitations: 10.4. XSP
JSR (Java Specification Request): 7.4.1. JDOM as a JSR
JTreeContentHandler class: 3.3. Content Handlers
jUDDI, online resources: 13.2. UDDI

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: K

There are no index entries for this letter.

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

language bindings: 5.1.1. Language Bindings
Lark and Larval: 1.3.2. A Parser
LDAP (Lightweight Directory Access Protocol): 11.1.1. What Is RMI?
11.1.1. What Is RMI?
LexicalHandler class: 4.4.1. LexicalHandler
LexicalHandler interface: A.1.2.2. LexicalHandler
Lightweight Directory Access Protocol (LDAP): 11.1.1. What Is RMI?
Linux/Unix, Cocoon binaries: 10.2.1. Source Code or Binaries
list( ): 12.3.2.1. Code artifacts
error handling: 12.4.2. Better Error Handling
literal string property: 4.1.2.4. DOM nodes and literal strings
load( ): 7.3. XMLProperties
7.3.2. Loading XML
loadFromElements( ): 7.3.2. Loading XML
7.3.2. Loading XML
Locator class: 3.3.1. The Document Locator
A.1.1.10. Locator
Locator instance: 3.5.3. The Misplaced Locator
LocatorImpl class: A.1.3.4. LocatorImpl
looping: 2.3.3.2. Looping
low-level APIs: 1.3.3. APIs

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

mailing lists, Xerces parser: 3.1.1. Obtaining a Parser
mappings element: 12.4.1. Custom Parameter Types
mapTypes( ): 12.4.1. Custom Parameter Types
marshal( ): 13.4.1. A Messaging Service
15.4.3. Marshalling and Unmarshalling
marshalling: 15.1.3. Marshalling
memory and DOM: 5.1.3.3. Why use SAX at all?
5.4.1. Memory, Performance, and Deferred DOMs
message element: 13.3. WSDL
messaging services: 13.4.1. A Messaging Service
method names, and: 13.4.1. A Messaging Service
XML, server response to client, coding: 13.4.1. A Messaging Service
methods
addContent( ): 7.2.3. Creating XML with JDOM
7.3.1. Storing XML
addEvent( ): 11.3.1. A Shared Handler
11.3.1. A Shared Handler
addHandler( ): 11.2.3. Writing the Server
callback methods: 3.2.2. Parsing the Document
characters( ): 3.3.6. Element Data
createDocument( ): 6.1.1. Creating a New DOM Tree
createXMLRepresentation( ): 7.2.3. Creating XML with JDOM
7.2.3. Creating XML with JDOM
element methods: 8.2.1. Creating a Factory
endDocument( ): 3.3.2. The Beginning and the End of a Document
enumerateItem( ): 15.2.2. Source Generation
equals( ): 8.1.3. The Namespace Class
fault.getString( ): 12.4.2. Better Error Handling
generateRSSContentMethod( ): 14.3.2. Creating an XML RSS Document
getAttribute( ): 9.3.2.1. Working with the factory
getAttributeValue( ): 7.3.4. Backtracking
getBody( ): 13.4.1. A Messaging Service
getChild( ): 7.2.3. Creating XML with JDOM
getChildNodes( ): 5.2.3.3. Working with nodes
getChildren( ): 7.3.2. Loading XML
getColumnNumber( ): 3.3.1. The Document Locator
getContent( ): 7.3.4. Backtracking
7.3.4. Backtracking
getDetailEntries( ): 12.4.2. Better Error Handling
getDocumentElement( ): 5.2.3.3. Working with nodes
getException( ): 3.3.2. The Beginning and the End of a Document
getFeature( ): 9.3.1.2. Changes in SAX classes
getInstance( ): 11.4.2. Shared Instances
getItem( ): 15.2.2. Source Generation
getLineNumber( ): 3.3.1. The Document Locator
getListOfEvents( ): 11.3.1. A Shared Handler
getLocalName(int index): 3.3.5. Element Callbacks
getNodeName( ): 5.2.3.3. Working with nodes
getProperty( ): 7.2.3. Creating XML with JDOM
9.3.1.2. Changes in SAX classes
getRootElement( ): 7.3.2. Loading XML
getText( ): 7.3.2. Loading XML
getTextTrim( ): 7.3.2. Loading XML
getURI(int index): 3.3.5. Element Callbacks
getXMLReader( ): 9.3.1.1. The road to SAX 2.0
getXPath( ): 8.3.3. Providing Support for XPath
isNamespaceAware( ): 9.2.1.2. Working with the SAXParser class
isValidating( ): 9.2.1.2. Working with the SAXParser class
list( ): 12.3.2.1. Code artifacts
12.4.2. Better Error Handling
load( ): 7.3. XMLProperties
7.3.2. Loading XML
loadFromElements( ): 7.3.2. Loading XML
7.3.2. Loading XML
mapTypes( ): 12.4.1. Custom Parameter Types
marshal( ): 13.4.1. A Messaging Service
15.4.3. Marshalling and Unmarshalling
naming and messaging services: 13.4.1. A Messaging Service
newSAXParser( ): 9.2.1.1. A look at the SAXParserFactory class
newTransformer( ): 9.3.2.1. Working with the factory
parse( ): 3.2.2. Parsing the Document
propertyNames( ): 7.2.3. Creating XML with JDOM
remote calls: 11.2.3. Writing the Server
removeEvent( ): 11.3.1. A Shared Handler
11.3.1. A Shared Handler
RMI, invocation by: 11.1.1. What Is RMI?
save( ): 7.3. XMLProperties
setAttribute( ): 7.2.3. Creating XML with JDOM
9.3.2.1. Working with the factory
setDriver( ): 11.2.3. Writing the Server
11.2.4. Writing the Client
setErrorListener( ): 9.3.2.1. Working with the factory
setExpandEntities( ): 8.1.2. The EntityRef Class
setFeature( ): 9.3.1.2. Changes in SAX classes
setProperty( ): 9.3.1.2. Changes in SAX classes
setText( ): 7.2.3. Creating XML with JDOM
startDocument( ): 3.3.2. The Beginning and the End of a Document
store( ): 7.3. XMLProperties
7.3.1. Storing XML
7.3.4. Backtracking
transform( ): 9.3.2.2. Transforming XML
unmarshall( ): 15.4.3. Marshalling and Unmarshalling
XML output, for formatting: 7.2.4. Outputting XML with JDOM
XML-RPC, invocation in: 11.2.2. Writing the Handler
XMLReader interface, for setting: 4.1.1. Setting Properties and Features
Microsoft
Internet Explorer, XSL stylesheet: 10.3.3. Browser-Dependent Styling
MXSML Parser: 1.3.2. A Parser
SOAP implementation: 12.2.1.2. Isn't Microsoft a player?
Windows, Cocoon binaries: 10.2.1. Source Code or Binaries

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

NamedNodeMap: 5.2.3.3. Working with nodes
interface: A.2.1.13. NamedNodeMap
names of elements: 3.3.5. Element Callbacks
namespaces: 2.1.2. Namespaces
4.1.2.2. Namespaces
6.2. Namespaces
declarations: 6.2. Namespaces
WML: 10.3.4. WAP and XML
identifying XML with: 2.1.2. Namespaces
SAX callbacks: 3.3.4. Namespace Callbacks
schema specification, similarity to: 2.1.2. Namespaces
namespace-aware methods: 6.2. Namespaces
Namespace class: A.4.1.9. Namespace
JDOM: 8.1.3. The Namespace Class
namespace URI: 3.3.5. Element Callbacks
namespaceMappings object: 3.3.5. Element Callbacks
NamespaceSupport class: A.1.3.5. NamespaceSupport
naming conventions, elements: 2.1.1.2. Elements
nesting of tags: 2.1.1.2. Elements
newSAXParser( ): 9.2.1.1. A look at the SAXParserFactory class
newTransformer( ): 9.3.2.1. Working with the factory
node comparisons, DOM Level 3: 6.4.2. Node Comparisons
Node interface: 5.2.3.3. Working with nodes
A.2.1.14. Node
NodeFilter interface: 6.3.2.1. NodeIterator
NodeIterator interface: 6.3.2.1. NodeIterator
online resources: 6.3.2.2. TreeWalker
NodeList interface: A.2.1.15. NodeList
nodes, determining type of: 5.2.3.3. Working with nodes
non-fatal errors: 3.4.2. Nonfatal Errors
Notation interface: A.2.1.16. Notation

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

online resources: 0.3. Software and Versions
0.5. Comments and Questions
Castor: 15.2. Castor
Cocoon source and binaries: 10.2.1. Source Code or Binaries
CVS: 10.2.1. Source Code or Binaries
DOM Level 2 modules: 6.3.6. Odds and Ends
DOM Level 2 parsers: 6.3. DOM Level 2 Modules
DOM specifications, Levels 1 and 2: 5.1. The Document Object Model
DTD constraint models: 2.2.2.2. Multiple namespaces
installation, SOAP servlet engines: 12.2.2.3. The router servlet and admin client
JAXP: 9.1. API or Abstraction
9.3. JAXP 1.1
Apache Xerces implementation: 9.2.1. Starting with SAX
JSRs (Java Specification Requests): 7.4.1. JDOM as a JSR
jUDDI: 13.2. UDDI
Microsoft SOAP toolkit: 12.2.1.2. Isn't Microsoft a player?
NodeIterator interface: 6.3.2.2. TreeWalker
parsers: 1.3.2. A Parser
Range module: 6.3.3. Range
RSS: 14.3.2. Creating an XML RSS Document
SOAP: 12.1. Starting Out
installation errors: 12.2.2.3. The router servlet and admin client
Treewalker interface: 6.3.2.2. TreeWalker
UP.SDK: 10.3.4. WAP and XML
WAP: 10.3.4. WAP and XML
specifications: 10.3.4. WAP and XML
web publishing frameworks: 10.1. Selecting a Framework
web services: 13.4.6. Where Do I Go From Here?
WML: 10.3.4. WAP and XML
WSDL: 13.3. WSDL
13.4.4. WSDL
XML: 2.1. The Basics
related technologies: 2.4. And More...
XPointer: 16.2. XPointer
XSL: 10.3.2. Viewing PDFs from XML
opening tags: 2.1.1.2. Elements
operating systems (OS), Java compatibility: 1.3.1. An Operating System and Java
Oracle XML Parser: 1.3.2. A Parser
OrderProcessor class: 13.4.5. Writing a Client
org.apache.soap.Body: 13.4.1. A Messaging Service
org.apache.soap.Envelope: 13.4.1. A Messaging Service
org.jdom package: A.4.1. Package: org.jdom
org.jdom.adapters package: A.4.2. Package: org.jdom.adapters
org.jdom.input package: A.4.3. Package: org.jdom.input
org.jdom.output package: A.4.4. Package: org.jdom.output
org.w3c.dom package: A.2.1. Package: org.w3c.dom
org.w3c.dom.Document objects: 5.2.2. DOM Parser Output
org.w3c.dom.Node inferface: 5.4.2. Polymorphism and the Node Interface
org.xml.sax: A.1.1. Package: org.xml.sax
org.xml.sax package: 4.1.2.2. Namespaces
org.xml.sax.Attributes instance: 3.3.5. Element Callbacks
org.xml.sax.HandlerBase class: 9.2.1. Starting with SAX
org.xml.sax.helpers package: A.1.3. Package: org.xml.sax.helpers
org.xml.sax.helpers.ParserAdapter class: 3.5.1. My Parser Doesn't Support SAX 2.0
org.xml.sax.Parser interface: 3.2. SAX Readers
org.xml.sax.XMLReader interface
parse( ): 3.2.2. Parsing the Document
OS (operating systems), Java compatibility: 1.3.1. An Operating System and Java
OutputKeys class: A.3.2.2. OutputKeys

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

-package flag: 15.2.2. Source Generation
packages
Castor: 15.2. Castor
com.sun.xml.parser: 9.1.1. Sun's JAXP and Parser
for data binding: 15. Data Binding
helma.xmlrpc: 11.2.1. XML-RPC Libraries
javax.xml.parsers: 9.1. API or Abstraction
A.3.1. Package: javax.xml.parsers
javax.xml.transform: 9.3.2. The TrAX API
A.3.2. Package: javax.xml.transform
javax.xml.transform.dom: A.3.3. Package: javax.xml.transform.dom
javax.xml.transform.sax: A.3.4. Package: javax.xml.transform.sax
javax.xml.transform.stream: A.3.5. Package: javax.xml.transform.stream
org.apache.xalan.xslt: 14.2.2. XSLT from a Servlet
org.enhydra.zeus.binder: 15.3.2. Class Generation
org.enhydra.zeus.result: 15.3.3. Marshalling and Unmarshalling
org.enhydra.zeus.source: 15.3.3. Marshalling and Unmarshalling
org.jdom: A.4.1. Package: org.jdom
org.jdom.adapters: A.4.2. Package: org.jdom.adapters
org.jdom.input: 7.1.3. Input and Output
A.4.3. Package: org.jdom.input
org.jdom.output: 7.1.3. Input and Output
A.4.4. Package: org.jdom.output
org.w3c.dom: 5.1.1. Language Bindings
A.2.1. Package: org.w3c.dom
org.w3c.dom.css: 6.3.4.3. Style
org.w3c.dom.events: 6.3.4.1. Events
org.w3c.dom.html: 6.3.5. HTML
org.w3c.dom.ranges: 6.3.3. Range
org.w3c.dom.stylesheets: 6.3.4.3. Style
org.w3c.dom.traversal: 6.3.2. Traversal
org.w3c.dom.views: 6.3.4.2. Views
org.xml.sax: 3.1.2. Getting the SAX Classes and Interfaces
4.1.2.2. Namespaces
A.1.1. Package: org.xml.sax
org.xml.sax.ext: 4.4. Even More Handlers
A.1.2. Package: org.xml.sax.ext
org.xml.sax.helpers: A.1.3. Package: org.xml.sax.helpers
SAX: A.1. SAX 2.0
parse( ): 3.2.2. Parsing the Document
5.2.2. DOM Parser Output
5.2.2. DOM Parser Output
invocation: 3.2.2. Parsing the Document
SAXParser interface: 9.3.1.1. The road to SAX 2.0
Parser interface: 3.2. SAX Readers
A.1.1.11. Parser
ParserAdapter class: 3.5.1. My Parser Doesn't Support SAX 2.0
A.1.3.6. ParserAdapter
ParserConfigurationException class: A.3.1.4. ParserConfigurationException
ParserFactory class: 9.2.1. Starting with SAX
A.1.3.7. ParserFactory
parsers: 1.3.2. A Parser
callback methods: 3.2.2. Parsing the Document
DOM: 5.2.1. Getting a DOM Parser
DOM, and: 5.1.1. Language Bindings
DOM Level 2, sources: 6.3. DOM Level 2 Modules
features, proprietary: 4.1.3. Proprietary Properties and Features
I/O streams as input: 3.2.3. Using an InputSource
not reentrant: 3.5.2. The SAX XMLReader: Reused and Reentrant
obtaining: 3.1.1. Obtaining a Parser
online sources: 1.3.2. A Parser
properties, proprietary: 4.1.3. Proprietary Properties and Features
SAX 2.0 not supported: 3.5.1. My Parser Doesn't Support SAX 2.0
standardization, lack of: 4.1. Properties and Features
Sun version: 9.1.1. Sun's JAXP and Parser
vendor independence, and: 9.1.1. Sun's JAXP and Parser
Xerces: 3.1.1. Obtaining a Parser
XML parser classes: 3.1.1. Obtaining a Parser
parsing: 3. SAX
exception handling: 3.2.2. Parsing the Document
flow of events: 4.3.1. XMLFilters
parent-child consistency in the pipeline: 4.5.3. Parsing on the Reader Instead of the Filter
XML documents: 3.3.5. Element Callbacks
#PCDATA keyword: 2.2.1.1. Elements
Perl CGI scripts, generating XML output with: 14.1.2. Entering the Books
pipelines: 4.3.1. XMLFilters
PIs (processing instructions)
DOM and: 5.2.3.3. Working with nodes
SAX and: 3.3.3. Processing Instructions
XML documents: 2.1.1. XML 1.0
platform-specific technologies, drawbacks: 10.1.1. Stability
portType element: 13.3. WSDL
po.xsdXMLSchema: 13.4.1. A Messaging Service
prefix mapping: 3.3.4. Namespace Callbacks
processing instructions (see PIs)
ProcessingInstruction class: A.4.1.10. ProcessingInstruction
ProcessingInstruction interface: A.2.1.17. ProcessingInstruction
ProcessingInstruction interface (DOM): 5.2.3.3. Working with nodes
properties: 4.1.1. Setting Properties and Features
DOM nodes: 4.1.2.4. DOM nodes and literal strings
elements, distinction between textual and attribute values: 7.3. XMLProperties
literal strings: 4.1.2.4. DOM nodes and literal strings
properties and features, methods for setting: 4.1.1. Setting Properties and Features
property and feature URIs: 4.1.1. Setting Properties and Features
property files, Java: 7.2.1. Java Properties Files
XML, conversion to: 7.2.2. Converting to XML
property names, splitting on period (.) delimiters: 7.2.3. Creating XML with JDOM
property values, conversion to attributes: 7.2.3. Creating XML with JDOM
propertyNames( ): 7.2.3. Creating XML with JDOM
proprietary properties and features: 4.1.3. Proprietary Properties and Features
PropsToXML class: 7.2. PropsToXML
skeleton for the class: 7.2.2. Converting to XML
public ID: 5.2.3.3. Working with nodes
public identifiers: 2.1.1. XML 1.0
PUBLIC keyword: 2.1.1. XML 1.0
pushing data to clients: 14.3. Push Versus Pull

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

Q names: 3.3.5. Element Callbacks
quotation marks: 2.1.1.3. Attributes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

Range module: 6.3.3. Range
online resources: 6.3.3. Range
recurrence modifiers: 2.2.1.1. Elements
registration of services: 13.2. UDDI
13.4.2. Registering with UDDI
remote method invocation (see RMI)
remote procedure calls (see RPC)
removeChild( ): 6.1.2. Modifying a DOM Tree
removeEvent( ): 11.3.1. A Shared Handler
11.3.1. A Shared Handler
reserved characters, escaping: 2.1.1.4. Entity references and constants
resolveEntity( ), avoiding errors with: 4.5.1. Return Values from an EntityResolver
Result interface: A.3.2.3. Result
Rich Site Summary (see RSS)
RMI (remote method invocation): 11.1.1. What Is RMI?
method calls: 11.2.3. Writing the Server
registry: 11.1.1. What Is RMI?
RPC, compared to: 11.1. RPC Versus RMI
root element: 14.3.2. Creating an XML RSS Document
root elements: 2.1.1.1. The root element
RPC (remote procedure calls): 11.1.2. What Is RPC?
handlers, registering: 11.2.3. Writing the Server
limitations: 11.1.2. What Is RPC?
method calls: 11.2.3. Writing the Server
RMI, compared to: 11.1. RPC Versus RMI
SOAP, using: 12.3.2. An RPC Service
RSS (Rich Site Summary): 14.3.1. Rich Site Summary
channels: 14.3.1. Rich Site Summary
online resources: 14.3.2. Creating an XML RSS Document
RSS file creation: 14.3.2. Creating an XML RSS Document
RSS (Rich Style Sheets): 14.3.2. Creating an XML RSS Document

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

save( ): 7.3. XMLProperties
SAX (Simple API for XML): 1.2.1. Low-Level APIs
3. SAX
classes: 3.1.2. Getting the SAX Classes and Interfaces
documentation: 3.1.2. Getting the SAX Classes and Interfaces
exceptions, thrown by DOM parsers: 5.4.3. DOM Parsers Throwing SAX Exceptions
filters: 4.3.1. XMLFilters
HandlerBase class: 9.2.1. Starting with SAX
handlers: 3.3. Content Handlers
hierarchical character: 5.1.3.2. SAX siblings
InputSource vs. URI: 3.2.3. Using an InputSource
Javadocs: 3.1.2. Getting the SAX Classes and Interfaces
JAXP, versions supported under: 9.1.2. The Old and the New
limitations with XML: 6.1. Changes
org.xml.sax.XMLReader interface: 3.2. SAX Readers
instantiating: 3.2.1. Instantiating a Reader
ParserFactory class: 9.2.1. Starting with SAX
reasons for using: 5.1.3.3. Why use SAX at all?
Release 2.0, parsers, not supported by: 3.5.1. My Parser Doesn't Support SAX 2.0
sequential model: 5.1.3.1. SAX is sequential
standardization: 7.4.2. SAX and DOM as Standards
uses: 5.1.3.3. Why use SAX at all?
Version 2.0: A.1. SAX 2.0
core properties: B.2. Core Properties
XML standards, support of: 4.1. Properties and Features
XMLReader implementations, core features: B.1. Core Features
web publishing frameworks, support by: 10.1.2. Integration with Other XML Tools and APIs
XML-RPC server, driver for: 11.2.3. Writing the Server
XSLT, limitations in use with: 5.1.3.2. SAX siblings
SAXBuilder class: 7.1.3. Input and Output
A.4.3.4. SAXBuilder
SAXException class: 3.3.2. The Beginning and the End of a Document
A.1.1.12. SAXException
SAXExceptions: 3.3.2. The Beginning and the End of a Document
SAXNot SupportedExceptions: 4.1.2.2. Namespaces
SAXNotRecognizedException class: A.1.1.13. SAXNotRecognizedException
SAXNotRecognizedExceptions: 4.1.2.2. Namespaces
SAXNotSupportedException class: A.1.1.14. SAXNotSupportedException
SAXOutputter: 7.1.3. Input and Output
7.2.4. Outputting XML with JDOM
8.1.3. The Namespace Class
A.4.4. Package: org.jdom.output
SAXParseException class: A.1.1.15. SAXParseException
SAXParseExceptions: 3.4. Error Handlers
SAXParser class: 9.2.1.2. Working with the SAXParser class
A.3.1.5. SAXParser
SAXParser interface
parse( ): 9.3.1.1. The road to SAX 2.0
SAXParserFactory class: 9.2.1.1. A look at the SAXParserFactory class
A.3.1.6. SAXParserFactory
JAXP v. 1.1: 9.3.1. Updating the Standards
SAXTreeViewer class: 3.2. SAX Readers
3.2.1. Instantiating a Reader
Scheduler class: 11.3.1. A Shared Handler
import statement: 11.3.1. A Shared Handler
returning events: 11.3.1. A Shared Handler
sorting algorithm: 11.3.1. A Shared Handler
SchedulerClient class: 11.3.3. A Useful Client
schemaLocation attribute: 2.1.2. Namespaces
search( ): 6.3.2.1. NodeIterator
serialization: 5.2.3. DOMSerializer
DOM node types: 5.2.3.2. Launching serialization
recursion: 5.2.3.2. Launching serialization
serialize( ): 5.2.3.1. Getting started
5.2.3.2. Launching serialization
invocation: 5.2.4. The Results
serializeNode( ): 5.2.3.2. Launching serialization
SerializerTest class: 5.2.1. Getting a DOM Parser
server.xml file: 10.2.2. Configuring the Servlet Engine
service element: 13.3. WSDL
service registry: 13.1. Web Services
servlets
compared to XML-RPC: 11.4.3. To Servlet or Not To Servlet
development: 10. Web Publishing Frameworks
engine configuration for Cocoon: 10.2.2. Configuring the Servlet Engine
XSLT from: 14.2.2. XSLT from a Servlet
setAttribute( ): 6.1.1. Creating a New DOM Tree
7.2.3. Creating XML with JDOM
9.3.2.1. Working with the factory
setContentHandler( ): 3.3. Content Handlers
3.3.9. The Results
setDocumentLocator( ), Locator instance: 3.5.3. The Misplaced Locator
setDriver( ): 11.2.3. Writing the Server
11.2.4. Writing the Client
ClassNotFoundException: 11.2.3. Writing the Server
setDTDHandler( ): 3.3. Content Handlers
setEndAfter( ): 6.3.3. Range
setEntityResolver( ): 3.3. Content Handlers
setErrorHandler( ): 3.3. Content Handlers
3.4. Error Handlers
setErrorListener( ): 9.3.2.1. Working with the factory
setExpandEntities( ): 8.1.2. The EntityRef Class
setFactory( ): 8.2.2. Building with Custom Classes
setFeature( ): 4.1.1. Setting Properties and Features
4.1.2.2. Namespaces
9.3.1.2. Changes in SAX classes
SetNameSpaceProcessing( ): 4.1.2.2. Namespaces
setParent( ): 4.3.1. XMLFilters
setProperty( ): 4.1.1. Setting Properties and Features
9.3.1.2. Changes in SAX classes
setRootPart( ): 13.4.1. A Messaging Service
setStartBefore( ): 6.3.3. Range
setText( ): 7.2.3. Creating XML with JDOM
element, invocation on: 8.1.1. The Text Class
Silverstream: 13.4.4. WSDL
Simple API for XML (see SAX)
Simple Object Access Protocol (see SOAP)
SimpleXPathViewer class: 8.3.4. Endgame
skipped entities and SAX: 3.5.1. My Parser Doesn't Support SAX 2.0
SOAP-RPC calls, necessary operations: 12.3.3. An RPC Client
SOAP (Simple Object Access Protocol): 1.2.3. XML-Based Applications
12. SOAP
Apache Axis: 12.2.1.3. What's Axis?
Apache implementation: 12.2. Setting Up
Apache, stackTrace element: 12.4.2. Better Error Handling
basic components: 12.1. Starting Out
custom parameters: 12.4.1. Custom Parameter Types
encodingStyle attribute: 12.4.1. Custom Parameter Types
differences in implementations: 12.2.1.3. What's Axis?
DOMFaultListener: 12.4.2. Better Error Handling
encoding: 12.1.2. Encoding
envelope: 12.1.1. The Envelope
error handling: 12.4.2. Better Error Handling
IBM SOAP4J: 12.2.1.1. What about IBM SOAP4J?
installation: 12.2.2. Installation
admin client: 12.2.2.3. The router servlet and admin client
clients: 12.2.2.1. The client
online resources: 12.2.2.3. The router servlet and admin client
12.2.2.3. The router servlet and admin client
router servlets: 12.2.2.3. The router servlet and admin client
invocation: 12.1.3. Invocation
Microsoft implementation: 12.2.1.2. Isn't Microsoft a player?
online resources: 12.1. Starting Out
RPC calls: 12.3.2. An RPC Service
RPC clients: 12.3.3. An RPC Client
RPC services: 12.3.2.1. Code artifacts
deploying: 12.3.2.3. Deploying the service
updating: 12.3.2.3. Deploying the service
system design: 12.3. Getting Dirty
SOAPMappingRegistry class: 12.4.1. Custom Parameter Types
SOAPWiz: 13.4.4. WSDL
Source interface: A.3.2.4. Source
sourcecode, online resources: 0.3. Software and Versions
SourceLocator interface: A.3.2.5. SourceLocator
startDocument( ): 3.3.2. The Beginning and the End of a Document
startElement( ): 3.3.5. Element Callbacks
3.3.5. Element Callbacks
callback report: 3.3.4. Namespace Callbacks
startPrefixMapping( ): 3.3.4. Namespace Callbacks
static content generation: 10. Web Publishing Frameworks
store( ): 7.3. XMLProperties
7.3.1. Storing XML
7.3.4. Backtracking
StreamResult class: 9.3.2.2. Transforming XML
StreamSource class: 9.3.2.2. Transforming XML
string-interning: 4.1.2.3. Interning and entities
Javadoc sources: 4.1.2.3. Interning and entities
Style module: 6.3.4.3. Style
stylesheets
JavaXML.fo.xsl: 10.4.1. Creating an XSP Page
PDF transformation: 10.3.2. Viewing PDFs from XML
WML: 10.3.4. WAP and XML
XSP: 10.4. XSP
subclassing, JDOM: 8.4.1. More on Subclassing
Sun Microsystems Project X: 1.3.2. A Parser
supplyBooks.pl: 14.1.3. Providing a Listing of Available Books
switch construct (_ _ Java): 5.2.3.3. Working with nodes
system ID: 5.2.3.3. Working with nodes
in XML documents: 3.2.3. Using an InputSource
SYSTEM keyword: 2.1.1. XML 1.0
system resources, DOM and: 5.4.1. Memory, Performance, and Deferred DOMs

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

tags: 2.1.1.2. Elements
consolidation of opening and closing tags in an element: 2.1.1.2. Elements
targets: 2.1.1. XML 1.0
template keyword: 2.3.3.1. Template matching
template matching: 2.3.3.1. Template matching
Templates: 9.3.2.1. Working with the factory
Templates interface: A.3.2.6. Templates
test( ): 5.2.2. DOM Parser Output
TestXMLProperties class: 7.3.4. Backtracking
Text class: A.4.1.11. Text
JDOM: 8.1.1. The Text Class
text construct: 2.3.3.2. Looping
Text interface: A.2.1.18. Text
textual data: 3.3.6. Element Data
The Mind Electric, Glue: 13.4.4. WSDL
Thread class: 11.3.1. A Shared Handler
Tomcat servlet engine
Cocoon, configuration for: 10.2.2. Configuring the Servlet Engine
context, configuration: 10.2.2. Configuring the Servlet Engine
libraries, loading of: 10.2.2. Configuring the Servlet Engine
jar files, alphabetization problem: 10.2.2. Configuring the Servlet Engine
SOAP, servlet installation: 12.2.2.3. The router servlet and admin client
transform( ): 9.3.2.2. Transforming XML
Transformer class: A.3.2.7. Transformer
Transformer instance, obtaining: 9.3.2.1. Working with the factory
TransformerFactory class: 9.3.2.1. Working with the factory
A.3.2.8. TransformerFactory
Traversal module: 6.3.2. Traversal
TrAX API: 9.3.2. The TrAX API
tree structure of XSL processing stages: 2.3.1.1. XSL and trees
tree-walking: 6.3.2. Traversal
TreeWalker interface: 6.3.2.2. TreeWalker
online resources: 6.3.2.2. TreeWalker
try/catch blocks, JDOM, for catching null values: 7.5.2. Null Return Values
types element: 13.3. WSDL

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

UDDI (Universal Discovery, Description, and Integration): 1.2.3. XML-Based Applications
13.2. UDDI
implementations: 13.2. UDDI
registry: 13.2. UDDI
searching: 13.4.3. Searching a UDDI Registry
uniform resource indicators (URIs): 3.2.1. Instantiating a Reader
uniform resource locators (see URLs)
Universal Discovery, Description, and Integration (see UDDI)
unmarshal( ): 15.4.3. Marshalling and Unmarshalling
unmarshalling: 15.1.2. Unmarshalling
unparsed data: 2.1.1.5. Unparsed data
UpdateItemServlet class: 6.1.1. Creating a New DOM Tree
6.3.3. Range
UP.SDK: 10.3.4. WAP and XML
URI mappings, storage in data structures: 3.3.4. Namespace Callbacks
URIResolver interface: 9.3.2.1. Working with the factory
A.3.2.9. URIResolver
URIs (uniform resource indicators): 3.2.1. Instantiating a Reader
property and feature IDs as: 4.1.1. Setting Properties and Features
URLs (uniform resource locators): 3.2.1. Instantiating a Reader
Apache Jetspeed project: 14.3.1. Rich Site Summary
Java and XML web site: 3.1.3. Have an XML Document on Hand
SAX API Javadocs: 3.1.2. Getting the SAX Classes and Interfaces
Xerces parser: 3.1.1. Obtaining a Parser
use cases, APIs and data binding: 15.1.4. Use Cases

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

valid XML documents: 2.1.1.2. Elements
validating XML parsers: 1.3.2. A Parser
validation: 4.1.2.1. Validation
value-of construct: 2.3.3.1. Template matching
Vector: 11.3.1. A Shared Handler
vendor-neutral parsing code and JAXP: 9.2. JAXP 1.0
vendor-specific code: 6.4.3. Bootstrapping
Views module: 6.3.4.2. Views

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

WAP (Wireless Application Protocol)
online resources: 10.3.4. WAP and XML
specifications, online resources: 10.3.4. WAP and XML
warning, DOMSerializer class: 5.2.4. The Results
warnings: 3.4.1. Warnings
W3C (World Wide Web Consortium), XML standardization: 1.1.1. Portability
web artists: 10. Web Publishing Frameworks
web publishing frameworks: 1.2.3. XML-Based Applications
10. Web Publishing Frameworks
Cocoon: 10.1.4. Making the Decision
DOM support, importance of: 10.1.2. Integration with Other XML Tools and APIs
installation: 10.2. Installation
online resources: 10.1. Selecting a Framework
platform-specific technologies, drawbacks of: 10.1.1. Stability
SAX support, importance of: 10.1.2. Integration with Other XML Tools and APIs
selection: 10.1. Selecting a Framework
parser support: 10.1.2. Integration with Other XML Tools and APIs
production usage: 10.1.3. Production Presence
stability considerations: 10.1.1. Stability
standardization, absence of: 10.1.4. Making the Decision
web development, and: 10. Web Publishing Frameworks
XML files, publication: 10.3.1. Viewing XML Converted to HTML
XSP: 10.4. XSP
web services: 13.1. Web Services
alternatives to: 14. Content Syndication
clients, writing: 13.4.5. Writing a Client
example: 13.4. Putting It All Together
messaging services: 13.4.1. A Messaging Service
online resources: 13.4.6. Where Do I Go From Here?
SOAP, in addition to: 13.3. WSDL
testing a client: 13.4.5. Writing a Client
UDDI, registering with: 13.4.2. Registering with UDDI
UDDI registry, searching in: 13.4.3. Searching a UDDI Registry
well-formed XML documents: 2.1.1.2. Elements
whitespace
reporting: 3.3.6. Element Data
RSS and: 14.3.2. Creating an XML RSS Document
Wireless Markup Language (see WML)
WML (Wireless Markup Language): 10.3.4. WAP and XML
online resources: 10.3.4. WAP and XML
stylesheet: 10.3.4. WAP and XML
wrappers: 8.3. Wrappers and Decorators
wrong document exception: 6.5.1. The Dreaded WRONG DOCUMENT Exception
WSDL (Web Services Description Language): 13.3. WSDL
elements: 13.3. WSDL
future developments: 13.4.4. WSDL
online resources: 13.3. WSDL
XML Schema, current usable version: 13.3. WSDL
WSDL (Web Services Descriptor Language): 1.2.3. XML-Based Applications
files, data included in: 13.3. WSDL

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

Xalan processor: 14.2.2. XSLT from a Servlet
Xerces parser: 3.1.1. Obtaining a Parser
XLink: 16.1. XLink
xlink\:actuate: 16.1. XLink
xlink\:href attribute: 16.1. XLink
xlink\:show attribute: 16.1. XLink
xlink\:type attribute: 16.1. XLink
XML 1.0 Recommendation: 2.1. The Basics
XML-based applications: 1.2.3. XML-Based Applications
XML declarations
DOM Level 3: 6.4.1. The XML Declaration
DOM Level 2, lack of exposure in: 5.2.3.3. Working with nodes
XML documents: 2.1.1. XML 1.0
content, manipulation via data binding: 15.1. First Principles
DTD: 2.2.1. DTDs
graphical tree, viewing as: 3.2.1. Instantiating a Reader
locator: 3.3.1. The Document Locator
parsing: 3. SAX
parse( ): 3.2.2. Parsing the Document
system ID: 3.2.3. Using an InputSource
validation: 1.3.2. A Parser
contrasted with processing of DTDs: 4.1.2.1. Validation
validity: 2.1.1.2. Elements
well-formed: 2.1.1.2. Elements
XML (Extensible Markup Language): 1.1. XML Matters
2. Nuts and Bolts
browser-dependent styling: 10.3.3. Browser-Dependent Styling
business-to-business real world model: 14. Content Syndication
constraints: 2.2. Constraints
data binding: 1.2.2. High-Level APIs
15. Data Binding
Zeus: 15.3. Zeus
data sharing, applications and companies: 14. Content Syndication
document fragments, working with: 14.1.2. Entering the Books
elements: 2.1.1.2. Elements
naming: 3.3.5. Element Callbacks
errors involving version of: 3.4.4. Breaking the Data
files, viewing as HTML: 10.3.1. Viewing XML Converted to HTML
HTML forms, data generation from: 14.1.2. Entering the Books
identifying with namespaces: 2.1.2. Namespaces
interoperability: 1.1.2. Interoperability
13.1. Web Services
Java, and, future developments: 16. Looking Forward
messaging: 13.4.1. A Messaging Service
namespace declarations, WML: 10.3.4. WAP and XML
namespaces: 2.1.2. Namespaces
OÕReilly publications about: 2. Nuts and Bolts
PDF files, conversion to: 10.3.2. Viewing PDFs from XML
Perl CGI scripts, outputting from: 14.1.2. Entering the Books
portability: 1.1.1. Portability
recent developments: 0. Preface
related technologies: 2.4. And More...
RSS, conversion to using JDOM: 14.3.2. Creating an XML RSS Document
standardization: 1.1.1. Portability
transformations: 9.3.2.2. Transforming XML
versions: 0.3. Software and Versions
web publishing framework: 10. Web Publishing Frameworks
XML Schema bindings: 16.3. XML Schema Bindings
XPointer references, document using: 16.2. XPointer
XML parser classes: 3.1.1. Obtaining a Parser
XML parsers: 1.3.2. A Parser
DOM, and: 5.1.1. Language Bindings
DOM features, verifying support: 6.3.1. Branching Out
online sources: 1.3.2. A Parser
XML-RPC: 1.2.3. XML-Based Applications
11. XML-RPC
advantages: 11.1.3. XML-RPC
client application: 11.2.4. Writing the Client
handler class: 11.2.2. Writing the Handler
handlers: 11.3.1. A Shared Handler
ÕHello, WorldÕ: 11.2. Saying Hello
HTTP listener, creating: 11.2.3. Writing the Server
invocation: 12.1.3. Invocation
Java datatypes, supported: 11.2.2. Writing the Handler
libraries: 11.2.1. XML-RPC Libraries
SAX 2.0, lack of support for: 11.2.3. Writing the Server
server classes: 11.3. Putting the Load on the Server
configuration: 11.3.2. A Configurable Server
port conflicts: 11.3.4. Talk to Me (Again)
sorting method: 11.3.1. A Shared Handler
server, loading: 11.2.3. Writing the Server
servlets, compared to: 11.4.3. To Servlet or Not To Servlet
XML, and: 11.4.1. Where's the XML in XML-RPC?
XML Schema: 2.2.2. XML Schema
Castor, classes, generating with: 15.2.2. Source Generation
purchase order (PO) document: 13.4.1. A Messaging Service
schema specification, similarity to namespace declarations: 2.1.2. Namespaces
schemaLocation attribute: 2.1.2. Namespaces
SOAP, integration in: 12.1.2. Encoding
xml-string property: 4.1.2.4. DOM nodes and literal strings
XML transformations: 2.3. Transformations
9.3.2. The TrAX API
XMLFilter class: 4.3.1. XMLFilters
A.1.1.16. XMLFilter
XMLFilterImpl class: 4.3.1. XMLFilters
A.1.3.8. XMLFilterImpl
XMLOutputter: 7.1.3. Input and Output
A.4.4. Package: org.jdom.output
namespace declarations: 8.1.3. The Namespace Class
XMLOutputter class: 7.2.4. Outputting XML with JDOM
7.2.4. Outputting XML with JDOM
XMLProperties class: 7.3. XMLProperties
incompatible versions: 7.3.3. Taking a Test Drive
skeleton: 7.3. XMLProperties
testing: 7.3.3. Taking a Test Drive
XMLReader interface: 3.2. SAX Readers
3.2.1. Instantiating a Reader
A.1.1.17. XMLReader
methods for setting: 4.1.1. Setting Properties and Features
parse(): 3.2.2. Parsing the Document
XMLReaderAdapter class: A.1.3.9. XMLReaderAdapter
XMLReaderFactory class: A.1.3.10. XMLReaderFactory
XMLReaders, pipelining events: 4.3.1. XMLFilters
XmlRpc class: 11.2.3. Writing the Server
XmlRpcClient class: 11.2.4. Writing the Client
XMLUtils class: 13.4.5. Writing a Client
XMLWriter class: 4.3.2. XMLWriter
XP: 1.3.2. A Parser
XPath: 2.3.3. XPath
XPathDisplayNode class: 8.3.3. Providing Support for XPath
XPointer: 16.2. XPointer
online resources: 16.2. XPointer
shorthand: 16.2. XPointer
XSL (Extensible Stylesheet Language): 2.3.1. XSL
formatting objects: 2.3.1.2. Formatting objects
Internet Explorer, stylesheet: 10.3.3. Browser-Dependent Styling
looping: 2.3.3.2. Looping
online resources, formatting objects definition: 10.3.2. Viewing PDFs from XML
stylesheets
for viewing XML as HTML: 10.3.1. Viewing XML Converted to HTML
in business-to-business communication: 14.2.1. Filtering XML Data
PDF transformation: 10.3.2. Viewing PDFs from XML
template matching: 2.3.3.1. Template matching
templates in business-to-business communication: 14.2.1. Filtering XML Data
tree structures: 2.3.1.1. XSL and trees
web development, and: 10. Web Publishing Frameworks
WML pages, developer tools: 10.3.4. WAP and XML
xsl\:apply-templates construct: 5.1.3.2. SAX siblings
xsl\:stylesheet element: 10.3.4. WAP and XML
XSLT (Extensible Stylesheet Language Transformations): 2.3.2. XSLT
5.1.3.2. SAX siblings
10. Web Publishing Frameworks
XSLTProcessor class: 14.2.2. XSLT from a Servlet
XSLTProcessorFactory class: 14.2.2. XSLT from a Servlet
XSP (Extensible Server Pages): 10.4. XSP
creating pages: 10.4.1. Creating an XSP Page
JSP, advantages compared to: 10.4. XSP
XML, and: 10.4. XSP
XSL stylesheets for: 10.4. XSP

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Z

Zeus: 15.3. Zeus
classes, generating: 15.3.2. Class Generation
installation: 15.3.1. Installation
marshalling and unmarshalling: 15.3.3. Marshalling and Unmarshalling

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Colophon (Java & XML, 2nd Edition)

 Java and XML, 2nd EditionSearch this book
Previous

Colophon

Our look is the result of reader comments, our own experimentation, and feedback from distribution channels. Distinctive covers complement our distinctive approach to technical topics, breathing personality and life into potentially dry subjects.

The animals on the cover of Java & XML, Second Edition are lions (Panthera leo). These great cats differ from other solitary felines in that they form family groups, called prides. Prides consist of as many as 30 to 40 lions, most of which are females and their offspring. The lifespan of a lion is approximately 3-4 years. Full grown males can grow up to 10 feet in length; the only cat larger is the tiger. The lion's eye is particularly sensitive to movement, and it can detect the movement of its prey from a great distance. Special receptor cells in the cat's eye give it exceptional night vision.

Lions live in eastern and southern Africa, although some subspecies of the African lion are endangered. The Asiatic lion (P.l. persica) once lived throughout India, the Middle East, and Southern Asia. Today, its population has been reduced to approximately 290 animals, which can be found in the Gir Forest National Park in Gujarat, western India. The Barbary and Cape lions are extinct.

Lions are carnivores and prey on large herd animals. They are at the top of the food chain, the apex predator of their environment. The females are the hunters, while the males' role is to protect the pride from other aggressive males. Lions are not as fast as other big cats, such as the cheetah. As a result, they concentrate on heavier, less agile animals, and hunt from ambush by driving prey toward concealed members of the hunting group. They use coordinated, cooperative techniques that enable the group to hunt with more success than an individual could. African lions eat wildebeest, zebra, antelope, gazelle, impala, and giraffe.

Colleen Gorman was the production editor and copyeditor for Java & XML, Second Edition. Emily Quill was the proofreader, and Catherine Morris and Jane Ellin provided quality control. John Bickelhaupt wrote the index.

Ellie Volckhausen designed the cover of this book, based on a series design by Edie Freedman. The cover image is a 19th-century engraving from Grosvenor Prints in London. Emma Colby produced the cover layout with QuarkXPress 4.1 using Adobe's ITC Garamond font.

Melanie Wang designed the interior layout, based on a series design by Nancy Priest. The heading font is Bitstream Bodoni, the text font is ITC New Baskerville, and the code font is Constant Willison. Neil Walls and Anne-Marie Vaduva converted the files from Microsoft Word to FrameMaker 5.5.6 using tools created by Mike Sierra. The illustrations that appear in the book were produced by Jessamyn Read and Rob Romano using Macromedia FreeHand 9 and Adobe Photoshop 6.

Previous | Home
Index | Book Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Java and XSLT

by Eric M. Burke
ISBN 0-596-00143-6
First Edition, published September 2001.
(See the catalog page for this book.)

Table of Contents

Copyright Page
Dedication
Preface
Chapter 1: Introduction
Chapter 2: XSLT Part 1 -- The Basics
Chapter 3: XSLT Part 2 -- Beyond the Basics
Chapter 4: Java-Based Web Technologies
Chapter 5: XSLT Processing with Java
Chapter 6: Servlet Basics and XSLT
Chapter 7: Discussion Forum
Chapter 8: Additional Techniques
Chapter 9: Development Environment, Testing, and Performance
Chapter 10: Wireless Applications
Appendix A: Discussion Forum Code
Appendix B: JAXP API Reference
Appendix C: XSLT Quick Reference
Index
Colophon
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Dedication (Java and XSLT)

 Java and XSLTSearch this book

Dedication

For Jennifer and Aidan

Previous | Home | Next
Copyright Page | Book Index | 0.1. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Copyright (Java and XSLT)

 Java and XSLTSearch this book

Copyright © 2001 O'Reilly & Associates, Inc. All rights reserved.

Printed in the United States of America.

Published by O'Reilly & Associates, Inc., 1005 Gravenstein Hwy North Sebastopol, CA 95472.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safari.oreilly.com). For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are registered trademarks of O'Reilly & Associates, Inc. The association between the image of an ermine and Java and XSLT is a trademark of O'Reilly & Associates, Inc. Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc., in the United States and other countries. O'Reilly & Associates, Inc., is independent of Sun Microsystems.

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps.

While every precaution has been taken in the preparation of this book, the publisher and author assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Previous | Home | Next
Table of Contents | Book Index | Dedication
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Preface (Java and XSLT)

 Java and XSLTSearch this book

Preface

Java and Extensible Stylesheet Language Transformations (XSLT) are very different technologies that complement one another, rather than compete. Java's strengths are portability, its vast collection of standard libraries, and widespread acceptance by most companies. One weakness of Java, however, is in its ability to process text. For instance, Java may not be the best technology for merely converting XML files into another format such as XHTML or Wireless Markup Language (WML). Using Java for such a task requires skilled programmers who understand APIs such as DOM, SAX, or JDOM. For web sites in particular, it is desirable to simplify the page generation process so nonprogrammers can participate.

XSLT is explicitly designed for XML transformations. With XSLT, XML data can be transformed into any other text format, including HTML, XHTML, WML, and even unexpected formats such as Java source code. In terms of complexity and sophistication, XSLT is harder than HTML but easier than Java. This means that page authors can probably learn how to use XSLT successfully but will require assistance from programmers as pages are developed.

XSLT processors are required to interpret and execute the instructions found in XSLT stylesheets. Many of these processors are written in Java, making Java an excellent choice for applications that must interoperate with XML and XSLT. For web sites that utilize XSLT, Java servlets and EJBs are still required to intercept client requests, fetch data from databases, and implement business logic. XSLT may be used to generate each of the XHTML web pages, but this cannot be done without a language like Java acting as the coordinator.

This book explains the most important concepts behind the XSLT markup language but is not a comprehensive reference on that subject. Instead, the focus is on interoperability with Java, with particular emphasis on servlets and web applications. Every concept is backed by working examples, all of which work on widely available, free tools.

0.1. Audience

Java programmers who want to learn how to use XSLT comprise the target audience for this book. Java programming experience is essential, and basic familiarity with XML terminology is helpful, but not required. Since so many of the examples revolve around web applications and servlets, Chapter 4, "Java-Based Web Technologies" and 6 are devoted to this topic, offering a fast-paced tutorial to servlet technology. Chapter 2, "XSLT Part 1 -- The Basics" and Chapter 3, "XSLT Part 2 -- Beyond the Basics" contain a detailed XSLT tutorial, so no prior knowledge of XSLT is required.

This book is particularly well-suited for readers who may have read a lot about these technologies but have not used everything together in a complete application. Chapter 7, "Discussion Forum", for example, presents the implementation of a web-based discussion forum from start to finish. Fully worked examples can be found in every chapter, ranging from an Ant build file documentation stylesheet in Chapter 3, "XSLT Part 2 -- Beyond the Basics" to internationalization techniques in Chapter 8, "Additional Techniques".

Previous | Home | Next
Dedication | Book Index | 0.2. Software and Versions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Software and Versions (Java and XSLT)

 Java and XSLTSearch this book

0.2. Software and Versions

Keeping up with the latest technologies is always a challenge, particularly when writing about XML-related tools. The set of tools listed in Table 0-1 is sufficient to run just about every example in this book.

Table 0-1. Software and versions

Tool | URL | Description
Crimson | Included with JAXP 1.1 | XML parser from Apache
JAXP 1.1 | http://java.sun.com/xml | Java API for XML Processing
JDK 1.2.x | http://java.sun.com | Any Java 2 Standard Edition SDK
JDOM beta 6 | http://www.jdom.org | Open source alternative to DOM
JUnit 3.7 | http://www.junit.org | Open source unit testing framework
Tomcat 4.0 | http://jakarta.apache.org | Open source servlet container
Xalan | Included with JAXP 1.1 | XSLT processor

There are certainly other tools, most notably the SAXON XSLT processor available from http://users.iclway.co.uk/mhkay/saxon. This can easily be substituted for Xalan because of the vendor-independence that JAXP offers.

All of the examples, as well as JAR files for the tools listed in Table 0-1, are available for download from http://www.javaxslt.com and from the O'Reilly web site at http://www.oreilly.com/catalog/javaxslt. The included README.txt file contains instructions for compiling and running the examples.

Previous | Home | Next
0. Preface | Book Index | 0.3. Organization
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Organization (Java and XSLT)

 Java and XSLTSearch this book

0.3. Organization

This book consists of 10 chapters and 3 appendixes, as follows:

Chapter 1, "Introduction "
Provides a broad overview of the technologies covered in this book and explains how XML, XSLT, Java, and other APIs are related. Also reviews basic XML concepts for readers who are familiar with Java but do not have a lot of XML experience.

Chapter 2, "XSLT Part 1 -- The Basics"
Introduces XSLT syntax through a series of small examples and descriptions. Describes how to produce HTML and XHTML output and explains how XSLT works as a language. XPath syntax is also introduced in this chapter.

Chapter 3, "XSLT Part 2 -- Beyond the Basics"
Continues with material presented in the previous chapter, covering more sophisticated XSLT language features such as conditional logic, parameters and variables, text and number formatting, and producing XML output. This chapter concludes with a more sophisticated example that produces summary reports for Ant build files.

Chapter 4, "Java-Based Web Technologies"
Offers comparisons between popular web development technologies, comparing each with the Java and XSLT approach. The model-view-controller architecture is discussed in detail, and the relationship between XSLT web applications and EJB is touched upon.

Chapter 5, "XSLT Processing with Java"
Shows how to use XSLT processors with Java applications and servlets. Older Xalan and SAXON APIs are mentioned, but the primary focus is on Sun's JAXP. Key examples show how to use XSLT and SAX to transform non-XML files and data sources, how to improve performance through caching techniques, and how to interoperate with DOM and JDOM.

Chapter 6, "Servlet Basics and XSLT"
Provides a detailed review of Java servlet programming techniques. Shows how to create web applications and WAR files, how to deploy XML and XSLT files within these web applications, and how to perform XSLT transformations from servlets.

Chapter 7, "Discussion Forum"
Implements a complete web application from start to finish. In this chapter, a web-based discussion forum is designed and implemented using Java, XML, and XSLT techniques. The relationship between CSS and XSLT is presented, and XHTML Strict is used for all web pages.

Chapter 8, "Additional Techniques"
Covers important Java and XSLT programming techniques that build upon concepts presented in earlier chapters, concluding with a detailed discussion of XSLT internationalization. Other topics include XSLT page layout templates, servlet session tracking without cookies, browser identification, and servlet filters.

Chapter 9, "Development Environment, Testing, and Performance"
Offers practical advice for making a wide range of XML parsers, XSLT processors, and various other Java tools work together. Shows how to resolve conflicts with incompatible XML JAR files, how to write simple unit tests with JUnit, and how to write custom JAXP error handlers. Also discusses performance techniques and the relationship between XSLT and EJB.

Chapter 10, "Wireless Applications"
Describes the world of wireless technologies, with emphasis on Wireless Markup Language (WML). Shows how to detect wireless devices from a servlet, how to write XSLT stylesheets for these devices, and how to test using a variety of cell phone simulators. An online movie theater application is developed to reinforce the concepts.

Appendix A, "Discussion Forum Code"
Contains all of the remaining code from the discussion forum example presented in Chapter 7, "Discussion Forum".

Appendix B, "JAXP API Reference"
Lists and briefly describes each of the classes in Version 1.1 of the JAXP API.

Appendix C, "XSLT Quick Reference"
Contains a quick reference for the XSLT language. Lists all XSLT elements along with required and optional attributes and allowable content within each element. Also cross references each element with the W3C XSLT specification.

Previous | Home | Next
0.2. Software and Versions | Book Index | 0.4. Conventions Used in This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Conventions Used in This Book (Java and XSLT)

 Java and XSLTSearch this book

0.4. Conventions Used in This Book

Italic is used for:

  • Pathnames, filenames, and program names

  • New terms where they are defined

  • Internet addresses, such as domain names and URLs

Constant width is used for:

  • Anything that appears literally in a Java program, including keywords, datatypes, constants, method names, variables, class names, and interface names

  • All Java code listings

  • HTML, XML, and XSLT documents, tags, and attributes

Constant width italic is used for:

  • General placeholders that indicate that an item is replaced by some actual value in your own program

Constant width bold is used for:

  • Command-line entries

  • Emphasis within a Java or XML source file

Previous | Home | Next
0.3. Organization | Book Index | 0.5. How to Contact Us
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How to Contact Us (Java and XSLT)

 Java and XSLTSearch this book

0.5. How to Contact Us

We have tested and verified the information in this book to the best of our ability, but you may find that features have changed (or even that we have made mistakes!). Please let us know about any errors you find, as well as your suggestions for future editions, by writing to:

O'Reilly & Associates, Inc.
1005 Gravenstein Hwy North
Sebastopol, CA 95472
(800) 998-9938 (in the U.S. or Canada)
(707) 829-0515 (international/local)
(707) 829-0104 (FAX)

There is a web page for this book, which lists errata, examples, or any additional information. You can access this page at:

http://www.oreilly.com/catalog/javaxslt

To comment or ask technical questions about this book, send email to:

bookquestions@oreilly.com

For more information about books, conferences, software, Resource Centers, and the O'Reilly Network, see the O'Reilly web site at:

http://www.oreilly.com

Previous | Home | Next
0.4. Conventions Used in This Book | Book Index | 0.6. Acknowledgments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Acknowledgments (Java and XSLT)

 Java and XSLTSearch this book

0.6. Acknowledgments

I would like to thank my wife Jennifer for tolerating my absence during the past six months, as I have locked myself in the basement researching, writing, and thinking. I also feel fortunate that my two-year-old son Aidan goes to bed early; a vast majority of this book was written well after 8:30 P.M.!

Coming up with a list of people to thank is a difficult job because so many have influenced the material in this book. I only hope that I do not leave anyone out. All of the technical reviewers did an amazing amount of work, each offering a unique perspective and useful advice. The official reviewers were Dean Wette, Kevin Heifner, Paul Jensen, Shane Curcuru, and Tim Brown.

I would also like to thank Weiqi Gao, Shu Zhu, Santosh Shanbhag, and Suman Ganesh for help with the internationalization example in Chapter 8, "Additional Techniques". A technical article by Dan Troesser inspired my servlet filter implementation, and Justin Michel and Brent Roberts reviewed some of the first chapters that I wrote.

There are two companies that I really want to thank. O'Reilly has this little link on their home page called "Write for Us." This book came into existence because I casually clicked on that link one day and decided to submit a proposal. Although my original idea was not accepted, Mike Loukides and I exchanged several emails after that in a virtual brainstorming session, and eventually the proposal for this book emerged. I am still amazed that an unknown visitor to a web site can become an O'Reilly author.

The other company I would like to thank is Object Computing, Inc. (OCI), my employer. They have a remarkable group of highly talented software engineers, all of whom are always available to answer questions, offer advice, and inspire me to learn more. These people are the reason I work for OCI and are the reason this book was possible.

Finally, I would like to thank Mark Volkmann of OCI for teaching me about XML in the first place and for answering countless questions during the past five years.

Previous | Home | Next
0.5. How to Contact Us | Book Index | 1. Introduction
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Introduction (Java and XSLT)

 Java and XSLTSearch this book

Chapter 1. Introduction

Contents:

Java, XSLT, and the Web
XML Review
Beyond Dynamic Web Pages
Getting Started
Web Browser Support for XSLT

When XML first appeared, people widely believed that it was the imminent successor to HTML. This viewpoint was influenced by a variety of factors, including media hype, wishful thinking, and simple confusion about the number of new technologies associated with XML. The reality is that millions of web sites are written in HTML, and no widely used browser fully supports XML and its related standards. Even when browser vendors incorporate full support for XML and its family of related technologies, it will take years before enough people use these new versions to justify rewriting most web sites in XML. Although maintaining compatibility with older browsers is essential, companies should not hesitate to move forward with XML and related technologies on the server.

From the browser perspective, HTML will remain dominant on the Web for many years to come. Looking beneath the hood will reveal a much different picture, however, in which HTML is used only during the last instant of presentation. Web applications must support a multitude of browsers, and the easiest way to do this is to simply transform data into HTML before sending it to the client. On the server side, XML is the preferred way to process and exchange data because it is portable, standard, and easy to work with. This is where Java and XSLT enter the picture.

1.1. Java, XSLT, and the Web

Extensible Stylesheet Language Transformations (XSLT) is designed to transform XML data into some other form, most commonly HTML, XHTML, or another XML format. An XSLT processor , such as Apache's Xalan, performs transformations using one or more XSLT stylesheets , which are also XML documents. As Figure 1-1 illustrates, XSLT can be utilized on the web tier while web browsers on the client tier deal only with HTML.

Figure 1-1. XSLT transformation

Typically in an XSLT- and Java-based web application, XML data is generated dynamically based on database queries. Although some newer databases can export data directly as XML, you will often write custom Java code to extract data using JDBC and convert it to XML. This XML data, such as a customized list of benefit elections or perhaps an airline schedule for a specific time window, may be different for each client using the application. In order to display this XML data on most browsers, it must first be converted to HTML. As Figure 1-1 shows, the XML data is fed into the processor as one input, and an XSLT stylesheet is provided as a second input. The output is then sent directly to the web browser as a stream of HTML. The XSLT stylesheet produces HTML formatting instructions, while the XML provides raw data.

1.1.1. What's Wrong with HTML?

One of the fundamental problems with HTML is its haphazard implementation. Although the specification for HTML is available from the World Wide Web Consortium (W3C), its evolution was driven mostly by competition between Netscape and Microsoft rather than a thoughtful design process and open standards. This resulted in a bloated language littered with browser-specific tags and varying support for standards. Since no two browsers support the exact same set of HTML features, web authors often limit themselves to a subset of HTML. Another approach is to create and maintain separate copies of each web page, which take advantage of the unique features found in a particular browser. The limitations of HTML are compounded for dynamic sites, in which Java programs are often responsible for accessing enterprise data sources and presenting that information through the browser.

Extracting information from back-end data sources is much more difficult than simple web page authoring. This requires skilled developers who know how to interact with Enterprise JavaBeans or relational databases. Since skilled Java developers are a scarce and expensive resource, it makes sense to let them work on the back-end data sources and business logic while web page developers and less experienced programmers work on the HTML user interface. As we will see in Chapter 4, "Java-Based Web Technologies", this can be difficult with traditional Java servlet approaches because Java code is often cluttered with HTML generation code.

1.1.2. Keeping Data and Presentation Separate

HTML does not separate data from presentation. For example, the following fragment of HTML displays some information about a customer. In it, data fields such as "Aidan" and "Burke" are clearly intertwined with formatting elements such as <tr> and <td>:

<h3>Customer Information</h3>
<table border="1" cellpadding="2" cellspacing="0">
  <tr><td>First Name:</td><td>Aidan</td></tr>
  <tr><td>Last Name:</td><td>Burke</td></tr>
  <!-- etc... -->
</table>

Traditionally, this sort of HTML is generated dynamically using println( ) statements in a servlet, or perhaps through a JavaServer Page (JSP). Both require Java programmers, and neither technology explicitly keeps business logic and data separated from the HTML generation code. To support multiple incompatible browsers, you have to be careful to avoid duplication of a lot of Java code and the HTML itself. This places additional burdens on Java developers who should be working on more important problems.

There are ways to keep programming logic separate from the HTML generation, but extracting meaningful data from HTML pages is next to impossible. This is because the HTML does not clearly indicate how its data is structured. A human can look at HTML and determine what its fields mean, but it is quite difficult to write a computer program that can reliably extract meaningful data. Although you can search for text patterns such as First Name: followed by <td>, this approach[1] fails as soon as the presentation is modified. For example, changing the page as follows would cause this approach to fail:

[1] This approach is commonly known as "screen scraping."

<tr><td>Full Name:</td><td>Aidan Burke</td></tr>

1.1.3. The XSLT Solution

XSLT makes it possible to define clearly the roles of Java, XML, XSLT, and HTML. Java is used for business logic, database queries and updates, and for creating XML data. The XML is responsible for raw data, while XSLT transforms the XML into HTML for viewing by a browser. A key advantage of this approach is the clean separation between the XML data and the HTML views. In order to support multiple browsers, multiple XSLT stylesheets are written, but the same XML data is reused on the server. In the previous example, the XML data for the customer did not contain any formatting instructions:

<customer>
  <firstName>Aidan</firstName>
  <lastName>Burke</lastName>
</customer>

Since XML contains only data, it is almost always much simpler than HTML. Additionally, XML can be created using a Java API such as JDOM (http://www.jdom.org). This facilitates error checking and validation, something that cannot be achieved if you are simply printing HTML as text using PrintWriter and println( ) statements in a servlet.

Best of all, the XML-generation code has to be written only once. The XML data can then be transformed by any number of XSLT stylesheets in order to support different browsers, alternate languages, or even nonbrowser devices such as web-enabled cell phones.

Previous | Home | Next
0.6. Acknowledgments | Book Index | 1.2. XML Review
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Review (Java and XSLT)

 Java and XSLTSearch this book

1.2. XML Review

In a nutshell, XML is a format for storing structured data. Although it looks a lot like HTML, XML is much more strict with quotes, properly terminated tags, and other such details. XML does not define tag names, so document authors must invent their own set of tags or look towards a standards organization that defines a suitable XML markup language. A markup language is essentially a set of custom tags with semantic meaning behind each tag; XSLT is one such markup language, since it is expressed using XML syntax.

The terms element and tag are often used interchangeably, and both are used in this book. Speaking from a more technical viewpoint, element refers to the concept being modeled, while tag refers to the actual markup that appears in the XML document. So <account> is a tag that represents an account element in a computer program.

1.2.1. SGML, XML, and Markup Languages

Standard Generalized Markup Language (SGML) forms the basis for HTML, XHTML, XML, and XSLT, but in very different ways for each. Figure 1-2 illustrates the relationships between these technologies.

Figure 1-2. SGML heritage

SGML is a very sophisticated metalanguage designed for large and complex documentation. As a metalanguage, it defines syntax rules for tags but does not define any specific tags. HTML, on the other hand, is a specific markup language implemented using SGML. A markup language defines its own set of tags, such as <h1> and <p>. Because HTML is a markup language instead of a metalanguage, you cannot add new tags and are at the mercy of the browser vendor to properly implement those tags.

XML, as shown in Figure 1-2, is a subset of SGML. XML documents are compatible with SGML documents, however XML is a much smaller language. A key goal of XML is simplicity, since it has to work well on the Web where bandwidth and limited client processing power is a concern. Because of its simplicity, XML is easier to parse and validate, making it a better performer than SGML. XML is also a metalanguage, which explains why XML does not define any tags of its own. XSLT is a particular markup language implemented using XML, and will be covered in detail in the next two chapters.

XHTML, like XSLT, is also an XML-based markup language. XHTML is designed to be a replacement for HTML and is almost completely compatible with existing web browsers. Unlike HTML, however, XHTML is based strictly on XML, and the rules for well-formed documents are very clearly defined. This means that it is much easier for vendors to develop editors and programming tools to deal with XHTML, because the syntax is much more predictable and can be validated just like any other XML document. Many of the examples in this book use XHTML instead of HTML, although XSLT can easily handle either format.

XHTML Basics

XHTML is a W3C Recommendation that represents the future of HTML. Based on HTML 4.0, XHTML is designed to be compatible with existing web browsers while complying fully with XML. This means that a properly written XHTML document is always a well-formed XML document. Furthermore, XHTML documents must adhere to one or more of the XHTML DTDs, therefore XHTML pages can be validated using today's XML parsers such as Apache's Crimson.

XHTML is designed to be modular; therefore, subsets can be extracted and utilized for wireless devices such as cell phones. XHTML Basic, also a W3C Recommendation, is one such modularization effort, and will likely become a force to be reckoned with in the wireless space.

Here is an example XHTML document:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Hello, World!</title>
  </head>
  <body>
    <p>Hello, World!</p>
  </body>
</html>

Some of the most important XHTML rules include:

  • XHTML documents must be well-formed XML and must adhere to one of the XHTML DTDs. As expected with XML, all elements must be properly terminated, attribute values must be quoted, and elements must be properly nested.

  • The <!DOCTYPE ...> tag is required.

  • Unlike HTML, tags must be lowercase.

  • The root element must be <html> and must designate the XHTML namespace as shown in the previous example.

  • <head> and <body> are required.

The preceding document adheres to the strict DTD, which eliminates deprecated HTML tags and many style-related tags. Two other DTDs, transitional and frameset, provide more compatibility with existing web browsers but should be avoided when possible. For full information, refer to the W3C's specifications and documentation at http://www.w3.org.

As we look at more advanced techniques for processing XML with XSLT, we will see that XML is not always dealt with in terms of a text file containing tags. From a certain perspective, XML files and their tags are really just a serialized representation of the underlying XML elements. This serialized form is good for storing XML data in files but may not be the most efficient format for exchanging data between systems or programmatically modifying the underlying data. For particularly large documents, a relational or object database offers far better scalability and performance than native XML text files.

1.2.2. XML Syntax

Example 1-1 shows a sample XML document that contains data about U.S. Presidents. This document is said to be well-formed because it adheres to several basic rules about proper XML formatting.

Example 1-1. presidents.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE presidents SYSTEM "presidents.dtd">
<presidents>
  <president>
    <term from="1789" to="1797"/>
    <name>
      <first>George</first>
      <last>Washington</last>
    </name>
    <party>Federalist</party>
    <vicePresident>
      <name>
        <first>John</first>
        <last>Adams</last>
      </name>
    </vicePresident>
  </president>
  <president>
    <term from="1797" to="1801"/>
    <name>
      <first>John</first>
      <last>Adams</last>
    </name>
    <party>Federalist</party>
    <vicePresident>
      <name>
        <first>Thomas</first>
        <last>Jefferson</last>
      </name>
    </vicePresident>
  </president>

  <!-- remaining presidents omitted -->

</presidents>

In HTML, a missing tag here and there or mismatched quotes are not disastrous. Browsers make every effort to go ahead and display these poorly formatted documents anyway. This makes the Web a much more enjoyable environment because users are not bombarded with constant syntax errors.

Since the primary role of XML is to represent structured data, being well-formed is very important. When two banking systems exchange data, if the message is corrupted in any way, the receiving system must reject the message altogether or risk making the wrong assumptions. This is important for XSLT programmers to understand because XSLT itself is expressed using XML. When writing stylesheets, you must always adhere to the basic rules for well-formed documents.

All well-formed XML documents must have exactly one root element . In Example 1-1, the root element is <presidents>. This forms the base of a tree data structure in which every other element has exactly one parent and zero or more children. Elements must also be properly terminated and nested:

<name>
  <first>George</first>
  <last>Washington</last>
</name>

Although whitespace (spaces, tabs, and linefeeds) between elements is typically irrelevant, it can make documents more readable if you take the time to indent consistently. Although XML parsers preserve whitespace, it does not affect the meaning of the underlying elements. In this example, the <first> tag must be terminated with a corresponding </first>. The following XML would be illegal because the tags are not properly nested:

<name>
  <first>George
    <last>Washington</first>
  </last>
</name>

XML provides an alternate syntax for terminating elements that do not have children, formally known as empty elements . The <term> element is one such example:

<term from="1797" to="1801"/>

The closing slash indicates that this element does not contain any content , although it may contain attributes. An attribute is a name/value pair, such as from="1797". Another requirement for well-formed XML is that all attribute values be enclosed in quotes ("") or apostrophes ('').

Most presidents had middle names, some did not have vice presidents, and others had several vice presidents. For our example XML file, these are known as optional elements. Ulysses Grant, for example, had two vice presidents. He also had a middle name:

<president>
  <term from="1869" to="1877"/>
  <name>
    <first>Ulysses</first>
    <middle>Simpson</middle>
    <last>Grant</last>
  </name>
  <party>Republican</party>
  <vicePresident>
    <name>
      <first>Schuyler</first>
      <last>Colfax</last>
    </name>
  </vicePresident>
  <vicePresident>
    <name>
      <first>Henry</first>
      <last>Wilson</last>
    </name>
  </vicePresident>
</president>

Capitalization is also important in XML. Unlike HTML, all XML tags are case sensitive. This means that <president> is not the same as <PRESIDENT>. It does not matter which capitalization scheme you use, provided you are consistent. As you might guess, since XHTML documents are also XML documents, they too are case sensitive. In XHTML, all tags must be lowercase, such as <html>, <body>, and <head>.

The following list summarizes the basic rules for a well-formed XML document:

  • It must contain exactly one root element; the remainder of the document forms a tree structure, in which every element is contained within exactly one parent.

  • All elements must be properly terminated. For example, <name>Eric</name> is properly terminated because the <name> tag is terminated with </name>. In XML, you can also create empty elements like <married/>.

  • Elements must be properly nested. This is legal:

    <b><i>bold and italic</i></b>

    But this is illegal:

    <b><i>bold and italic</b></i>

  • Attributes must be quoted using either quotes or apostrophes. For example:

    <date month="march" day='01' year="1971"/>

  • Attributes must contain name/value pairs. Some HTML elements contain marker attributes, such as <td nowrap>. In XHTML, you would write this as <td nowrap="nowrap"/>. This is compatible with XML and should work in existing web browsers.

This is not the complete list of rules but is sufficient to get you through the examples in this book. Clearly, most HTML documents are not well-formed. Many tags, such as <br> or <hr>, violate the rule that all elements must be properly terminated. In addition, browsers do not complain when attribute values are not quoted. This will have interesting ramifications for us when we write XSLT stylesheets, which are themselves written in XML but often produce HTML. What this basically means is that the stylesheet must contain well-formed XML, so it is difficult to produce HTML that is not well-formed. XHTML is certainly a more natural fit because it is also XML, just like the XSLT stylesheet.

1.2.3. Validation

A well-formed XML document adheres to the basic syntax guidelines just outlined. A valid XML document goes one step further by adhering to either a Document Type Definition (DTD) or an XML Schema. In order to be considered valid, an XML document must first be well-formed. Stated simply, DTDs are the traditional approach to validation, and XML Schemas are the logical successor. XML Schema is another specification from the W3C and offers much more sophisticated validation capabilities than DTDs. Since XML Schema is very new, DTDs will continue to be used for quite some time. You can learn more about XML Schema at http://www.w3.org/XML/Schema.

The second line of Example 1-1 contains the following document type declaration:

<!DOCTYPE presidents SYSTEM "presidents.dtd">

This refers to the DTD that exists in the same directory as the presidents.xml file. In many cases, the DTD will be referenced by a URI instead:

<!DOCTYPE presidents SYSTEM "http://www.javaxslt.com/dtds/presidents.dtd">

Regardless of where the DTD is located, it contains rules that define the allowable structure of the XML data. Example 1-2 shows the DTD for our list of presidents.

Example 1-2. presidents.dtd

<!ELEMENT presidents (president+)>
<!ELEMENT president (term, name, party, vicePresident*)>
<!ELEMENT name (first, middle*, last, nickname?)>
<!ELEMENT vicePresident (name)>
<!ELEMENT first (#PCDATA)>
<!ELEMENT last (#PCDATA)>
<!ELEMENT middle (#PCDATA)>
<!ELEMENT nickname (#PCDATA)>
<!ELEMENT party (#PCDATA)>
<!ELEMENT term EMPTY>
<!ATTLIST term
    from CDATA #REQUIRED
    to CDATA #REQUIRED
>

The first line in the DTD says that the <presidents> element can contain one or more <president> elements as children. The <president>, in turn, contains one each of <term>, <name>, and <party> in that order. It then may contain zero or more <vicePresident> elements. If the XML data did not adhere to these rules, the XML parser would have rejected it as invalid.

The <name> element can contain the following content: exactly one <first>, followed by zero or more <middle>, followed by exactly one <last>, followed by zero or one <nickname>. If you are wondering why <middle> can occur many times, consider this former president:

<name>
  <first>George</first>
  <middle>Herbert</middle>
  <middle>Walker</middle>
  <last>Bush</last>
</name>

Elements such as <first>George</first> are said to contain #PCDATA , which stands for parsed character data. This is ordinary text that can contain markup, such as nested tags. The CDATA type, which is used for attribute values, cannot contain markup. This means that < characters appearing in attribute values will have to be encoded in your XML documents as &lt;. The <term> element is EMPTY, meaning that it cannot have content. This is not to say that it cannot contain attributes, however. This DTD specifies that <term> must have from and to attributes:

<term from="1869" to="1877"/>

We will not cover the remaining syntax rules for DTDs in this book, primarily because they do not have much impact on our code as we apply XSLT stylesheets. DTDs are primarily used during the parsing process, when XML data is read from a file into memory. When generating XML for a web site, you generally produce new XML rather than parse existing XML, so there is much less need to validate. One area where we will use DTDs, however, is when we examine how to write unit tests for our Java and XSLT code. This will be covered in Chapter 9, "Development Environment, Testing, and Performance".

1.2.4. Java and XML

Java APIs for XML such as SAX, DOM, and JDOM will be used throughout this book. Although we will not go into a great deal of detail on specific parsing APIs, the Java-based XSLT tools do build on these technologies, so it is important to have a basic understanding of what each API does and where it fits into the XML landscape. For in-depth information on any of these topics, you might want to pick up a copy of Java & XML by Brett McLaughlin (O'Reilly).

A parser is a tool that reads XML data into memory. The most common pattern is to parse the XML data from a text file, although Java XML parsers can also read XML from any Java InputStream or even a URL. If a DTD or Schema is used, then validating parsers will ensure that the XML is valid during the parsing process. This means that once your XML files have been successfully parsed into memory, a lot less custom Java validation code has to be written.

1.2.4.1. SAX

In the Java community, Simple API for XML (SAX) is the most commonly used XML parsing method today. SAX is a free API available from David Megginson and members of the XML-DEV mailing list (http://www.xml.org/xml-dev). It can be downloaded[2] from http://www.megginson.com/SAX. Although SAX has been ported to several other languages, we will focus on the Java features. SAX is only responsible for scanning through XML data top to bottom and sending event notifications as elements, text, and other items are encountered; it is up to the recipient of these events to process the data. SAX parsers do not store the entire document in memory, therefore they have the potential to be very fast for even huge files.

[2] One does not generally need to download SAX directly because it is supported by and included with all of the popular XML parsers.

Currently, there are two versions of SAX: 1.0 and 2.0. Many changes were made in version 2.0, and the SAX examples in this book use this version. Most SAX parsers should support the older 1.0 classes and interfaces, however, you will receive deprecation warnings from the Java compiler if you use these older features.

Java SAX parsers are implemented using a series of interfaces. The most important interface is org.xml.sax.ContentHandler , which has methods such as startDocument( ) , startElement( ) , characters( ) , endElement( ) , and endDocument( ) . During the parsing process, startDocument( ) is called once, then startElement( ) and endElement( ) are called once for each tag in the XML data. For the following XML:

<first>George</first>

the startElement( ) method will be called, followed by characters( ), followed by endElement( ). The characters( ) method provides the text "George" in this example. This basic process continues until the end of the document, at which time endDocument( ) is called.

NOTE: Depending on the SAX implementation, the characters( ) method may break up contiguous character data into several chunks of data. In this case, the characters( ) method will be called several times until the character data is entirely parsed.

Since ContentHandler is an interface, it is up to your application code to somehow implement this interface and subsequently do something when the parser invokes its methods. SAX does provide a class called DefaultHandler that implements the ContentHandler interface. To use DefaultHandler, create a subclass and override the methods that interest you. The other methods can safely be ignored, since they are just empty methods. If you are familiar with AWT programming, you may recognize that this idiom is identical to event adapter classes such as java.awt.event.WindowAdapter.

Getting back to XSLT, you may be wondering where SAX fits into the picture. It turns out that XSLT processors typically have the ability to gather input from a series of SAX events as an alternative to static XML files. Somewhat nonintuitively, it also turns out that you can generate your own series of SAX events rather easily -- without using a SAX parser. Since a SAX parser just calls a series of methods on the ContentHandler interface, you can write your own pseudo-parser that does the same thing. We will explore this in Chapter 5, "XSLT Processing with Java" when we talk about using SAX and an XSLT processor to apply transformations to non-XML data, such as results from a database query or content of a comma separated values (CSV) file.

1.2.4.2. DOM

The Document Object Model (DOM) is an API that allows computer programs to manipulate the underlying data structure of an XML document. DOM is a W3C Recommendation, and implementations are available for many programming languages. The in-memory representation of XML is typically referred to as a DOM tree because DOM is a tree data structure. The root of the tree represents the XML document itself, using the org.w3c.dom.Document interface. The document root element, on the other hand, is represented using the org.w3c.dom.Element interface. In the presidents example, the <presidents> element is the document root element. In DOM, almost every interface extends from the org.w3c.dom.Node interface; Document and Element are no exception. The Node interface provides numerous methods to navigate and modify the DOM tree consistently.

Strangely enough, the DOM Level 2 Recommendation does not provide standard mechanisms for reading or writing XML data. Instead, each vendor implementation does this a little bit differently. This is generally not a big problem because every DOM implementation out there provides some mechanism for both parsing and serializing, or writing out XML files. The unfortunate result, however, is that reading and writing XML will cause vendor-specific code to creep into any application you write.

NOTE: At the time of this writing, a new W3C document called "Document Object Model (DOM) Level 3 Content Models and Load and Save Specification" was in the working draft status. Once this specification reaches the recommendation status, DOM will provide a standard mechanism for reading and writing XML.

Since DOM does not specify a standard way to read XML data into memory, most DOM (if not all) implementations delegate this task to a dedicated parser. In the case of Java, SAX is the preferred parsing technology. Figure 1-3 illustrates the typical interaction between SAX parsers and DOM implementations.

Figure 1-3. DOM and SAX interaction

Although it is important to understand how these pieces fit together, we will not go into detailed parsing syntax in this book. As we progress to more sophisticated topics, we will almost always be generating XML dynamically rather than parsing in static XML data files. For this reason, let's look at how DOM can be used to generate a new document from scratch. Example 1-3 contains XML for a personal library.

Example 1-3. library.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library SYSTEM "library.dtd">
<library>
  <!-- This is an XML comment -->
  <publisher id="oreilly">
    <name>O'Reilly</name>
    <street>1005 Gravenstein Hwy North</street>
    <city>Sebastopol</city>
    <state>CA</state>
    <postal>95472</postal>
  </publisher>
  <book publisher="oreilly" isbn="1-56592-709-5">
    <edition>1</edition>
    <publicationDate mm="10" yy="1999"/>
    <title>XML Pocket Reference</title>
    <author>Robert Eckstein</author>
  </book>
  <book publisher="oreilly" isbn="0-596-00016-2">
    <edition>1</edition>
    <publicationDate mm="06" yy="2000"/>
    <title>Java and XML</title>
    <author>Brett McLaughlin</author>
  </book>
</library>

As shown in library.xml, a <library> consists of <publisher> elements and <book> elements. To generate this XML, we will use Java classes called Library, Book, and Publisher. These classes are not shown here, but they are really simple. For example, here is a portion of the Book class:

public class Book {
  private String author;
  private String title;
  ...

  public String getAuthor( ) {
    return this.author;
  }

  public String getTitle( ) {
    return this.title;
  }
  ...
}

Each of these three helper classes is merely used to hold data. The code that creates XML is encapsulated in a separate class called LibraryDOMCreator, which is shown in Example 1-4.

Example 1-4. XML generation using DOM

package chap1;

import java.io.*;
import java.util.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * An example from Chapter 1, "Introduction ". Creates the library XML file using the
 * DOM API.
 */
public class LibraryDOMCreator {

    /**
     * Create a new DOM org.w3c.dom.Document object from the specified
     * Library object.
     *
     * @param library an application defined class that
     * provides a list of publishers and books.
     * @return a new DOM document.
     */
    public Document createDocument(Library library)
            throws javax.xml.parsers.ParserConfigurationException {
        // Use Sun's Java API for XML Parsing to create the
        // DOM Document
        javax.xml.parsers.DocumentBuilderFactory dbf =
            javax.xml.parsers.DocumentBuilderFactory.newInstance( );
        javax.xml.parsers.DocumentBuilder docBuilder =
            dbf.newDocumentBuilder( );
        Document doc = docBuilder.newDocument( );

        // NOTE: DOM does not provide a factory method for creating:
        //   <!DOCTYPE library SYSTEM "library.dtd">
        // Apache's Xerces provides the createDocumentType method
        // on their DocumentImpl class for doing this.  Not used here.

        // create the <library> document root element
        Element root = doc.createElement("library");
        doc.appendChild(root);

        // add <publisher> children to the <library> element
        Iterator publisherIter = library.getPublishers().iterator( );
        while (publisherIter.hasNext( )) {
            Publisher pub = (Publisher) publisherIter.next( );
            Element pubElem = createPublisherElement(doc, pub);
            root.appendChild(pubElem);
        }

        // now add <book> children to the <library> element
        Iterator bookIter = library.getBooks().iterator( );
        while (bookIter.hasNext( )) {
            Book book = (Book) bookIter.next( );
            Element bookElem = createBookElement(doc, book);
            root.appendChild(bookElem);
        }

        return doc;
    }

    private Element createPublisherElement(Document doc, Publisher pub) {
        Element pubElem = doc.createElement("publisher");

        // set id="oreilly" attribute
        pubElem.setAttribute("id", pub.getId( ));

        Element name = doc.createElement("name");
        name.appendChild(doc.createTextNode(pub.getName( )));
        pubElem.appendChild(name);

        Element street = doc.createElement("street");
        street.appendChild(doc.createTextNode(pub.getStreet( )));
        pubElem.appendChild(street);

        Element city = doc.createElement("city");
        city.appendChild(doc.createTextNode(pub.getCity( )));
        pubElem.appendChild(city);

        Element state= doc.createElement("state");
        state.appendChild(doc.createTextNode(pub.getState( )));
        pubElem.appendChild(state);

        Element postal = doc.createElement("postal");
        postal.appendChild(doc.createTextNode(pub.getPostal( )));
        pubElem.appendChild(postal);

        return pubElem;
    }

    private Element createBookElement(Document doc, Book book) {
        Element bookElem = doc.createElement("book");

        bookElem.setAttribute("publisher", book.getPublisher().getId( ));
        bookElem.setAttribute("isbn", book.getISBN( ));

        Element edition = doc.createElement("edition");
        edition.appendChild(doc.createTextNode(
                Integer.toString(book.getEdition( ))));
        bookElem.appendChild(edition);

        Element publicationDate = doc.createElement("publicationDate");
        publicationDate.setAttribute("mm",
                Integer.toString(book.getPublicationMonth( )));
        publicationDate.setAttribute("yy",
                Integer.toString(book.getPublicationYear( )));
        bookElem.appendChild(publicationDate);

        Element title = doc.createElement("title");
        title.appendChild(doc.createTextNode(book.getTitle( )));
        bookElem.appendChild(title);

        Element author = doc.createElement("author");
        author.appendChild(doc.createTextNode(book.getAuthor( )));
        bookElem.appendChild(author);

        return bookElem;
    }

    public static void main(String[] args) throws IOException,
            javax.xml.parsers.ParserConfigurationException {
        Library lib = new Library( );
        LibraryDOMCreator ldc = new LibraryDOMCreator( );
        Document doc = ldc.createDocument(lib);

        // write the Document using Apache Xerces
        // output the Document with UTF-8 encoding; indent each line
        org.apache.xml.serialize.OutputFormat fmt =
            new org.apache.xml.serialize.OutputFormat(doc, "UTF-8", true);
        org.apache.xml.serialize.XMLSerializer serial =
            new org.apache.xml.serialize.XMLSerializer(System.out, fmt);
        serial.serialize(doc.getDocumentElement( ));
    }
}

This example starts with the usual series of import statements. Notice that org.w3c.dom.* is imported, but packages such as org.apache.xml.serialize.* are not. The code is written this way in order to make it obvious that many of the classes you will use are not part of the standard DOM API. These nonstandard classes all use fully qualified class and package names in the code. Although DOM itself is a W3C recommendation, many common tasks are not covered by the spec and can only be accomplished by reverting to vendor-specific code.

The workhorse of this class is the createDocument method, which takes a Library as a parameter and returns an org.w3c.dom.Document object. This method could throw a ParserConfigurationException, which indicates that Sun's Java API for XML Parsing (JAXP) could not locate an XML parser:

public Document createDocument(Library library)
        throws javax.xml.parsers.ParserConfigurationException {

The Library class simply stores data representing a personal library of books. In a real application, the Library class might also be responsible for connecting to a back-end data source. This arrangement provides a clear separation between XML generation code and the underlying database. The sole purpose of LibraryDOMCreator is to crank out DOM trees, making it easy for one programmer to work on this class while another focuses on the implementation of Library, Book, and Publisher.

The next step is to begin constructing a DOM Document object:

javax.xml.parsers.DocumentBuilderFactory dbf =
    javax.xml.parsers.DocumentBuilderFactory.newInstance( );
javax.xml.parsers.DocumentBuilder docBuilder =
    dbf.newDocumentBuilder( );
Document doc = docBuilder.newDocument( );

This code relies on JAXP because the standard DOM API does not provide any support for creating a new Document object in a standard way. Different parsers have their own proprietary way of doing this, which brings us to the whole point of JAXP: it encapsulates differences between various XML parsers, allowing Java programmers to use a consistent API regardless of which parser they use. As we will see in Chapter 5, "XSLT Processing with Java", JAXP 1.1 adds a consistent wrapper around various XSLT processors in addition to standard SAX and DOM parsers.

JAXP provides a DocumentBuilderFactory to construct a DocumentBuilder, which is then used to construct new Document objects. The Document class is a part of DOM, so most of the remaining code is defined by the DOM specification.

In DOM, new XML elements must always be created using factory methods, such as createElement(...), on an instance of Document. These elements must then be added to either the document itself or one of the elements within the document before they actually become part of the XML:

// create the <library> document root element
Element root = doc.createElement("library");
doc.appendChild(root);

At this point, the <library/> element is empty, but it has been added to the document. The code then proceeds to add all <publisher> children:

// add <publisher> children to the <library> element
Iterator publisherIter = library.getPublishers().iterator( );
while (publisherIter.hasNext( )) {
    Publisher pub = (Publisher) publisherIter.next( );
    Element pubElem = createPublisherElement(doc, pub);
    root.appendChild(pubElem);
}

For each instance of Publisher, a <publisher> Element is created and then added to <library>. The createPublisherElement method is a private helper method that simply goes through the tedious DOM steps required to create each XML element. One thing that may not seem entirely obvious is the way that text is added to elements, such as O'Reilly in the <name>O'Reilly</name> tag:

Element name = doc.createElement("name");
name.appendChild(doc.createTextNode(pub.getName( )));
pubElem.appendChild(name);

The first line is pretty obvious, simply creating an empty <name/> element. The next line then adds a new text node as a child of the name object rather than setting the value directly on the name. This is indicative of the way that DOM represents XML: any parsed character data is considered to be a child of a node, rather than part of the node itself. DOM uses the org.w3c.dom.Text interface, which extends from org.w3c.dom.Node, to represent text nodes. This is often a nuisance because it results in at least one extra line of code for each element you wish to generate.

The main() method in Example 1-4 creates a Library object, converts it into a DOM tree, then prints the XML text to System.out. Since the standard DOM API does not provide a standard way to convert a DOM tree to XML, we introduce Xerces specific code to convert the DOM tree to text form:

// write the document using Apache Xerces
// output the document with UTF-8 encoding; indent each line
org.apache.xml.serialize.OutputFormat fmt =
    new org.apache.xml.serialize.OutputFormat(doc, "UTF-8", true);
org.apache.xml.serialize.XMLSerializer serial =
    new org.apache.xml.serialize.XMLSerializer(System.out, fmt);
serial.serialize(doc.getDocumentElement( ));

As we will see in Chapter 5, "XSLT Processing with Java", JAXP 1.1 does provide a mechanism to perform this task using its transformation APIs, so we do not technically have to use the Xerces code listed here. The JAXP approach maximizes portability but introduces the overhead of an XSLT processor when all we really need is DOM.

1.2.4.3. JDOM

DOM is specified in the language independent Common Object Request Broker Architecture Interface Definition Language (CORBA IDL), allowing the same interfaces and concepts to be utilized by many different programming languages. Though valuable from a specification perspective, this approach does not take advantage of specific Java language features. JDOM is a Java-only API that can be used to create and modify XML documents in a more natural way. By taking advantage of Java features, JDOM aims to simplify some of the more tedious aspects of DOM programming.

JDOM is not a W3C specification, but is open source software[3] available at http://www.jdom.org. JDOM is great from a programming perspective because it results in much cleaner, more maintainable code. Since JDOM has the ability to convert its data into a standard DOM tree, it integrates nicely with any other XML tool. JDOM can also utilize whatever XML parser you specify and can write out XML to any Java output stream or file. It even features a class called SAXOutputter that allows the JDOM data to be integrated with any tool that expects a series of SAX events.

[3] Sun has accepted JDOM as Java Specification Request (JSR) 000102; see http://java.sun.com/aboutJava/communityprocess/.

The code in Example 1-5 shows how much easier JDOM is than DOM; it does the same thing as the DOM example, but is about fifty lines shorter. This difference would be greater for more complex applications.

Example 1-5. XML generation using JDOM

package com.oreilly.javaxslt.chap1;

import java.io.*;
import java.util.*;
import org.jdom.DocType;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

/**
 * An example from Chapter 1, "Introduction ". Creates the library XML file.
 */
public class LibraryJDOMCreator {

    public Document createDocument(Library library) {
        Element root = new Element("library");
        // JDOM supports the <!DOCTYPE...>
        DocType dt = new DocType("library", "library.dtd");
        Document doc = new Document(root, dt);

        // add <publisher> children to the <library> element
        Iterator publisherIter = library.getPublishers().iterator( );
        while (publisherIter.hasNext( )) {
            Publisher pub = (Publisher) publisherIter.next( );
            Element pubElem = createPublisherElement(pub);
            root.addContent(pubElem);
        }

        // now add <book> children to the <library> element
        Iterator bookIter = library.getBooks().iterator( );
        while (bookIter.hasNext( )) {
            Book book = (Book) bookIter.next( );
            Element bookElem = createBookElement(book);
            root.addContent(bookElem);
        }

        return doc;
    }

    private Element createPublisherElement(Publisher pub) {
        Element pubElem = new Element("publisher");

        pubElem.addAttribute("id", pub.getId( ));
        pubElem.addContent(new Element("name").setText(pub.getName( )));
        pubElem.addContent(new Element("street").setText(pub.getStreet( )));
        pubElem.addContent(new Element("city").setText(pub.getCity( )));
        pubElem.addContent(new Element("state").setText(pub.getState( )));
        pubElem.addContent(new Element("postal").setText(pub.getPostal( )));

        return pubElem;
    }

    private Element createBookElement(Book book) {
        Element bookElem = new Element("book");

        // add publisher="oreilly" and isbn="1234567" attributes
        // to the <book> element
        bookElem.addAttribute("publisher", book.getPublisher().getId( ))
                .addAttribute("isbn", book.getISBN( ));

        // now add an <edition> element to <book>
        bookElem.addContent(new Element("edition").setText(
                Integer.toString(book.getEdition( ))));

        Element pubDate = new Element("publicationDate");
        pubDate.addAttribute("mm",
                Integer.toString(book.getPublicationMonth( )));
        pubDate.addAttribute("yy",
                Integer.toString(book.getPublicationYear( )));
        bookElem.addContent(pubDate);

        bookElem.addContent(new Element("title").setText(book.getTitle( )));
        bookElem.addContent(new Element("author").setText(book.getAuthor( )));

        return bookElem;
    }

    public static void main(String[] args) throws IOException {
        Library lib = new Library( );
        LibraryJDOMCreator ljc = new LibraryJDOMCreator( );
        Document doc = ljc.createDocument(lib);

        // Write the XML to System.out, indent two spaces, include
        // newlines after each element
        new XMLOutputter("  ", true, "UTF-8").output(doc, System.out);
    }
}

The JDOM example is structured just like the DOM example, beginning with a method that converts a Library object into a JDOM Document:

public Document createDocument(Library library) {

The most striking difference in this particular method is the way in which the Document and its Element s are created. In JDOM, you simply create Java objects to represent items in your XML data. This contrasts with the DOM approach, which relies on interfaces and factory methods. Creating the Document is also easy in JDOM:

Element root = new Element("library");
// JDOM supports the <!DOCTYPE...>
DocType dt = new DocType("library", "library.dtd");
Document doc = new Document(root, dt);

As this comment indicates, JDOM allows you to refer to a DTD, while DOM does not. This is just another odd limitation of DOM that forces you to include implementation-specific code in your Java applications. Another area where JDOM shines is in its ability to create new elements. Unlike DOM, text is set directly on the Element objects, which is more intuitive to Java programmers:

private Element createPublisherElement(Publisher pub) {
    Element pubElem = new Element("publisher");

    pubElem.addAttribute("id", pub.getId( ));
    pubElem.addContent(new Element("name").setText(pub.getName( )));
    pubElem.addContent(new Element("street").setText(pub.getStreet( )));
    pubElem.addContent(new Element("city").setText(pub.getCity( )));
    pubElem.addContent(new Element("state").setText(pub.getState( )));
    pubElem.addContent(new Element("postal").setText(pub.getPostal( )));

    return pubElem;
}

Since methods such as addContent( ) and addAttribute( ) return a reference to the Element instance, the code shown here could have been written as one long line. This is similar to StringBuffer.append( ), which can also be "chained" together:

buf.append("a").append("b").append("c");

In an effort to keep the JDOM code more readable, however, our example adds one element per line.

The final piece of this pie is the ability to print out the contents of JDOM as an XML file. JDOM includes a class called XMLOutputter, which allows us to generate the XML for a Document object in a single line of code:

new XMLOutputter("  ", true, "UTF-8").output(doc, System.out);

The three arguments to XMLOutputter indicate that it should use two spaces for indentation, include linefeeds, and encode its output using UTF-8.

1.2.4.4. JDOM and DOM interoperability

Current XSLT processors are very flexible, generally supporting any of the following sources for XML or XSLT input:

  • a DOM tree or output from a SAX parser

  • any Java InputStream or Reader

  • a URI, file name, or java.io.File object

JDOM is not directly supported by some XSLT processors, although this is changing fast.[4] For this reason, it is typical to convert a JDOM Document instance to some other format so it can be fed into an XSLT processor for transformation. Fortunately, the JDOM package provides a class called DOMOutputter that can easily make the transformation:

[4] As this book went to press, Version 6.4 of SAXON was released with beta support for transforming JDOM trees. Additionally, JDOM beta 7 introduces two new classes, JDOMSource and JDOMResult, that interoperate with any JAXP-compliant XSLT processor.

org.jdom.output.DOMOutputter outputter =
        new org.jdom.output.DOMOutputter( );
org.w3c.dom.Document domDoc = outputter.output(jdomDoc);

The DOM Document object can then be used with any of the XSLT processors or a whole host of other XML libraries and tools. JDOM also includes a class that can convert a Document into a series of SAX events and another that can send XML data to an OutputStream or Writer. In time, it seems likely that tools will begin offering native support for JDOM, making extra conversions unnecessary. The details of all these techniques are covered in Chapter 5, "XSLT Processing with Java".

Previous | Home | Next
1. Introduction | Book Index | 1.3. Beyond Dynamic Web Pages
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Beyond Dynamic Web Pages (Java and XSLT)

 Java and XSLTSearch this book

1.3. Beyond Dynamic Web Pages

You probably know a little bit about servlets already. Essentially, they are Java classes that run on the web tier, offering a high-performance, portable alternative to CGI scripts. Java servlets are great for extracting data from a database and then generating XHTML for the browser. They are also good for validating HTTP POST or GET requests from browsers, allowing people to fill out job applications or order books online. But more powerful techniques are required when you create web applications instead of simple web sites.

1.3.1. Web Development Challenges

When compared to GUI applications based on Swing or AWT, developing for the Web can be much more difficult. Most of the difficulties you will encounter can be traced to one of the following:

  • Hypertext Transfer Protocol (HTTP)

  • HTML limitations

  • browser compatibility problems

  • concurrency issues

HTTP is a fairly simple protocol that enables a client to communicate with a server. Web browsers almost always use HTTP to communicate with web servers, although they may use other protocols such as HTTPS for secure connections or even FTP for file downloads. HTTP is a request/response protocol, and the browser must initiate the request. Each time you click on a hyperlink, your browser issues a new request to a web server. The server processes the request and sends a response, thus finishing the exchange.

This request/response cycle is easy to understand but makes it tedious to develop an application that maintains state information as the user moves through a complex web application. For example, as a user adds items to a shopping cart, a servlet must store that data somewhere while waiting for the client to make another request. When that request arrives, the servlet has to associate the cart with that particular client, since the servlet could be dealing with hundreds or thousands of concurrent clients. Other than establishing a timeout period, the servlet has no idea when the client abandons the cart, deciding to shop on a competitor's site instead. The HTTP protocol makes it impossible for the server to initiate a conversation with the client, so the servlet cannot periodically ping the client as it can with a "normal" client/server application.

HTML itself can be another hindrance to web application development. It was not designed to compete with feature-rich GUI toolkits, yet customers are increasingly demanding that applications of all sorts become "web enabled." This presents a significant challenge because HTML offers only a small set of primitive GUI components. Sophisticated HTML generation is not the subject of this book, but we will see how to use XSLT to separate complex HTML generation code from underlying programming logic and servlet code. As HTML grows ever more complex, the benefits of a clean separation become increasingly obvious.

As you probably well know, browsers are not entirely compatible with one another. As a web application developer, this generally means that you have to test on a wide variety of platforms. XSLT offers support in this area because you can write reusable stylesheets for the consistent parts of HTML and import or include browser-specific stylesheet fragments to work around browser incompatibilities. Of course, the underlying XML data and programming logic is shared across all browsers, even though you may have multiple stylesheets.

Finally, we have the issue of concurrency. In the servlet model, a single servlet instance must handle multiple concurrent requests. Although you can explicitly synchronize access to a servlet, this often results in performance degradation as individual client requests queue up, waiting for their turn. Processing requests in parallel will be an important part of our XSLT-based servlet designs in later chapters.

1.3.2. Web Applications

The difference between a "web site" and a "web application" is subjective. Although some of the technologies are the same, web applications tend to be far more interactive and more difficult to create than typical web sites. For example, a web site is mostly read-only, with occasional forms for submitting information. For this, simple technologies such as HTML combined with JavaServer Pages (JSPs) can do the job. A web application, on the other hand, is typically a custom application intended to perform a specific business or technical function. They are often written as replacements for existing systems in an effort to enable browser-based access. When replacing existing systems, developers are typically asked to duplicate all of the existing functionality, using a web browser and HTML. This is difficult at best because of HTML's limited support for sophisticated GUI components. Most of the screens in a web application are dynamically generated and customized on a per-user basis, while many pages on a typical web site are static.

Java, XML, and XSLT are suitable for web applications because of the high degree of modularity they offer. While one programmer develops the back-end data access code, a graphic designer can be working on the HTML user interface. Yet another servlet expert can be working on the web tier, while someone else is defining and creating the XML data. Programmers and graphic designers will typically work together to define the XSLT stylesheets, although the current lack of interactive tools may make this more of a programming task.

Another reason XML is suitable for web applications is its unique ability to interoperate with back-end business systems and databases. Once an XML layer has been added to your data tier, the web tier can extract that data in XML form regardless of which operating system or hardware platform is used. XSLT can then convert that XML into HTML without a great deal of custom coding, resulting in less work for your development team.

1.3.3. Nonbrowser Clients

While web sites typically deliver HTML to browsers, web applications may be asked to interoperate with applications other than browsers. It is typical to provide feature-rich Swing GUI clients for use within a company, while remote workers access the system via an XHTML interface through a web browser. An XML approach is key in this environment because the raw XML can be sent to the Swing client, while XSLT can be used to generate the XHTML views from the same XML data.

If your XML is not in the correct format, XSLT can also be used to transform it into another variant of XML. For example, a client application may expect to see:

<name>Eric Burke</name>

But the XML data on the web tier deals with the data as:

<firstName>Eric</firstName><lastName>Burke</lastName>

In this case, XSLT can be used to transform the XML into the simplified format that the client expects.

1.3.3.1. SOAP

Sending raw XML data to clients is a good approach because it interoperates with any operating system, hardware platform, or programming language. Allowing Visual Basic clients to extract XML data from a web application allows existing client software to be salvaged while enabling remote access to enterprise data using a more portable solution such as Java. But defining a custom XML format is tedious because it requires you to manually write code that encodes and decodes messages between the client and the web application.

Simple Object Access Protocol (SOAP) is a standardized protocol for exchanging data using XML messages. SOAP was originally introduced by Microsoft but has been submitted to the W3C for standardization and is endorsed by many companies. SOAP is fairly simple, allowing vendors to quickly create tools that simplify data exchange between web applications and any type of client.

Since SOAP messages are implemented using XML, they can be created and updated using XSLT stylesheets. This means that data can be extracted from a relational database as XML, transformed with XSLT into a standard SOAP message, and then delivered to a client application written in any language. For more information on SOAP standardization efforts, visit http://www.w3.org/TR/SOAP.

1.3.4. Wireless

Cell phones, personal digital assistants (PDAs), and other handheld devices seem to be the next big thing. From a marketing perspective, it is not entirely clear how the business model of the Web will translate to the world of wireless. It is also unclear which technologies will be used for this new generation of devices. One currently popular technology is Wireless Application Protocol (WAP), which uses an XML markup language called Wireless Markup Language (WML) to render pages. Other languages have been proposed, such as Compact HTML (CHTML), but perhaps the most promising prospect is XHTML Basic. XHTML Basic is backed by the W3C and is primarily based on several XHTML modules. Its designers had the luxury of coming after WML, so they could incorporate many WML concepts and build on that experience.

Because of the uncertainties in the wireless arena, an XML and XSLT approach is the safest available today. Encoding your data in XML enables flexibility to support any markup language or protocol on the client, hopefully without rewriting major pieces of Java code. Instead, new XSLT stylesheets are written to support new devices and protocols. An added benefit of XSLT is its ability to support both traditional browser clients and newer wireless clients from the same underlying XML data and Java business logic.

Previous | Home | Next
1.2. XML Review | Book Index | 1.4. Getting Started
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Getting Started (Java and XSLT)

 Java and XSLTSearch this book

1.4. Getting Started

The best way to get started with new technologies is to experiment. For example, if you do not know XSLT, you should experiment with plenty of stylesheets as you work through the next two chapters. Aside from trying out the examples that appear in this book, you may want to invent a simple XML data file that represents something of interest to you, such as your personal music collection or family tree. Using XSLT stylesheets, try to create web pages that show your data in many different formats.

Once the basics of XSLT are out of the way, servlets will be your next big challenge. Although the servlet API is not particularly difficult to learn, configuration and deployment issues can make it difficult to debug and test your applications. The best advice is to start small, writing a very basic application that proves your environment is configured correctly before moving on to more sophisticated examples. Apache's Tomcat is probably the best servlet container for beginners because it is free, easy to configure, and is the official reference implementation for Sun's servlet API. A servlet container is the server that runs servlets. Chapter 6, "Servlet Basics and XSLT" covers the essentials of the servlet API, but for all the details you will want to pick up a copy of Java Servlet Programming by Jason Hunter (O'Reilly). You definitely want to get the second edition because it covers the dramatic changes that were introduced in Version 2.2 of the servlet API.

1.4.1. Java XSLT Processor Choices

Although this book uses primarily Sun's JAXP and Apache's Xalan, many other XSLT processors are available. Processors based on other languages may offer much higher performance when invoked from the command line, primarily because they do not incur the overhead of a Java Virtual Machine (JVM) at application startup time. When using XSLT from a servlet, however, the JVM is already running, so startup time is no longer an issue. Pure Java processors are great for servlets because of the ease with which they can be embedded into the web application. Simply adding a JAR file to the CLASSPATH is generally all that must be done.

Putting an up-to-date list of XSLT processors into a book is futile because the market is maturing too fast. Some of the currently popular Java-based processors are listed here, but a quick web search for "XSLT Processors" would be prudent before you decide to standardize on a particular tool, as new processors are constantly appearing. We will see how to use Xalan in the next chapter; a few other choices are listed here.

1.4.1.1. XT

XT was one of the earliest XSLT processors, written by James Clark. If you read the XSLT specification, you may recognize him as the editor of the XSLT specification. As the XSLT specification evolved, XT followed a parallel path of evolution, making it a leader in terms of standards compliance. At the time of this writing, however, XT had not been updated as recently as some of the other Java- based processors. Version 19991105 of XT implements the W3C's proposed-recommendation (PR-xslt-19991008) version of XSLT and is available at http://www.jclark.com/xml/xt.html. Like the other processors listed here, XT is free.

1.4.1.2. LotusXSL

LotusXSL is a Java XSLT processor from IBM Alphaworks available at http://www.alphaworks.ibm.com. In November 1999 IBM donated LotusXSL to Apache, forming the basis for Xalan. LotusXSL continued to exist as a separate product. However, it is currently a thin wrapper around the Xalan processor. Future versions of LotusXSL may add features above and beyond those offered by Xalan, but there doesn't seem to be a compelling reason to choose LotusXSL unless you are already using it.

1.4.1.3. SAXON

The SAXON XSLT processor from Michael Kay is available at http://saxon.sourceforge.net. SAXON is open source software in accordance with the Mozilla Public License and is a very popular alternative to Xalan. SAXON provides full support for the current XSLT specification and is very well documented. It also provides several value-added features such as the ability to output multiple result trees from the same transformation and update the values of variables within stylesheets.

To transform a document using SAXON, first include saxon.jar in your CLASSPATH. Then type java com.icl.saxon.StyleSheet -? to list all available options. The basic syntax for transforming a stylesheet is as follows:

java com.icl.saxon.StyleSheet [options] source-doc style-doc [ params...]

To transform the presidents.xml file and send the results to standard output, type the following:

java com.icl.saxon.StyleSheet presidents.xml presidents.xslt

1.4.1.4. JAXP

Version 1.1 of Sun's Java API for XML Processing (JAXP) contains support for XSLT transformations, a notable omission from earlier versions of JAXP. It can be downloaded from http://java.sun.com/xml. Parsing XML and transforming XSLT are not the primary focus of JAXP. Instead, the key goal is to provide a standard Java interface to a wide variety of XML parsers and XSLT processors. Although JAXP does include reference implementations of XML parsers and an XSLT processor, its key benefit is the choice of tools afforded to Java developers. Vendor lock-in should be much less of an issue thanks to JAXP.

Since JAXP is primarily a Java-based API, we will cover its programmatic interfaces in depth as we talk about XSLT programming techniques in Chapter 5, "XSLT Processing with Java". JAXP currently includes Apache's Xalan as its default XSLT processor, so the Xalan instructions presented in Chapter 2, "XSLT Part 1 -- The Basics" will also apply to JAXP.

Previous | Home | Next
1.3. Beyond Dynamic Web Pages | Book Index | 1.5. Web Browser Support for XSLT
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Web Browser Support for XSLT (Java and XSLT)

 Java and XSLTSearch this book

1.5. Web Browser Support for XSLT

In a web application environment, performing XSLT transformations on the client instead of the server is valuable for a number of reasons. Most importantly, it reduces the workload on the server machine, allowing a greater number of clients to be served. Once a stylesheet is downloaded to the client, subsequent requests will presumably use a cached copy, therefore only the raw XML data will need to be transmitted with each request. This has the potential to greatly reduce bandwidth requirements.

Even more interesting tricks are possible when JavaScript is introduced into the equation. You can programmatically modify either the XML data or the XSLT stylesheet on the client side, reapply the stylesheet, and see the results immediately without requesting a new document from the server.

Microsoft introduced XSLT support into Version 5.0 of Internet Explorer, but the XSLT specification was not finalized at the time. Unfortunately, significant changes were made to XSLT before it was finally promoted to a W3C Recommendation, but IE had already shipped using the older version of the specification. Although Microsoft has done a good job updating its MSXML parser with full support for the final XSLT Recommendation, millions of users will probably stick to IE 5.0 or 5.5 for quite some time, making it very difficult to perform portable XSLT transformations on the client. For IE 5.0 or 5.5 users, the MSXML parser is available as a separate download from Microsoft. Once downloaded, installed, and configured using a separate program called xmlinst, the browser will be compliant with Version 1.0 of the XSLT recommendation. This is something that developers will want to do, but probably very few end users will have the technical skills to go through these steps.

At the time of this writing, Netscape had not introduced support for XSLT into its browsers. We hope this changes by the time this book is published. Although their implementation will be released much later than Microsoft's, it should be compliant with the latest XSLT Recommendation.

Yet another alternative is to utilize a browser plug-in that supports XSLT, although this approach is probably most effective within the confines of a corporation. In this environment, the browser can be controlled to a certain extent, allowing client-side transformations much sooner than possible on public web sites.

Because XSLT transformation on the client will likely be mired in browser compatibility issues for several years, the role of Java with respect to XSLT will continue to be important. One use will be to detect the browser using a Java servlet, and then deliver the appropriate stylesheet to the client only if a compliant browser is in use. Otherwise, the servlet will drive the transformation process by invoking the XSLT processor on the web server. Once we finish with XSLT syntax in the next two chapters, the role of Java and XSLT will be covered throughout the remainder of this book.

Previous | Home | Next
1.4. Getting Started | Book Index | 2. XSLT Part 1 -- The Basics
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Part 1 -- The Basics (Java and XSLT)

 Java and XSLTSearch this book

Chapter 2. XSLT Part 1 -- The Basics

Contents:

XSLT Introduction
Transformation Process
Another XSLT Example, Using XHTML
XPath Basics
Looping and Sorting
Outputting Dynamic Attributes

Extensible Stylesheet Language (XSL) is a specification from the World Wide Web Consortium (W3C) and is broken down into two complementary technologies: XSL Formatting Objects and XSL Transformations (XSLT). XSL Formatting Objects, a language for defining formatting such as fonts and page layout, is not covered in this book. XSLT, on the other hand, was primarily designed to transform a well-formed XML document into XSL Formatting Objects.

Even though XSLT was designed to support XSL Formatting Objects, it has emerged as the preferred technology for all sorts of transformations. Transformation from XML to HTML is the most common, but XSLT can also be used to transform well-formed XML into just about any text file format. This will give XML- and XSLT-based web sites a major leg up as wireless devices become more prevalent because XSLT can also be used to transform XML into Wireless Markup Language or some other stripped-down format that wireless devices will require.

2.1. XSLT Introduction

Why is transformation so important? XML provides a simple syntax for defining markup, but it is up to individuals and organizations to define specific markup languages. There is no guarantee that two organizations will use the exact same markup; in fact, you may struggle to agree on consistent formats within the same group or company. One group may use <employee>, while others may use <worker> or <associate>. In order to share data, the XML data has to be transformed into a common format. This is where XSLT shines -- it eliminates the need to write custom computer programs to transform data. Instead, you simply create one or more XSLT stylesheets.

An XSLT processor is an application that applies an XSLT stylesheet to an XML data source. Instead of modifying the original XML data, the result of the transformation is copied into something called a result tree, which can be directed to a static file, sent directly to an output stream, or even piped into another XSLT processor for further transformations. Figure 2-1 illustrates the transformation process, showing how the XML input, XSLT stylesheet, XSLT processor, and result tree relate to one another.

Figure 2-1. XSLT transformation

The XML input and XSLT stylesheet are normally two separate entities.[5] For the examples in this chapter, the XML will always reside in a text file. In future chapters, however, we will see how to improve performance by dealing with the XML as an in-memory object tree. This makes sense from a Java/XSLT perspective because most web applications will generate XML dynamically rather than deal with a series of static files. Since the XML data and XSLT stylesheet are clearly separated, it is very plausible to write several different stylesheets that convert the same XML into radically different formats.

[5] Section 2.7 of the XSLT specification covers embedded stylesheets.

XSLT transformation can occur on either the client or server, although server-side transformations are currently dominant. Since a vast majority of Internet users do not use XSLT-compliant browsers (at the time of this writing), the typical model is to transform XML into HTML on the web server so the browser sees only the resulting HTML. In a closed corporate environment where the browser feature set can be controlled, moving the XSLT transformation process to the browser can improve scalability and reduce network traffic.

It should be noted that XSLT stylesheets do not perform the same function as Cascading Style Sheets (CSS), which you may be familiar with. In the CSS model, style elements are applied to HTML or XML on the web browser, affecting formatting such as fonts and colors. CSS do not produce a separate result tree and cannot be applied in advance using a standalone processor as XSLT can. The CSS processing model operates on the underlying data in a top down fashion in a single pass, while XSLT can iterate and perform conditional logic on the XML data. Although XSLT can produce style instructions, its true role is that of a transformation language rather than a style language. XSL Formatting Objects, on the other hand, is a style language that is much more comparable to CSS.

For wireless applications, HTML is not typically generated. Instead, Wireless Markup Language (WML) is the current standard for cell phones and other wireless devices. In the future, new standards such as XHTML Basic may be used. When using an XSLT approach, the same XML data can be transformed into many forms, all via different stylesheets. Regardless of how many stylesheets are used, the XML data will remain unchanged. A typical web site might have the following stylesheets for a single XML home page:

homeBasic.xslt
For older web browsers

homeIE5.xslt
Takes advantage of newer Internet Explorer features

homeMozilla.xslt
Takes advantage of newer Netscape features

homeWML.xslt
Transforms into Wireless Markup Language

homeB2B.xslt
Transforms the XML into another XML format, suitable for "B2B-style" XML data feeds to customers

Schema evolution implies an upgrade to an existing data source where the structure of the data must be modified. When the data is stored in XML format, XSLT can be used to support schema evolution. For example, Version 1.0 of your application may store all of its files in XML format, but Version 2.0 might add new features that cannot be supported by the old 1.0 file format. A perfect solution is to write a single stylesheet to transform all of the old 1.0 XML files to the new 2.0 file format.

2.1.1. An XSLT Example

You need three components to perform XSLT transformations: an XML data source, an XSLT stylesheet, and an XSLT processor. The XSLT stylesheet is actually a well-formed XML document, so the XSLT processor will also include or use an XML parser. Apache's Xalan is used for most of the examples in this book; the previous chapter listed several other processors that you may want to investigate. You can download Xalan from http://xml.apache.org. It uses and includes Apache's Xerces parser, but can be configured to use other parsers. The ability to swap out parsers is important because this gives you the flexibility to use the latest innovations as competing (and perhaps faster) parsers are released.

Example 2-1 represents an early prototype of a discussion forum home page. The complete discussion forum application will be developed in Chapter 7, "Discussion Forum". This is the raw XML data, without any formatting instructions or HTML. As you can see, the home page simply lists the message boards that the user can choose to view.

Example 2-1. discussionForumHome.xml

<?xml version="1.0" encoding="UTF-8"?>
<discussionForumHome>
  <messageBoard id="1" name="Java Programming"/>
  <messageBoard id="2" name="XML Programming"/>
  <messageBoard id="3" name="XSLT Questions"/>
</discussionForumHome>

It is assumed that this data will be generated dynamically as the result of a database query, rather than hardcoded as a static XML file. Regardless of its origin, the XML data says nothing about how to actually display the web page. For clarity, we will keep the XSLT stylesheet fairly simple at this point. The beauty of an XML/XSLT approach is that you can beef up the stylesheet later on without compromising any of the underlying XML data structures. Even more importantly, the Java code that will generate the XML data does not have to be cluttered up with HTML and user interface logic; it just produces the basic XML data. Once the format of the data has been defined, a Java programmer can begin working on the database logic and XML generation code, while another team member begins writing the XSLT stylesheets.

Example 2-2 lists the XSLT stylesheet that produces the home page. Don't worry if not everything in this first example makes sense. XSLT is, after all, a completely new language. We will cover everything in detail throughout the remainder of this and the next chapter.

Example 2-2. discussionForumHome.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>

  <!-- match the document root -->
  <xsl:template match="/">
    <html>
      <head>
        <title>Discussion Forum Home Page</title>
      </head>
      <body>
        <h1>Discussion Forum Home Page</h1>
        <h3>Please select a message board to view:</h3>
        <ul>
          <xsl:apply-templates select="discussionForumHome/messageBoard"/>
        </ul>
      </body>
    </html>
  </xsl:template>

  <!-- match a <messageBoard> element -->
  <xsl:template match="messageBoard">
    <li>
      <a href="viewForum?id={@id}">
        <xsl:value-of select="@name"/>
      </a>
    </li>
  </xsl:template>
</xsl:stylesheet>
NOTE: The filename extension for XSLT stylesheets is irrelevant. In this book,.xslt is used. Many stylesheet authors prefer .xsl.

The first thing that should jump out immediately is the fact that the XSLT stylesheet is also a well-formed XML document. Do not let the xsl: namespace prefix fool you -- everything in this document adheres to the same basic rules that every other XML document must follow. Like other XML files, the first line of the stylesheet is an XML declaration:

<?xml version="1.0" encoding="UTF-8"?>

Unless you are dealing with internationalization issues, this will remain unchanged for every stylesheet you write. This line is immediately followed by the document root element, which contains the remainder of the stylesheet:

<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

The <xsl:stylesheet> element has two attributes in this case. The first, version="1.0", specifies the version of the XSLT specification. Although this is the current version at the time of this writing, the next version of the XSLT specification is well underway and may be finished by the time you read this. You can stay abreast of the latest XSLT developments by visiting the W3C home page at http://www.w3.org.

The next attribute declares the XML namespace, defining the meaning of the xsl: prefix you see on all of the XSLT elements. The prefix xsl is conventional, but could be anything you choose. This is useful if your document already uses the xsl prefix for other elements, and you do not want to introduce a naming conflict. This is really the entire point of namespaces: they help to avoid name conflicts. In XML, <a:book> and <b:book> can be discerned from one another because each book has a different namespace prefix. Since you pick the namespace prefix, this avoids the possibility that two vendors will use conflicting prefixes.

In the case of XSLT, the namespace prefix does not have to be xsl, but the value does have to be http://www.w3.org/1999/XSL/Transform. The value of a namespace is not necessarily a real web site, but the syntax is convenient because it helps ensure uniqueness. In the case of XSLT, 1999 represents the year that the URL was allocated for this purpose, and is not related to the version number. It is almost certain that future versions of XSLT will continue to use this same URL.

WARNING: Even the slightest typo in the namespace will render the stylesheet useless for most processors. The text must match http://www.w3.org/1999/XSL/Transform exactly, or your stylesheet will not be processed. Spelling or capitalization errors are a common mistake and should be the first thing you check when things are not working as you expect.

The next line of the stylesheet simply indicates that the result tree should be treated as an HTML document instead of an XML document:

<xsl:output method="html"/>

In Version 1.0 of XSLT, processors are not required to fully support this element. Xalan does, however, so we will include this in all of our stylesheets. Since the XSLT stylesheet itself must be written as well-formed XML, some HTML tags are difficult to include. Instead of writing <hr>, you must write <hr/> in your stylesheet. When the output method is html, processors such as Xalan will remove the slash (/) character from the result tree, which produces HTML that typical web browsers expect.

The remainder of our stylesheet consists of two templates . Each matches some pattern in the XML input document and is responsible for producing output to the result tree. The first template is repeated as follows:

<xsl:template match="/">
  <html>
    <head>
      <title>Discussion Forum Home Page</title>
    </head>
    <body>
      <h1>Discussion Forum Home Page</h1>
      <h3>Please select a message board to view:</h3>
      <ul>
        <xsl:apply-templates select="discussionForumHome/messageBoard"/>
      </ul>
    </body>
  </html>
</xsl:template>

When the XSLT processor begins its transformation process, it looks in your stylesheet for a template that matches the "/" pattern. This pattern matches the source XML document that is being transformed. You may recall from Chapter 1, "Introduction " that DOM uses the Document interface to represent the document, which is what we are matching here. This is always the starting point for processing, so nearly every stylesheet you write will contain a template similar to this one. Since this is the first template to be instantiated, it is also where we create the framework for the resulting HTML document. The second template, which matches the "messageBoard" pattern, is currently ignored. This is because the processor is only looking at the root of the XML document, and the <messageBoard> element is nested beneath the <discussionForumHome> element.

Most of the tags in this template do not start with <xsl:, so they are simply copied to the result tree. In fact, the only dynamic content in this particular template is the following line, which tells the processor to continue the transformation process:

<xsl:apply-templates select="discussionForumHome/messageBoard"/>

Without this line, the transformation process would be complete because the "/" pattern was already located and a corresponding template was instantiated. The <xsl:apply-templates> element tells the XSLT processor to begin a new search for elements in the source XML document that match the "discussionForumHome/messageBoard" pattern and to instantiate an additional template that matches. As we will see shortly, the transformation process is recursive and must be driven by XSLT elements such as <xsl:apply-templates>. Simply including one or more <xsl:template> elements in a stylesheet does not mean that they will be instantiated.

In this example, the <xsl:apply-templates> element tells the XSLT processor to first select all <discussionForumHome> elements of the current node. The current node is "/" , or the top of the document, so it only selects the <discussionForumHome> element that occurs at the document's root level. If another <discussionForumHome> element is deeply nested within the XML document, it will not be selected by this pattern. Assuming that the processor locates the <discussionForumHome> element, it then searches for all of its <messageBoard> children.

NOTE: The select attribute in <xsl:apply-templates> does not have to be the same as the match attribute in <xsl:template>. Although the stylesheet presented in Example 2-2 could have specified <xsl:template match="discussionForumHome/messageBoard"> for the second template, this would limit the reusability of the template. Specifically, it could only be applied to <messageBoard> elements that occur as direct children of <discussionForumHome> elements. Since our template matches only "messageBoard", it can be reused for <messageBoard> elements that appear anywhere in the XML document.

For each <messageBoard> child, the processor looks for the template in your stylesheet that provides the best match. Since our stylesheet contains a template that matches the "messageBoard" pattern exactly, it is instantiated for each of the <messageBoard> elements. The job of this template is to produce a single HTML list item tag for each <messageBoard> element:

<xsl:template match="messageBoard">
  <li>
    <a href="viewForum?id={@id}">
      <xsl:value-of select="@name"/>
    </a>
  </li>
</xsl:template>

As you can see, the list item must be properly terminated; HTML-style standalone <li> tags are not allowed because they break the requirement that XSLT stylesheets be well-formed XML. Terminating the element with </li> also works with HTML, so this is the approach you must take. The hyperlink is a best guess at this point in the design process because the servlet has not been defined yet. Later, when we develop a servlet to actually process this web page, we will update the link to point to the correct servlet.

In the stylesheet, @ is used to select the values of attributes. Curly braces ({}) are known as an attribute value template and will be discussed in Chapter 3, "XSLT Part 2 -- Beyond the Basics". If you look back at Example 2-1, you will see that each message board has two attributes, id and name:

<messageBoard id="1" name="Java Programming"/>

When the stylesheet processor is executed and the result tree generated, we end up with the HTML shown in Example 2-3. The HTML is minimal at this point, which is exactly what you want. Fancy changes to the page layout can be added later; the important concept is that programmers can get started right away with the underlying application logic because of the clean separation between data and presentation that XML and XSLT provide.

Example 2-3. discussionForumHome.html

<html>
  <head>
    <title>Discussion Forum Home Page</title>
  </head>
  <body>
    <h1>Discussion Forum Home Page</h1>
    <h3>Please select a message board to view:</h3>
    <ul>
      <li>
        <a href="viewForum?id=1">Java Programming</a>
      </li>
      <li>
        <a href="viewForum?id=2">XML Programming</a>
      </li>
      <li>
        <a href="viewForum?id=3">XSLT Questions</a>
      </li>
    </ul>
  </body>
</html>

2.1.2. Trying It Out

To try things out, download the examples for this book and locate discussionForumHome.xml and discussionForumHome.xslt. They can be found in the chap1 directory. If you would rather type in the examples, you can use any text editor or a dedicated XML editor such as Altova's XML Spy (http://www.xmlspy.com). After downloading and unzipping the Xalan distribution from Apache, simply add xalan.jar and erces.jar to your CLASSPATH. The transformation can then be initiated with the following command:

java org.apache.xalan.xslt.Process -IN discussionForumHome.xml -XSL
discussionForumHome.xslt

This will apply the stylesheet, sending the resulting HTML content to standard output. Adding -OUTfilename to the command will cause Xalan to send the result tree directly to a file. To see the complete list of Xalan options, just type java org.apache.xalan.xslt.Process. For example, the -TT option allows you to see (trace) which templates are being called.

NOTE: Xalan's -IN and -XSL parameters accept URLs as arguments rather than as file names. A simple filename will work if the files are in the current working directory, but you may need to use a full URL syntax, such as file:///path/file.ext, when the file is located elsewhere.

In Chapter 5, "XSLT Processing with Java", we will show how to invoke Xalan and other XSLT processors from Java code, which is far more efficient because a separate Java Virtual Machine (JVM) does not have to be invoked for each transformation. Although it can take several seconds to start the JVM, the actual XSLT transformations will usually occur in milliseconds.

Another option is to find a web browser that supports XSLT, which allows you to edit your stylesheet and hit the "Reload" button to view the transformation.

Previous | Home | Next
1.5. Web Browser Support for XSLT | Book Index | 2.2. Transformation Process
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Transformation Process (Java and XSLT)

 Java and XSLTSearch this book

2.2. Transformation Process

Now that we have seen an example, let's back up and talk about some basics. In particular, it is important to understand the relationship between <xsl:template match=...> and <xsl:apply-templates select=...>. This should help to solidify your understanding of the previous example and lay the groundwork for more sophisticated processing. Although XSLT is a language, it is not intended to be a general-purpose programming language. Because of its specialized mission as a transformation language,[6] the design of XSLT works in the way that XML is structured, which is fundamentally a tree data structure.

[6] XSLT is declarative in nature, while mainstream programming languages tend to be more procedural.

2.2.1. XML Tree Data Structure

Every well-formed XML document forms a tree data structure. The document itself is always the root of the tree, and every element within the document has exactly one parent. Since the document itself is the root, it has no parent. As you learn XSLT, it can be helpful to draw pictures of your XML data that show its tree structure. Figure 2-2 illustrates the tree structure for discussionForumHome.xml.

Figure 2-2. Tree structure for discussionForumHome.xml

The document itself is the root of the tree and may contain processing instructions, the document root element, and even comments. XSLT has the ability to select any of these items, although you will probably want to select elements and attributes when transforming to HTML. As mentioned earlier, the "/" pattern matches the document itself, which is the root node of the entire tree.

A tree data structure is fundamentally recursive because it consists of leaf nodes and smaller trees. Each of these smaller trees, in turn, also consist of leaf nodes and still smaller trees. Algorithms that deal with tree structures can almost always be expressed recursively, and XSLT is no exception. The processing model adopted by XSLT is explicitly designed to take advantage of the recursive nature of every well-formed XML document. This means that most stylesheets can be broken down into highly modular, easily understandable pieces, each of which processes a subset of the overall tree (i.e., a subtree).

Two important concepts in XSLT are the current node and current node list. The current node is comparable to the current working directory on a file system. The <xsl:value-of select="."/> element is similar to printing the name of the current working directory. The current node list is similar to the list of subdirectories. The key difference is that in XSLT, the current node appears in your source XML document. The current node list is a collection of nodes. As processing proceeds, the current node and current node list are constantly changing as you traverse the source tree, looking for patterns in the data.

2.2.2. Recursive Processing with Templates

Most transformation in XSLT is driven by two elements: <xsl:template> and <xsl:apply-templates> . In XSLT lingo, a node can represent anything that appears within your XML data. Nodes are typically elements such as <message> or element attributes such as id="123". Nodes can also be XML processing instructions, text, or even comments. XSLT transformation begins with a current node list that contains a single entry: the root node. This is the XML document and is represented by the "/" pattern. Processing proceeds as follows:

  • For each node "X" in the current node list, the processor searches for all <xsl:template match="pattern"> elements in your stylesheet that potentially match that node. From this list of templates, the one with the best match[7] is selected.

    [7] See section 5.5 of the XSLT specification for conflict-resolution rules.

  • The selected <xsl:template match="pattern"> is instantiated using node "X" as its current node. This template typically copies data from the source document to the result tree or produces brand new content in combination with data from the source.

  • If the template contains <xsl:apply-templates select="newPattern"/>, a new current node list is created and the process repeats recursively. The select pattern is relative to node "X", rather than the document root.

As the XSLT transformation process continues, the current node and current node list are constantly changing. This is a good thing, since you do not want to constantly search for patterns beginning from the document root element. You are not limited to traversing down the tree, however; you can iterate over portions of the XML data many times or navigate back up through the document tree structure. This gives XSLT a huge advantage over CSS because CSS is limited to displaying the XML in the order in which it appears in the document.

Comparing <xsl:template> to <xsl:apply-templates>

One way to understand the difference between <xsl:template> and <xsl:apply-templates> is to think about the difference between a Java method and the code that invokes the method. For example, a method in Java is declared as follows:

public void printMessageBoard(MessageBoard board) {
    // print information about the message board
}

In XSLT, the template plays a similar role:

<xsl:template match="messageBoard">
  <!-- print information about the message board
</xsl:template>

In order to invoke the Java method, use the following Java code:

someObject.printMessageBoard(currentBoard);

And in XSLT, use:

<xsl:apply-templates select="..."/>

to instantiate the template using the current <messageBoard> node.

While this is a good comparison to help illustrate the difference between <xsl:template> and <xsl:apply-templates>, it is important to remember that the XSLT model is not really a method call. Instead, <xsl:apply-templates> instructs the processor to scan through the XML document again, looking for nodes that match a pattern. If matching nodes are found, the best matching template is instantiated.

In the next chapter, we will see that XSLT also has <xsl:call-template>, which works similarly to a Java method call.

Let's suppose that your source document contains the following XML:

<school>
  <name>SIUC</name>
  <city>Carbondale</city>
  <state>Illinois</state>
</school>

The following template could be used to match the <school> element and output its contents:

<xsl:template match="school">
  <b><xsl:value-of select="name"/> is located in
  <xsl:value-of select="city"/>, <xsl:value-of select="state"/>.</b>
</xsl:template>

The result will be something like:

<b>SIUC is located in Carbondale, Illinois.</b>

As you can see, elements that do not start with xsl: are simply copied to the result tree, as is plain text such as "is located in."[8] We do not show this here, but if you try the example you will see that whitespace characters (spaces, tabs, and linefeeds) are also copied to the result tree. When the destination is HTML, it is usually safe to ignore this issue because the browser will collapse that whitespace. If you view the actual source code of the generated HTML, it can look pretty ugly. An alternative to simply including "is located in" is to use:

[8] Technically, elements that do not belong to the XSLT namespace are simply copied to the result tree; the namespace prefix might not be xsl:.

<xsl:text> is located in </xsl:text>.

This provides explicit control over how whitespace and linefeeds are treated.

<xsl:value-of> copies the value of something in the XML source tree to the result tree. In this case, the current node is <school>, so <xsl:value-of select="name"/> selects the text content of the <name> element contained within <school>. This is the simplest usage of XPath, which will be introduced shortly. XPath is not limited to the current node, so it can also be used to locate elements in other parts of the source document. It can even select attributes, processing instructions, or anything else that can occur in XML.

2.2.3. Built-in Template Rules

All XSLT processors must include four built-in template rules that have lower precedence than any other rules, so they can be overridden by simply writing a new template rule that matches the same pattern. The best way to think about built-in rules is to assume they are always in the background, ready to be applied if no other rule is found that matches a node.

The first rule allows recursive processing to continue in case an explicit rule does not match the current node or the root node:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

This template matches all elements (*) and the root node (/), i.e., the document itself. It will not match processing instructions, comments, attributes, or text. The <xsl:apply-templates/> causes all children that are not attribute nodes or processing instruction nodes to be processed.

The second built-in rule is identical to the first, except it applies to each mode used in the stylesheet:

<xsl:template match="*|/" mode="m">
  <xsl:apply-templates mode="m"/>
</xsl:template>

Template modes are discussed in the next chapter, so we will not go into details here. The third built-in rule simply copies all text and attribute nodes to the result tree:

<xsl:template match="text( )|@*">
  <xsl:value-of select="."/>
</xsl:template>

And finally, the built-in rule for processing instructions and comments does nothing. This is why comments and processing instructions in the input XML data do not automatically show up in the result tree:

<xsl:template match="processing-instruction()|comment( )"/>

2.2.4. A Skeleton Stylesheet

As your XML documents get more complex, you will most likely want to break up your stylesheets into several templates. The starting point is a template that matches the "/" pattern:

<xsl:template match="/">
...content
</xsl:template>

This template matches the document itself and is usually where you output the basic <html>, <head>, and <body> elements. Somewhere within this template, you must tell the processor to continue searching for additional patterns, thus beginning the recursive transformation process. In a typical stylesheet, <xsl:apply-templates> is used for this purpose, instructing the processor to search for additional content in the XML data.

It should be stressed that this is not the only way to write a stylesheet, but it is a very natural way to handle the recursive nature of XML. Example 2-4 contains a skeleton XSLT stylesheet that you can use as a starting point for most of your projects.

Example 2-4. Skeleton stylesheet

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <!--****************************************************************
      ** "/" template matches the document and is the starting point
      *************************************************************-->
  <xsl:template match="/">
    <html>
      <head>
        <title>[title goes here]</title>
      </head>
      <body>
         <xsl:apply-templates select="[some XPath expression]"/>
      </body>
    </html>
  </xsl:template>
  <!--****************************************************************
      ** "[???]" template
      *************************************************************-->
  <xsl:template match="???">
    [continue the process...]
    <xsl:apply-templates select="[another XPath expression]"/>
    [you can also include more content here...or even include
     multiple apply-templates...]
  </xsl:template>
</xsl:stylesheet>

Deciding how to modularize the stylesheet is a subjective process. One suggestion is to look for moderately sized chunks of XML data repeated numerous times throughout a document. For example, a <customer> element may contain a name, address, and phone number. Creating a template that matches "customer" is probably a good idea. You may even want to create another template for the <name> element, particularly if the name is broken down into subelements, or if the name is reused in other contexts such as <employee> and <manager>.

When you need to produce HTML tables or unordered lists in the result tree, two templates (instead of one) can make the job very easy. The first template will produce the <table> or <ul> element, and the second will produce each table row or list item. The following fragment illustrates this basic pattern:

<!-- the outer template produces the unordered list -->
<!-- (note: plural 'customers') -->
<xsl:template match="customers">
  <ul>
    <xsl:apply-templates select="customer"/>
  </ul>
</xsl:template>

<!-- the inner template is repeated for each customer -->
<xsl:template match="customer">
  <li><xsl:value-of select="name"/></li>
</xsl:template>
Previous | Home | Next
2. XSLT Part 1 -- The Basics | Book Index | 2.3. Another XSLT Example, Using XHTML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Another XSLT Example, Using XHTML (Java and XSLT)

 Java and XSLTSearch this book

2.3. Another XSLT Example, Using XHTML

Example 2-5 contains XML data from an imaginary scheduling program. A schedule has an owner followed by a list of appointments. Each appointment has a date, start time, end time, subject, location, and optional notes. Needless to say, a true scheduling application probably has a lot more data, such as repeating appointments, alarms, categories, and many other bells and whistles. Assuming that the scheduler stores its data in XML files, we can easily add features later by writing a stylesheet to convert the existing XML files to some new format.

Example 2-5. schedule.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="schedule.xslt"?>
<schedule>
  <owner>
    <name>
      <first>Eric</first>
      <last>Burke</last>
    </name>
  </owner>
  <appointment>
    <when>
      <date month="03" day="15" year="2001"/>
      <startTime hour="09" minute="30"/>
      <endTime hour="10" minute="30"/>
    </when>
    <subject>Interview potential new hire</subject>
    <location>Rm 103</location>
    <note>Ask Bob for an updated resume.</note>
  </appointment>
  <appointment>
    <when>
      <date month="03" day="15" year="2001"/>
      <startTime hour="15" minute="30"/>
      <endTime hour="16" minute="30"/>
    </when>
    <subject>Dr. Appointment</subject>
    <location>1532 Main Street</location>
  </appointment>
  <appointment>
    <when>
      <date month="03" day="16" year="2001"/>
      <startTime hour="11" minute="30"/>
      <endTime hour="12" minute="30"/>
    </when>
    <subject>Lunch w/Boss</subject>
    <location>Pizza Place on First Capitol Drive</location>
  </appointment>
</schedule>

As you can see, the XML document uses both attributes (month="03") and child elements to represent its data. XSLT has the ability to search for and transform both types of data, as well as comments, processing instructions, and text. In our current document, the appointments are stored in chronological order. Later, we will see how to change the sort order using <xsl:sort>.

Unlike the earlier example, the second line of Example 2-5 contains a reference to the XSLT stylesheet:

<?xml-stylesheet type="text/xsl" href="schedule.xslt"?>

This processing instruction is entirely optional. When viewing the XML document in a web browser that supports XSLT, this is the stylesheet that is used. If you apply the stylesheet from the command line or from a server-side process, however, you normally specify both the XML document and the XSLT document as parameters to the processor. Because of this capability, the processing instruction shown does not force that particular stylesheet to be used. From a development perspective, including this line quickly displays your work because you simply load the XML document into a compatible web browser, and the stylesheet is loaded automatically.

NOTE: In this book, the xml-stylesheet processing instruction uses type="text/xsl". However, some processors use type="text/xml", which does not work with Microsoft Internet Explorer. The XSLT specification contains one example, which uses "text/xml".

Figure 2-3 shows the XHTML output from an XSLT transformation of schedule.xml. As you can see, the stylesheet is capable of producing content that does not appear in the original XML data, such as "Subject:". It can also selectively copy element content and attribute values from the XML source to the result tree; nothing requires every piece of data to be copied.

Figure 2-3. XHTML output

The XSLT stylesheet that produces this output is shown in Example 2-6. As mentioned previously, XSLT stylesheets must be well-formed XML documents. Once again, we use .xslt as the filename extension, but .xsl is also common. This stylesheet is based on the skeleton document presented in Example 2-4. However, it produces XHTML instead of HTML.

Example 2-6. schedule.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml"
      doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
  <!--****************************************************************
      ** "/" template
      *************************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Schedule</title>
      </head>
      <body>
        <h2 align="center">
          <xsl:value-of select="schedule/owner/name/first"/>
          <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
          <xsl:value-of select="schedule/owner/name/last"/>'s Schedule</h2>
        <xsl:apply-templates select="schedule/appointment"/>
      </body>
    </html>
  </xsl:template>
  <!--***************************************************************
      ** "appointment" template
      ************************************************************-->
  <xsl:template match="appointment">
    <hr/>
    <h3>Appointment</h3>
    <xsl:apply-templates select="when"/>
    <table>
      <tr>
        <td>Subject:</td>
        <td>
          <xsl:value-of select="subject"/>
        </td>
      </tr>
      <tr>
        <td>Location:</td>
        <td>
          <xsl:value-of select="location"/>
        </td>
      </tr>
      <tr>
        <td>Note:</td>
        <td>
          <xsl:value-of select="note"/>
        </td>
      </tr>
    </table>
  </xsl:template>
  <!--****************************************************************
       ** "when" template
       *************************************************************-->
  <xsl:template match="when">
    <p>
      <xsl:value-of select="date/@month"/>
      <xsl:text>/</xsl:text>
      <xsl:value-of select="date/@day"/>
      <xsl:text>/</xsl:text>
      <xsl:value-of select="date/@year"/>
      from
      <xsl:value-of select="startTime/@hour"/>
      <xsl:text>:</xsl:text>
      <xsl:value-of select="startTime/@minute"/>
      until
      <xsl:value-of select="endTime/@hour"/>
      <xsl:text>:</xsl:text>
      <xsl:value-of select="endTime/@minute"/>
    </p>
  </xsl:template>
</xsl:stylesheet>

The first part of this stylesheet should look familiar. The first four lines are typical of just about any stylesheet you will write. Next, the output method is specified as xml because this stylesheet is producing XHTML instead of HTML:

<xsl:output method="xml"
    doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>

The <xsl:output> element produces the following XHTML content:

<?xml version="1.0" encoding="UTF-16"?>
<!DOCTYPE html PUBLIC
  "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Moving on, the first template in the stylesheet matches "/" and outputs the skeleton for the XHTML document. Another requirement for XHTML is the namespace attribute on the <html> element:

<html xmlns="http://www.w3.org/1999/xhtml">

The remainder of schedule.xslt consists of additional templates, each of which matches a particular pattern in the XML input.

NOTE: Because of its XML syntax, XSLT stylesheets can be hard to read. If you prefix each template with a distinctive comment block as shown in Example 2-6, it is fairly easy to see the overall structure of the stylesheet. Without consistent indentation and comments, the markup tends to run together, making the stylesheet much harder to understand and maintain.

The <xsl:text> element is used to insert additional text into the result tree. Although plain text is allowed in XSLT stylesheets, the <xsl:text> element allows more explicit control over whitespace handling. As shown here, a nonbreaking space is inserted into the result tree:

<xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>

Unfortunately, the following syntax does not work:

<!-- does not work... -->
<xsl:text>&nbsp;</xsl:text>

This is because &nbsp; is not one of the five built-in entities supported by XML. Since XSLT stylesheets are always well-formed XML, the parser complains when &nbsp; is found in the stylesheet. Replacing the first ampersand character with &amp; allows the XML parser to read the stylesheet into memory. The XML parser interprets this entity and sends the following markup to the XSLT processor:

<!-- this is what the XSLT processor sees, after the XML parser
     interprets the &amp; entity -->
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>

The second piece of this solution is the disable-output-escaping="yes" attribute. Without this attribute the XSLT processor may attempt to escape the nonbreaking space by converting it into an actual character. This causes many web browsers to display question marks because they cannot interpret the character. Disabling output escaping tells the XSLT processor to pass &nbsp; to the result tree. Web browsers then interpret and display the nonbreaking space properly.

In the final template shown in Example 2-6, you may notice the element <xsl:value-of select="date/@month"/>. The @ character represents an attribute, so in this case the stylesheet is outputting the value of the month attribute on the date element. For this element:

<date month="03" day="15" year="2001"/>,

the value "03" is copied to the result tree.

Previous | Home | Next
2.2. Transformation Process | Book Index | 2.4. XPath Basics
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Basics (Java and XSLT)

 Java and XSLTSearch this book

2.4. XPath Basics

XPath is another recommendation from the W3C and is designed for use by XSLT and another technology called XPointer. The primary goal of XPath is to define a mechanism for addressing portions of an XML document, which means it is used for locating element nodes, attribute nodes, text nodes, and anything else that can occur in an XML document. XPath treats these nodes as part of a tree structure rather than dealing with XML as a text string. XSLT also relies on the tree structure that XPath defines. In addition to addressing, XPath contains a set of functions to format text, convert to and from numbers, and deal with booleans.

Unlike XSLT, XPath itself is not expressed using XML syntax. A simplified syntax makes sense when you consider that XPath is most commonly used inside of attribute values within other XML documents. XPath includes both a verbose syntax and a set of abbreviations, which end up looking a lot like path names on a file system or web site.

2.4.1. How XSLT Uses XPath

XSLT uses XPath in three basic ways:

  • To select and match patterns in the original XML data. Using XPath in this manner is the focus of this chapter. You see this most often in <xsl:template match="pattern"> and <xsl:apply-templates select="node-set-expression"/>. In either case, XPath syntax is used to locate various types of nodes.

  • To support conditional processing. We will see the exact syntax of <xsl:if> and <xsl:choose> in the next chapter, both of which rely on XPath's ability to represent boolean values of true and false.

  • To generate text. A number of string formatting instructions are provided, giving you the ability to concatenate strings, manipulate substrings, and convert from other data types to strings. Again, this will be covered in the next chapter.

2.4.2. Axes

Whenever XSLT uses XPath, something in the XML data is considered to be the current context node. XPath defines seven different types of nodes, each representing a different part of the XML data. These are the document root, elements, text, attributes, processing instructions, comments, and nodes representing namespaces. An axis represents a relationship to the current context node, which may be any one of the preceding seven items.

A few examples should clear things up. One axis is child, representing all immediate children of the context node. From our earlier schedule.xml example, the child axis of <name> includes the <first> and <last> elements. Another axis is parent, which represents the immediate parent of the context node. In many cases the axis is empty. For example, the document root node has no parent axis. Figure 2-4 illustrates some of the other axes.

Figure 2-4. XPath axes

As you can see, the second <department> element is the context node. The diagram illustrates how some of the more common axes relate to this node. Although the names are singular, in most cases the axes represent node sets rather than individual nodes. The code:

<xsl:apply-templates select="child::team"/>

selects all <team> children, not just the first one. Table 2-1 lists the available axes in alphabetical order, along with a brief description of each.

Table 2-1. Axes summary

Axis name

Description

ancestor

The parent of the context node, its parent, and so on until the root node is reached. The ancestor of the root is an empty node set.

ancestor-or-self

The same as ancestor, with the addition of the context node. The root node is always included.

attribute

All attributes of the context node.

child

All immediate children of the context node. Attributes and namespace nodes are not included.

descendant

All children, grandchildren, and so forth. Attribute and namespace nodes are not considered descendants of element nodes.

descendant-or-self

Same as descendant, with the addition of the context node.

following

All elements in the document that occur after the context node. Descendants of the context node are not included.

following-sibling

All following nodes in the document that have the same parent as the context node.

namespace

The namespace nodes of the context node.

parent

The immediate parent of the context node, if a parent exists.

preceding

All nodes in the document that occur before the context node, except for ancestors, attribute nodes, and namespace nodes.

preceding-sibling

All nodes in the document that occur before the context node and have the same parent. This axis is empty if the context node is an attribute node or a namespace node.

self

The context node itself.

2.4.3. Location Steps

As you may have guessed, an axis alone is only a piece of the puzzle. A location step is a more complex construct used by XPath and XSLT to select a node set from the XML data. Location steps have the following syntax:

axis::node-test[predicate-1]...[predicate-n]

The axis and node-test are separated by double colons and are followed by zero or more predicates. As mentioned, the job of the axis is to specify the relationship between the context node and the node-test. The node-test allows you to specify the type of node that will be selected, and the predicates filter the resulting node set.

Once again, discussion of XSLT and XPath tends to sound overly technical until you see a few basic examples. Let's start with a basic fragment of XML:

<message>
  <header> <!-- the context node -->
    <subject>Hello, World</subject>
    <date mm="03" dd="01" yy="2002"/>
    <sender>pres@whitehouse.gov</sender>
    <recipient>burke_e@ociweb.com</recipient>
    <recipient>burke_e@yahoo.com</recipient>
    <recipient>aidan@burke.com</recipient>
  </header>
  <body>
    ...
  </body>
</message>

If the <header> is the context node, then child::subject will select the <subject> node, child::recipient will select the set of all <recipient> nodes, and child::* will select all children of <header>. The asterisk (*) character is a wildcard that represents all nodes of the principal node type. Each axis has a principal node type, which is always element unless the axis is attribute or namespace. If <date> is the context node, then attribute::yy will select the yy attribute, and attribute::* will select all attributes of the <date> element.

Without any predicates, a location step can result in zero or more nodes. Adding a predicate simply filters the resulting node set, generally reducing the size of the resulting node set. Adding additional predicates applies additional filters. For example, child::recipient[position( )=1] will initially select all <recipient> elements from the previous example then filter (reduce) the list down to the first one: burke_e@ociweb.com. Positions start at 1, rather than 0. As Example 2-8 will show, predicates can contain any XPath expression and can become quite sophisticated.

2.4.4. Location Paths

Location paths consist of one or more location steps, separated by slash (/) characters. An absolute location path begins with the slash (/) character and is relative to the document root. All other types of location paths are relative to the context node. Paths are evaluated from left to right, just like a path in a file system or a web site. The XML shown in Example 2-7 is a portion of a larger file containing basic information about U.S. presidents. This is used to demonstrate a few more XSLT and XPath examples.

Example 2-7. presidents.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="xpathExamples.xslt"?>
<presidents>
  <president>
    <term from="1789" to="1797"/>
    <name>
      <first>George</first>
      <last>Washington</last>
    </name>
    <party>Federalist</party>
    <vicePresident>
      <name>
        <first>John</first>
        <last>Adams</last>
      </name>
    </vicePresident>
  </president>
  <president>
    <term from="1797" to="1801"/>
    <name>
      <first>John</first>
      <last>Adams</last>
    </name>
    <party>Federalist</party>
    <vicePresident>
      <name>
        <first>Thomas</first>
        <last>Jefferson</last>
      </name>
    </vicePresident>
  </president>
    /**
    * remaining presidents omitted
    */

The complete file is too long to list here but is included with the downloadable files for this book. The <vicePresident> element can occur many times or not at all because some presidents did not have vice presidents. Names can also contain optional <middle> elements. Using this XML data, the XSLT stylesheet in Example 2-8 shows several location paths.

Example 2-8. Location paths

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" />
  <xsl:template match="/">
    <html>
    <body>
    <h1>XPath Examples</h1>

    The third president was:
    <ul>
      <xsl:apply-templates select="presidents/president[position( ) = 3]/name"/>
    </ul>

    Presidents without vice presidents were:
    <ul>
      <xsl:apply-templates
          select="presidents/president[count(vicePresident) = 0]/name"/>
    </ul>

    Presidents elected before 1800 were:
    <ul>
      <xsl:apply-templates
          select="presidents/president[term/@from &lt; 1800]/name"/>
    </ul>

    Presidents with more than one vice president were:
    <ul>
      <xsl:apply-templates
          select="descendant::president[count(vicePresident) > 1]/name"/>
    </ul>

    Presidents named John were:
    <ul>
      <xsl:apply-templates
          select="presidents/president/name[child::first='John']"/>
    </ul>

    Presidents elected between 1800 and 1850 were:
    <ul>
      <xsl:apply-templates
           select="presidents/president[(term/@from > 1800) and
                   (term/@from &lt; 1850)]/name"/>
    </ul>

    </body>
    </html>
  </xsl:template>

  <xsl:template match="name">
    <li>
      <xsl:value-of select="first"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="middle"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="last"/>
    </li>
  </xsl:template>
</xsl:stylesheet>

In the first <xsl:apply-templates> element, the location path is as follows:

presidents/president[position( ) = 3]/name

This path consists of three location steps separated by slash (/) characters, but the final step is what we want to select. This path is read from left to right, so it first selects the <presidents> children of the current context. The next step is relative to the <presidents> context and selects all <president> children. It then filters the list according to the predicate. The third <president> element is now the context, and its <name> children are selected. Since each president has only one <name>, the template that matches "name" is instantiated only once.

This location path shows how to perform basic numeric comparisons:

presidents/president[term/@from &lt; 1800]/name

Since the less-than (<) character cannot appear in an XML attribute value, the &lt; entity must be substituted. In this particular example, we use the @ abbreviated syntax to represent the attribute axis.

2.4.5. Abbreviated Syntax

Using descendant::, child::, parent::, and other axes is very verbose, requiring a lot of typing. Fortunately, XPath supports an abbreviated syntax for many of these axes that requires a lot less effort. The abbreviated syntax has the added advantage in that it looks like you are navigating the file system, so it tends to be somewhat more intuitive. Table 2-2 compares the abbreviated syntax to the verbose syntax. The abbreviated syntax is almost always used and will be used throughout the remainder of this book.

Table 2-2. Abbreviated syntax

Abbreviation

Axis

//

descendant

.

self

..

parent

@

attribute

child

In the last row, the abbreviation for the child axis is blank, indicating that child:: is an implicit part of a location step. This means that vicePresident/name is equivalent to child::vicePresident/child::name. Additional explanations follow:

  • vicePresident selects the vicePresident children of the context node.

  • vicePresident/name selects all name children of vicePresident children of the context node.

  • //name selects all name descendants of the context node.

  • . selects the context node.

  • ../term/@from selects the from attribute of term children of the context node's parent.

Previous | Home | Next
2.3. Another XSLT Example, Using XHTML | Book Index | 2.5. Looping and Sorting
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Looping and Sorting (Java and XSLT)

 Java and XSLTSearch this book

2.5. Looping and Sorting

As shown throughout this chapter, you can use <xsl:apply-templates ...> to search for patterns in an XML document. This type of processing is sometimes referred to as a " data driven" approach because the data of the XML file drives the selection process. Another style of XSLT programming is called "template driven," which means that the template's code tends to drive the selection process.

2.5.1. Looping with <xsl:for-each>

Sometimes it is convenient to explicitly drive the selection process with an <xsl:for-each> element, which is reminiscent of traditional programming techniques. In this approach, you explicitly loop over a collection of nodes without instantiating a separate template as <xsl:apply-templates> does. The syntax for <xsl:for-each> is as follows:

<xsl:for-each select="president">
  ...content for each president element
</xsl:for-each>

The select attribute can contain any XPath location path, and the loop will iterate over each element in the resulting node set. In this example, the context is <president> for all content within the loop. Nested loops are possible and could be used to loop over the list of <vicePresident> elements.

2.5.2. Sorting

Sorting can be applied in either a data-driven or template-driven approach. In either case, <xsl:sort> is added as a child element to something else. By adding several consecutive <xsl:sort> elements, you can accomplish multifield sorting. Each sort can be in ascending or descending order, and the data type for sorting is either "number" or "text". The sort order defaults to ascending. Some examples of <xsl:sort> include:

<xsl:sort select="first"/>
<xsl:sort select="last" order="descending"/>
<xsl:sort select="term/@from" order="descending" data-type="number"/>
<xsl:sort select="name/first" data-type="text" case-order="upper-first"/>

In the last line, the case-order attribute specifies that uppercase letters should be alphabetized before lowercase letters. The other accepted value for this attribute is lower-first. According to the specification, the default behavior is "language dependent."

2.5.3. Looping and Sorting Examples

The easiest way to learn about looping and sorting is to play around with a lot of small examples. The code in Example 2-9 applies numerous different looping and sorting strategies to our list of presidents. Comments in the code indicate what is happening at each step.

Example 2-9. Looping and sorting

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:template match="/">
    <html>
      <body>
        <h1>Sorting Examples</h1>
        <xsl:apply-templates select="presidents"/>
      </body>
    </html>
  </xsl:template>
  <!--********************************************************************
      ** presidents template
      *****************************************************************-->
  <xsl:template match="presidents">
    <!--*****************************************************************
        ** Sorting using xsl:for-each
        **************************************************************-->
    <h2>All presidents sorted by first name using xsl:for-each</h2>
    <xsl:for-each select="president">
      <xsl:sort select="name/first"/>
      <xsl:apply-templates select="name"/>
    </xsl:for-each>
    <!--*****************************************************************
        ** Sorting using xsl:apply-templates
        **************************************************************-->
    <h2>All presidents sorted by first name using xsl:apply-templates</h2>
    <xsl:apply-templates select="president/name">
      <xsl:sort select="first"/>
    </xsl:apply-templates>
    <h2>All presidents sorted by date using xsl:apply-templates</h2>
    <xsl:apply-templates select="president/name">
      <xsl:sort select="../term/@from" data-type="number" order="descending"/>
    </xsl:apply-templates>
    <!--*****************************************************************
        ** Multi-field sorting
        **************************************************************-->
    <h2>Multi-field sorting example</h2>
    <xsl:apply-templates select="president/name">
      <xsl:sort select="last"/>
      <xsl:sort select="first" order="descending"/>
    </xsl:apply-templates>
    <!--*****************************************************************
        ** Nested xsl:for-each loops
        **************************************************************-->
    <h2>All presidents and vice presidents using xsl:for-each</h2>
    <ul>
      <xsl:for-each select="president">
        <xsl:sort select="name/first" order="descending"/>
        <li>
          <xsl:apply-templates select="name"/>
        </li>
        <ul>
          <xsl:for-each select="vicePresident">
            <xsl:sort select="name/first"/>
            <li>
              <xsl:apply-templates select="name"/>
            </li>
          </xsl:for-each>
        </ul>
      </xsl:for-each>
    </ul>
    <!--*****************************************************************
        ** Same as previous, only using xsl:apply-templates
        **************************************************************-->
    <h2>All presidents and vice presidents using xsl:apply-templates</h2>
    <ul>
      <xsl:apply-templates select="president">
        <xsl:sort select="name/first" order="descending"/>
      </xsl:apply-templates>
    </ul>
  </xsl:template>
  <!--*****************************************************************
      ** 'president' template, outputs the president's name and vice
      **             president's name.
      **************************************************************-->
  <xsl:template match="president">
    <li>
      <xsl:apply-templates select="name"/>
    </li>
    <ul>
      <xsl:for-each select="vicePresident">
        <xsl:sort select="name/first"/>
        <li>
          <xsl:apply-templates select="name"/>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>
  <!--*****************************************************************
      ** name template, outputs first, middle, and last name
      **************************************************************-->
  <xsl:template match="name">
    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="first"/>
    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="middle"/>
    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="last"/>
    <br/>
  </xsl:template>
</xsl:stylesheet>

Notice that when applying a sort to <xsl:apply-templates>, that element can no longer be an empty element. Instead, one or more <xsl:sort> elements are added as children of <xsl:apply-templates>. You should also note that sorting cannot occur in the <xsl:template match="name"> element. The reason for this is simple: at the <xsl:apply-templates> end, you have a list of nodes to sort. By the time the processing reaches <xsl:template match="name">, the search has narrowed down to a single <name>, so there is no node list left to sort.

Previous | Home | Next
2.4. XPath Basics | Book Index | 2.6. Outputting Dynamic Attributes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Outputting Dynamic Attributes (Java and XSLT)

 Java and XSLTSearch this book

2.6. Outputting Dynamic Attributes

Let's assume we have an XML document that lists books in a personal library, and we want to create an HTML document with links to these books on Amazon.com. In order to generate the hyperlink, the href attribute must contain the ISBN of the book, which can be found in our original XML data. An example of the URL we would like to generate is as follows:

<a href="http://www.amazon.com/exec/obidos/ASIN/0596000162">Java and XML</a>

One thought is to include <xsl:value-of select="isbn"/> directly inside of the attribute. However, XML does not allow you to insert the less-than (<) character inside of an attribute value:

<!-- won't work... -->
<a href="<xsl:value-of select="isbn"/>">Java and XML</a>

We also need to consider that the attribute value is dynamic rather than static. XSLT does not automatically recognize content of the href="..." attribute as an XPath expression, since the <a> tag is not part of XSLT. There are two possible solutions to this problem.

2.6.1. <xsl:attribute>

In the first approach, <xsl:attribute> is used to add one or more attributes to elements. In the following template, an href attribute is added to an <a> element:

<xsl:template match="book">
  <li>
    <a>  <!-- the href attribute is generated below -->
      <xsl:attribute name="href">
        <xsl:text>http://www.amazon.com/exec/obidos/ASIN/</xsl:text>
        <xsl:value-of select="@isbn"/>
      </xsl:attribute>
      <xsl:value-of select="title"/>
    </a>
  </li>
</xsl:template>

The <li> tag is used because this is part of a larger stylesheet that presents a bulleted list of links to each book. The <a> tag, as you can see, is missing its href attribute. The <xsl:attribute> element adds the missing href. Any child content of <xsl:attribute> is added to the attribute value. Because we do not want to introduce any unnecessary whitespace, <xsl:text> is used. Finally, <xsl:value-of> is used to select the isbn attribute.

2.6.2. Attribute Value Templates

Using <xsl:attribute> can be quite complex for a simple attribute value. Fortunately, XSLT provides a much simpler syntax called attribute value templates (AVT). The next example uses an AVT to achieve the identical result:

<xsl:template match="book">
  <li>
    <a href="http://www.amazon.com/exec/obidos/ASIN/{@isbn}">
      <xsl:value-of select="title"/>
    </a>
  </li>
</xsl:template>

The curly braces ({}) inside of the attribute value cause the magic to happen. Normally, when the stylesheet encounters attribute values for HTML elements, it treats them as static text. The braces tell the processor to treat a portion of the attribute dynamically.

In the case of {@isbn}, the contents of the curly braces is treated exactly as <xsl:value-of select="@isbn"/> in the previous approach. This is obviously much simpler. The text inside of the {} characters can be any location path, so you are not limited to selecting attributes. For example, to select the title of the book, simply change the value to {title}.

So where do you use AVTs and where don't you? Well, whenever you need to treat an attribute value as an XPath expression rather than static text, you may need to use an AVT. But for standard XSLT elements, such as <xsl:template match="pattern">, you don't need to use the AVT syntax. For nonXSLT elements, such as any HTML tag, AVT syntax is required.

2.6.3. <xsl:attribute-set>

There are times when you may want to define a group of attributes that can be reused. For this task, XSLT provides the <xsl:attribute-set> element. Using this element allows you to define a named group of attributes that can be referenced from other points in a stylesheet. The following stylesheet fragment shows how to define an attribute set:

<xsl:attribute-set name="body-style">
  <xsl:attribute name="bgcolor">yellow</xsl:attribute>
  <xsl:attribute name="text">green</xsl:attribute>
  <xsl:attribute name="link">navy</xsl:attribute>
  <xsl:attribute name="vlink">red</xsl:attribute>
</xsl:attribute-set>

This is a " top level element," which means that it can occur as a direct child of the <xsl:stylesheet> element. The definition of an attribute set does not have to come before templates that use it. The attribute set can be referenced from another <xsl:attribute-set>, from <xsl:element>, or from <xsl:copy> elements. We will talk about <xsl:copy> in the next chapter, but here is how <xsl:element> is used:

<xsl:template match="/">
  <html>
    <head>
      <title>Demo of attribute-set</title>
    </head>
    <xsl:element name="body" use-attribute-sets="body-style">
      <h1>Books in my library...</h1>
      <ul>
        <xsl:apply-templates select="library/book"/>
      </ul>
    </xsl:element>
  </html>
</xsl:template>

As you can probably guess, the code shown here will output an HTML body tag that looks like this:

<body bgcolor="yellow" text="green" link="navy" vlink="red">
...body content
</body>

In this particular example, the <xsl:attribute-set> was used only once, so its value is minimal. It is possible for one stylesheet to include another, however, as we will see in the next chapter. In this way, you can define the <xsl:attribute-set> in a fragment of XSLT included in many other stylesheets. Changes to the shared fragment are immediately reflected in all of your other stylesheets.

Previous | Home | Next
2.5. Looping and Sorting | Book Index | 3. XSLT Part 2 -- Beyond the Basics
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Part 2 -- Beyond the Basics (Java and XSLT)

 Java and XSLTSearch this book

Chapter 3. XSLT Part 2 -- Beyond the Basics

Contents:

Conditional Processing
Parameters and Variables
Combining Multiple Stylesheets
Formatting Text and Numbers
Schema Evolution
Ant Documentation Stylesheet

As you may have guessed, this chapter is a continuation of the material presented in the previous chapter. The basic syntax of XSLT should make sense by now. If not, it is probably a good idea to sit down and write a few stylesheets to gain some basic familiarity with the technology. What we have seen so far covers the basic mechanics of XSLT but does not take full advantage of the programming capabilities this language has to offer. In particular, this chapter will show how to write more reusable, modular code through features such as named templates, parameters, and variables.

The chapter concludes with a real-world example that uses XSLT to produce HTML documentation for Ant build files. Ant is a Java build tool that uses XML files instead of Makefiles to drive the compilation process. Since XML is used, XSLT is a natural choice for producing documentation about the build process.

3.1. Conditional Processing

In the previous chapter, we saw a template that output the name of a president or vice president. Its basic job was to display the first name, middle name, and last name. A nonbreaking space was printed between each piece of data so the fields did not run into each other. What we did not see was that many presidents do not have middle names, so our template ended up printing the first name, followed by two spaces, followed by the last name. To fix this, we need to check for the existence of a middle name before simply outputting its content and a space. This requires conditional logic, a feature found in just about every programming language in existence.

XSLT provides two mechanisms that support conditional logic: <xsl:if> and <xsl:choose>. These allow a stylesheet to produce different output depending on the results of a boolean expression, which must yield true or false as defined by the XPath specification.

3.1.1. <xsl:if>

The behavior of the <xsl:if> element is comparable to the following Java code:

if (boolean-expression) {
  // do something
}

In XSLT, the syntax is as follows:

<xsl:if test="boolean-expression">
  <!-- Content: template -->
</xsl:if>

The test attribute is required and must contain a boolean expression. If the result is true, the content of this element is instantiated; otherwise, it is skipped. The code in Example 3-1 illustrates several uses of <xsl:if> and related XPath expressions. Code that is highlighted will be discussed in the next several paragraphs.

Example 3-1. <xsl:if> examples

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <!--******************************************************
      ** "/" template
      ***************************************************-->
  <xsl:template match="/">
    <html>
      <body>
        <h1>Conditional Processing Examples</h1>
        <xsl:apply-templates select="presidents"/>
      </body>
    </html>
  </xsl:template>
  <!--******************************************************
      ** "presidents" template
      ***************************************************-->
  <xsl:template match="presidents">
    <h3>
      List of
        <xsl:value-of select="count(president)"/>
      Presidents
    </h3>
    <ul>
      <xsl:for-each select="president">
        <li>
          <!-- display every other row in bold -->
          <xsl:if test="(position( ) mod 2) = 0">
            <xsl:attribute name="style">
              <xsl:text>font-weight: bold;</xsl:text>
            </xsl:attribute>
          </xsl:if>
          <xsl:apply-templates select="name"/>
          <!-- display some text after the last element -->
          <xsl:if test="position() = last( )">
            <xsl:text> (current president)</xsl:text>
          </xsl:if>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>
  <!--******************************************************
      ** "name" template
      ***************************************************-->
  <xsl:template match="name">
    <xsl:value-of select="last"/>
    <xsl:text>, </xsl:text>
    <xsl:value-of select="first"/>
    <xsl:if test="middle">
      <xsl:text> disable-output-escaping="yes">&amp;nbsp;</xsl:text>
      <xsl:value-of select="middle"/>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

The first thing the match="presidents" template outputs is a heading that displays the number of presidents:

List of
  <xsl:value-of select="count(president)"/>
Presidents

The count( ) function is an XPath node set function and returns the number of elements in a node set. In this case, the node set is the list of <president> elements that are direct children of the <presidents> element, so the number of presidents in the XML file is displayed. The next block of code does the bulk of the work in this stylesheet, outputting each president as a list item using a loop:

<xsl:for-each select="president">
  <li>
    <!-- display every other row in bold -->
    <xsl:if test="(position( ) mod 2) = 0">
      <xsl:attribute name="style">
        <xsl:text>font-weight: bold;</xsl:text>
      </xsl:attribute>
    </xsl:if>

In this example, the <xsl:for-each> loop first selects all <president> elements that are immediate children of the <presidents> element. As the loop iterates over this node set, the position( ) function returns an integer representing the current node position within the current node list, beginning with index 1. The mod operator computes the remainder following a truncating division, just as Java and ECMAScript do for their % operator. The XPath expression (position( ) mod 2) = 0 will return true for even numbers; therefore the style attribute will be added to the <li> tag for every other president, making that list item bold.

This template continues as follows:

<xsl:apply-templates select="name"/>
<!-- display some text after the last element -->
<xsl:if test="position() = last( )">
  <xsl:text> (current president)</xsl:text>
</xsl:if>
</li>
</xsl:for-each>

The last( ) function returns an integer indicating the size of the current context; in this case, it returns the number of presidents. When the position is equal to this count, the additional text (current president) is appended to the result tree. Java programmers should note that XPath uses a single = character for comparisons instead of ==, as Java does. A portion of the HTML for our list ends up looking like this:

<li>Washington, George</li>
<li style="font-weight: bold;">Adams, John</li>
<li>Jefferson, Thomas</li>
<li style="font-weight: bold;">Madison, James</li>
<li>Monroe, James</li>
<li style="font-weight: bold;">Adams, John&nbsp;Quincy</li>
<li>Jackson, Andrew</li>
...remaining HTML omitted
<li>Bush, George (current president)</li>

The name output has been improved from the previous chapter and now uses <xsl:if> to determine if the middle name is present:

<xsl:template match="name">
  <xsl:value-of select="last"/>
  <xsl:text>, </xsl:text>
  <xsl:value-of select="first"/>
  <xsl:if test="middle">
    <xsl:text> disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="middle"/>
  </xsl:if>
</xsl:template>

In this case, <xsl:if test="middle"> checks for the existence of a node set rather than for a boolean value. If any <middle> elements are found, the content of <xsl:if> is instantiated. The test does not have to be this simplistic; any of the XPath location paths from the previous chapter would work here as well.

As written here, if any <middle> elements are found, the first one is printed. Later, in Example 3-7, <xsl:for-each> will be used to print all middle names for presidents, such as George Herbert Walker Bush.

Checking for the existence of an attribute is very similar to checking for the existence of an element. For example:

<xsl:if test="@someAttribute">
  ...execute this code if "someAttribute" is present
</xsl:if>

Unlike most programming languages, <xsl:if> does not have a corresponding else or otherwise clause. This is only a minor inconvenience[9] because the <xsl:choose> element provides this functionality.

[9] <xsl:choose> requires a lot of typing.

3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise>

The XSLT equivalent of Java's switch statement is <xsl:choose> , which is virtually identical[10] in terms of functionality. <xsl:choose> must contain one or more <xsl:when> elements followed by an optional <xsl:otherwise> element. Example 3-2 illustrates how to use this feature. This example also uses <xsl:variable>, which will be covered in the next section.

[10] Java's switch statement only works with char, byte, short, or int.

Example 3-2. <xsl:choose>

<xsl:template match="presidents">
  <h3>Color Coded by Political Party</h3>
  <ul>
    <xsl:for-each select="president">
      <xsl:variable name="color">
        <!-- define the color value based on political party -->
        <xsl:choose>
          <xsl:when test="party = 'Democratic'">
            <xsl:text>blue</xsl:text>
          </xsl:when>
          <xsl:when test="party = 'Republican'">
            <xsl:text>green</xsl:text>
          </xsl:when>
          <xsl:when test="party = 'Democratic Republican'">
            <xsl:text>purple</xsl:text>
          </xsl:when>
          <xsl:when test="party = 'Federalist'">
            <xsl:text>brown</xsl:text>
          </xsl:when>
          <xsl:when test="party = 'Whig'">
            <xsl:text>black</xsl:text>
          </xsl:when>
          <!-- never executed in this example -->
          <xsl:otherwise>
            <xsl:text>red</xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <li>
        <font color="{$color}">
          <!-- show the party name -->
          <xsl:apply-templates select="name"/>
          <xsl:text> - </xsl:text>
          <xsl:value-of select="party"/>
        </font>
      </li>
    </xsl:for-each>
  </ul>
</xsl:template>

In this example, the list of presidents is displayed in order along with the political party of each president. The <xsl:when> elements test for each possible party, setting the value of a variable. This variable, color, is then used in a font tag to set the current color to something different for each party. The <xsl:otherwise> element is never executed because all of the political parties are listed in the <xsl:when> elements. If a new president affiliated with some other political party is ever elected, then none of the <xsl:when> conditions would be true, and the font color would be red.

One difference between the XSLT approach and a pure Java approach is that XSLT does not require break statements between <xsl:when> elements. In XSLT, the <xsl:when> elements are evaluated in the order in which they appear, and the first one with a test expression resulting in true is evaluated. All others are skipped. If no <xsl:when> elements match, then <xsl:otherwise>, if present, is evaluated.

Since <xsl:if> has no corresponding <xsl:else>, <xsl:choose> can be used to mimic the desired functionality as shown here:

<xsl:choose>
  <xsl:when test="condition">
    <!-- if condition -->
  </xsl:when>
  <xsl:otherwise>
    <!-- else condition -->
  </xsl:otherwise>
</xsl:choose>

As with other parts of XSLT, the XML syntax forces a lot more typing than Java programmers are accustomed to, but the mechanics of if/else are faithfully preserved.

Previous | Home | Next
2.6. Outputting Dynamic Attributes | Book Index | 3.2. Parameters and Variables
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Parameters and Variables (Java and XSLT)

 Java and XSLTSearch this book

3.2. Parameters and Variables

As in other programming languages, it is often desirable to set up a variable whose value is reused in several places throughout a stylesheet. If the title of a book is displayed repeatedly, then it makes sense to store that title in a variable rather than scan through the XML data and locate the title repeatedly. It can also be beneficial to set up a variable once and pass it as a parameter to one or more templates. These templates often use <xsl:if> or <xsl:choose> to produce different content depending on the value of the parameter that was passed.

3.2.1. <xsl:variable>

Variables in XSLT are defined with the <xsl:variable> element and can be global or local. A global variable is defined at the "top-level" of a stylesheet, which means that it is defined outside of any templates as a direct child of the <xsl:stylesheet> element. Top-level variables are visible throughout the entire stylesheet, even in templates that occur before the variable declaration.

The other place to define a variable is inside of a template. These variables are visible only to elements that follow the <xsl:variable> declaration within that template and to their descendants. The code in Example 3-2 showed this form of <xsl:variable> as a mechanism to define the font color.

3.2.1.1. Defining variables

Variables can be defined in one of three ways:

<xsl:variable name="homePage">index.html</xsl:variable>
<xsl:variable name="lastPresident"select="president[position() = last( )]/name"/>
<xsl:variable name="empty"/>

In the first example, the content of <xsl:variable> specifies the variable value. In the simple example listed here, the text index.html is assigned to the homePage variable. More complex content is certainly possible, as shown earlier in Example 3-2.

The second way to define a variable relies on the select attribute. The value is an XPath expression, so in this case we are selecting the name of the last president in the list.

Finally, a variable without a select attribute or content is bound to an empty string. The example shown in item 3 is equivalent to:

<xsl:variable name="empty" select="''"/>

3.2.1.2. Using variables

To use a variable, refer to the variable name with a $ character. In the following example, an XPath location path is used to select the name of the last president. This text is then stored in the lastPresident variable:

<xsl:variable name="lastPresident" select="president[position() = last( )]/name"/>

Later in the same stylesheet, the lastPresident variable can be displayed using the following fragment of code:

<xsl:value-of select="$lastPresident"/>

Since the select attribute of <xsl:value-of> expects to see an XPath expression, $lastPresident is treated as something dynamic, rather than as static text. To use a variable within an HTML attribute value, however, you must use the attribute value template (AVT) syntax, placing braces around the variable reference:

<a href="{$homePage}">Click here to return to the home page...</a>

Without the braces, the variable would be misinterpreted as literal text rather than treated dynamically.

The primary limitation of variables is that they cannot be changed. It is impossible, for example, to use a variable as a counter in an <xsl:for-each> loop. This can be frustrating to programmers accustomed to variables that can be changed, but can often be overcome with some ingenuity. It usually comes down to passing a parameter to a template instead of using a global variable and then recursively calling the template again with an incremented parameter value. An example of this technique will be presented shortly.

Another XSLT trick involves combining the variable initialization with <xsl:choose>. Since variables cannot be changed, you cannot first declare a variable and then assign its value later on. The workaround is to place the variable definition as a child of <xsl:variable>, perhaps using <xsl:choose> as follows:

<xsl:variable name="midName">
  <xsl:choose>
    <xsl:when test="middleName">
      <xsl:value-of select="middleName"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text> </xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

This code defines a variable called midName. If the <middleName> element is present, its value is assigned to midName. Otherwise, a blank space is assigned.

3.2.2. <xsl:call-template> and Named Templates

Up until this point, all of the templates have been tightly coupled to the actual data in the XML source. For example, the following template matches an <employee> element; therefore, <employee> must be contained within your XML data:

<xsl:template match="employee">
...content, perhaps display the name and SSN for the employee
</xsl:template>

But in many cases, you may wish to use this template for types of elements other than <employee>. In addition to <employee> elements, you may want to use this same code to output information for a <programmer> or <manager> element. In these circumstances, <xsl:call-template> can be used to explicitly invoke a template by name, rather than matching a pattern in the XML data. The template will have the following form:

<xsl:template name="formatSSN">
...content
</xsl:template>

This template will be used to support the following XML data, in which both <manager> and <programmer> elements have ssn attributes. Using a single named template avoids the necessity to write one template for <manager> and another for <programmer>. We will see an example XSLT stylesheet when we discuss parameters.

<?xml version="1.0" encoding="UTF-8"?>
<team>
  <manager ssn="230568737">
    <name>Aidan Burke</name>
  </manager>
  <programmer ssn="393776766">
    <name>Jennifer Burke</name>
  </programmer>
  <programmer ssn="993885777">
    <name>Bill Tellam</name>
  </programmer>
</team>

3.2.3. <xsl:param>and <xsl:with-param>

It is difficult to use named templates without parameters, and parameters can also be used for regular templates. Parameters allow the same template to take on different behavior depending on data the caller provides, resulting in more reusable code fragments. In the case of a named template, parameters allow data such as a social security number to be passed into the template. Example 3-3 contains a complete stylesheet that demonstrates how to pass the ssn parameter into a named template.

Example 3-3. namedTemplate.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:template match="/">
    <html>
      <body>
        <h3>Team Members</h3>
        <ul>
        <xsl:for-each select="team/manager|team/programmer">
          <xsl:sort select="name"/>
          <li>
            <xsl:value-of select="name"/>
            <xsl:text>, ssn = </xsl:text>
            <xsl:call-template name="formatSSN">
              <xsl:with-param name="ssn" select="@ssn"/>
            </xsl:call-template>
          </li>
        </xsl:for-each>
        </ul>
      </body>
    </html>
  </xsl:template>

  <!-- a named template that formats a 9 digit SSN
       by inserting '-' characters -->
  <xsl:template name="formatSSN">
    <xsl:param name="ssn"/>
    <xsl:value-of select="substring($ssn, 1, 3)"/>
    <xsl:text>-</xsl:text>
    <xsl:value-of select="substring($ssn, 4, 2)"/>
    <xsl:text>-</xsl:text>
    <xsl:value-of select="substring($ssn, 6)"/>
  </xsl:template>
</xsl:stylesheet>

This stylesheet displays the managers and programmers in a list, sorted by name. The <xsl:for-each> element selects the union of team/manager and team/programmer, so all of the managers and programmers are listed. The pipe operator (|) computes the union of its two operands:

<xsl:for-each select="team/manager|team/programmer">

For each manager or programmer, the content of the <name> element is printed, followed by the value of the ssn attribute, which is passed as a parameter to the formatSSN template. Passing one or more parameters is accomplished by adding <xsl:with-param> as a child of <xsl:call-template> . To pass additional parameters, simply list additional <xsl:with-param> elements, all as children of <xsl:call-template>.

At the receiving end, <xsl:param> is used as follows:

<xsl:template name="formatSSN">
  <xsl:param name="ssn"/>
  ...

In this case, the value of the ssn parameter defaults to an empty string if it is not passed. In order to specify a default value for a parameter, use the select attribute. In the following example, the zeros are in apostrophes in order to treat the default value as a string rather than as an XPath expression:

<xsl:param name="ssn" select="'000000000'"/>

Within the formatSSN template, you can see that the substring( ) function selects portions of the social security number string. More details on substring( ) and other string-formatting functions are discussed later in this chapter.

3.2.4. Incrementing Variables

Unfortunately, there is no standard way to increment a variable in XSLT. Once a variable has been defined, it cannot be changed. This is comparable to a final field in Java. In some circumstances, however, recursion combined with template parameters can achieve similar results. The XML shown in Example 3-4 will be used to illustrate one such approach.

Example 3-4. familyTree.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="familyTree.xslt"?>
<person name="Otto">
  <person name="Sandra">
    <person name="Jeremy">
      <person name="Eliana"/>
    </person>
    <person name="Eric">
      <person name="Aidan"/>
    </person>
    <person name="Philip">
      <person name="Alex"/>
      <person name="Andy"/>
    </person>
  </person>
</person>

As you can see, the XML is structured recursively. Each <person> element can contain any number of <person> children, which in turn can contain additional <person> children. This is certainly a simplified family tree, but this recursive pattern does occur in many XML documents. When displaying this family tree, it is desirable to indent the text according to the ancestry. Otto would be at the root, Sandra would be indented by one space, and her children would be indented by an additional space. This gives a visual indication of the relationships between the people. For example:

Otto
  Sandra
    Jeremy
      Eliana
    Eric
      Aidan
    Philip
      Alex
      Andy

The XSLT stylesheet that produces this output is shown in Example 3-5.

Example 3-5. familyTree.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>

  <!-- processing begins here -->
  <xsl:template match="/">
    <html>
      <body>
        <!-- select the top level person -->
        <xsl:apply-templates select="person">
          <xsl:with-param name="level" select="'0'"/>
        </xsl:apply-templates>
      </body>
    </html>
  </xsl:template>

  <!-- Output information for a person and recursively select
       all children. -->
  <xsl:template match="person">
    <xsl:param name="level"/>

    <!-- indent according to the level -->
    <div style="text-indent:{$level}em">
      <xsl:value-of select="@name"/>
    </div>

    <!-- recursively select children, incrementing the level -->
    <xsl:apply-templates select="person">
      <xsl:with-param name="level" select="$level + 1"/>
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

As usual, this stylesheet begins by matching the document root and outputting a basic HTML document. It then selects the root <person> element, passing level=0 as the parameter to the template that matches person:

<xsl:apply-templates select="person">
  <xsl:with-param name="level" select="'0'"/>
</xsl:apply-templates>

The person template uses an HTML <div> tag to display each person's name on a new line and specifies a text indent in em s. In Cascading Style Sheets, one em is supposed to be equal to the width of the lowercase letter m in the current font. Finally, the person template is invoked recursively, passing in $level + 1 as the parameter. Although this does not increment an existing variable, it does pass a new local variable to the template with a larger value than before. Other than tricks with recursive processing, there is really no way to increment the values of variables in XSLT.

3.2.5. Template Modes

The final variation on templates is that of the mode. This feature is similar to parameters but a little simpler, sometimes resulting in cleaner code. Modes make it possible for multiple templates to match the same pattern, each using a different mode of operation. One template may display data in verbose mode, while another may display the same data in abbreviated mode. There are no predefined modes; you make them up. The mode attribute looks like this:

<xsl:template match="name" mode="verbose">
   ...display the full name
</xsl:template>

<xsl:template match="name" mode="abbreviated">
   ...omit the middle name
</xsl:template>

In order to instantiate the appropriate template, a mode attribute must be added to <xsl:apply-templates> as follows:

<xsl:apply-templates select="president/name" mode="verbose"/>

If the mode attribute is omitted, then the processor searches for a matching template that does not have a mode. In the code shown here, both templates have modes, so you must include a mode on <xsl:apply-templates> in order for one of your templates to be instantiated.

A complete stylesheet is shown in Example 3-6. In this example, the name of a president may occur inside either a table or a list. Instead of passing a parameter to the president template, two modes of operation are defined. In table mode, the template displays the name as a row in a table. In list mode, the name is displayed as an HTML list item.

Example 3-6. Template modes

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>

  <!--
    ** Demonstrates how to use template modes
    -->
  <xsl:template match="/">
    <html>
      <body>

        <h2>Presidents in an HTML Table</h2>
        <table border="1">
          <tr>
            <th>Last Name</th>
            <th>First Name</th>
          </tr>
          <xsl:apply-templates select="//president" mode="table"/>
        </table>

        <h2>Presidents in an Unordered List</h2>
        <ul>
          <xsl:apply-templates select="//president" mode="list"/>
        </ul>
      </body>
    </html>
  </xsl:template>

  <!--
    ** Display a president's name as a table row
    -->
  <xsl:template match="president" mode="table">
    <tr>
      <td>
        <xsl:value-of select="name/last"/>
      </td>
      <td>
        <xsl:value-of select="name/first"/>
      </td>
    </tr>
  </xsl:template>

  <!--
    ** Display a president's name as a list item
    -->
  <xsl:template match="president" mode="list">
    <li>
      <xsl:value-of select="name/last"/>
      <xsl:text>, </xsl:text>
      <xsl:value-of select="name/first"/>
    </li>
  </xsl:template>

</xsl:stylesheet>

3.2.6. <xsl:template> Syntax Summary

Sorting through all of the possible variations of <xsl:template> is a seemingly difficult task, but we have really only covered three attributes:

match
Specifies the node in the XML data that a template applies to

name
Defines an arbitrary name for a template, independent of specific XML data

mode
Similar to method overloading in Java, allowing multiple versions of a template that match the same pattern

The only attribute we have not discussed in detail is priority, which is used to resolve conflicts when more than one template matches. The XSLT specification defines a very specific set of steps for processors to follow when more than one template rule matches.[11] From a code maintenance perspective, it is a good idea to avoid conflicting template rules within a stylesheet. When combining multiple stylesheets, however, you may find yourself with conflicting template rules. In these cases, specifying a higher numeric priority for one of the conflicting templates can resolve the problem. Table 3-1 provides a few summarized examples of the various forms of <xsl:template>.

[11] See section 5.5 of the XSLT specification at http://www.w3.org/TR/xslt.

Table 3-1. Summary of common template syntax

Template example

Notes

<xsl:template match="president">
  ...
</xsl:template>

Matches president nodes in the source XML document

<xsl:template name="formatName">
  <xsl:param name="style"/>
  ...
</xsl:template>

Defines a named template; used in conjunction with <xsl:call-template> and <xsl:with-param>

<xsl:template match="customer"
    mode="myModeName">
  ...
</xsl:template>

Matches customer nodes when <xsl:apply-templates> also uses mode="myModeName"

Previous | Home | Next
3. XSLT Part 2 -- Beyond the Basics | Book Index | 3.3. Combining Multiple Stylesheets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Combining Multiple Stylesheets (Java and XSLT)

 Java and XSLTSearch this book

3.3. Combining Multiple Stylesheets

Through template parameters, named templates, and template modes, we have seen how to create more reusable fragments of code that begin to resemble function calls. By combining multiple stylesheets, one can begin to develop libraries of reusable XSLT templates that can dramatically increase productivity.

Productivity gains occur because programmers are not writing the same code over and over for each stylesheet. Reusable code is placed into a single stylesheet and imported or included into other stylesheets. Another advantage of this technique is maintainability. XSLT syntax can get ugly, and modularizing code into small fragments can greatly enhance readability. For example, we have seen several examples related to the list of presidents so far. Since we almost always want to display the name of a president or vice president, name-formatting templates should be broken out into a separate stylesheet. Example 3-7 shows a stylesheet designed for reuse by other stylesheets.

Example 3-7. nameFormatting.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <!--
    ** Show a name formatted like: "Burke, Eric Matthew"
    -->
  <xsl:template match="name" mode="lastFirstMiddle">
    <xsl:value-of select="last"/>
    <xsl:text>, </xsl:text>
    <xsl:value-of select="first"/>
    <xsl:for-each select="middle">
      <xsl:text> disable-output-escaping="yes">&amp;nbsp;</xsl:text>
      <xsl:value-of select="."/>
    </xsl:for-each>
  </xsl:template>

  <!--
    ** Show a name formatted like: "Eric Matthew Burke"
    -->
  <xsl:template match="name" mode="firstMiddleLast">
    <xsl:value-of select="first"/>
    <xsl:for-each select="middle">
      <xsl:text> disable-output-escaping="yes">&amp;nbsp;</xsl:text>
      <xsl:value-of select="."/>
    </xsl:for-each>
    <xsl:text> disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="last"/>
  </xsl:template>
</xsl:stylesheet>

The code in Example 3-7 uses template modes to determine which template is instantiated. Adding additional templates would be simple, and those changes would be available to any stylesheet that included or imported this one. This stylesheet was designed to be reused by other stylesheets, so it does not include a template that matches the root node.

For large web sites, the ability to import or include stylesheets is crucial. It almost goes without saying that every web page on a large site will contain the same navigation bar, footer, and perhaps a common heading region. Standalone stylesheet fragments included by other stylesheets should generate all of these reusable elements. This allows you to modify something like the copyright notice on your page footer in one place, and those changes are reflected across the entire web site without any programming changes.

3.3.1. <xsl:include>

The <xsl:include> element allows one stylesheet to include another. It is only allowed as a top-level element, meaning that <xsl:include> elements are siblings to <xsl:template> elements in the stylesheet structure. The syntax of <xsl:include> is:

<xsl:include href="uri-reference"/>

When a stylesheet includes another, the included stylesheet is effectively inserted in place of the <xsl:include> element. Actually, the children of its <xsl:stylesheet> element are inserted into the including document. It is possible to include many other stylesheets and for those stylesheets to include others.

Inclusion is a relatively simple mechanism because the resulting stylesheet behaves exactly as if you had typed all included elements into the including stylesheet. This can result in problems when two conflicting template rules are included, so you must be careful to plan ahead to avoid any conflicts. When a conflict occurs, the XSLT processor should report an error and halt.

3.3.2. <xsl:import>

Importing (rather than including) a stylesheet adds some intelligence to the process. When conflicts occur, the importing stylesheet takes precedence over any imported stylesheets. Unlike <xsl:include>, <xsl:import> elements must occur before any other element children of <xsl:stylesheet>, as shown here:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!-- xsl:import must occur before any other top-level elements -->
  <xsl:import href="pageElements.xslt"/>
  <xsl:import href="globalConstants.xslt"/>
  <xsl:output method="html"/>
  <xsl:template match="/">
    <html>
      ...
    </html>
  </xsl:template>
  <!-- but xsl:include can occur anywhere, provided it is a top-level element -->
  <xsl:include href="nameFormatting.xslt"/>
</xsl:stylesheet>

For the purposes of most web sites, the most common usage pattern is for each page to import or include common stylesheet fragments, such as templates to produce page headers, footers, and other reusable elements on a web site. Once a stylesheet has been included or imported, its templates can be used as if they were in the current stylesheet.

The key reason to use <xsl:import> instead of <xsl:include> is to avoid conflicts. If your stylesheet already has a template that matches pageHeader, you will not be able to include pageElements.xslt if it also has that template. On the other hand, you can use <xsl:import>. In this case, your own pageHeader template will take priority over the imported pageHeader.

NOTE: Changing all <xsl:import> elements to <xsl:include> will help identify any naming conflicts you did not know about.

Previous | Home | Next
3.2. Parameters and Variables | Book Index | 3.4. Formatting Text and Numbers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Formatting Text and Numbers (Java and XSLT)

 Java and XSLTSearch this book

3.4. Formatting Text and Numbers

XSLT and XPath define a small set of functions to manipulate text and numbers. These allow you to concatenate strings, extract substrings, determine the length of a string, and perform other similar tasks. While these features do not approach the capabilities offered by a programming language like Java, they do allow for some of the most common string manipulation tasks.

3.4.1. Number Formatting

The format-number( ) function is provided by XSLT to convert numbers such as 123 into formatted numbers such as $123.00. The function takes the following form:

string format-number(number, string, string?)

The first parameter is the number to format, the second is a format string, and the third (optional) is the name of an <xsl:decimal-format> element. We will cover only the first two parameters in this book. Interestingly enough, the behavior of the format-number( ) function is defined by the JDK 1.1.x version of the java.text.DecimalFormat class. For complete information on the syntax of the second argument, refer to the JavaDocs for JDK 1.1.x.

Outputting currencies is a common use for the format-number( ) function. The pattern $#,##0.00 can properly format a number into just about any U.S. currency. Table 3-2 demonstrates several possible inputs and results for this pattern.

Table 3-2. Formatting currencies using $#,##0.00

Number | Result
0 | $0.00
0.9 | $0.90
0.919 | $0.92
10 | $10.00
1000 | $1,000.00
12345.12345 | $12,345.12

The XSLT code to utilize this function may look something like this:

<xsl:value-of select="format-number(amt,'$#,##0.00')"/>

It is assumed that amt is some element in the XML data,[12] such as <amt>1000</amt>. The # and 0 characters are placeholders for digits and behave exactly as java.text.DecimalFormat specifies. Basically, 0 is a placeholder for any digit, while # is a placeholder that is absent when the input value is 0.

[12] The XSLT specification does not define what happens if the XML data does not contain a valid number.

Besides currencies, another common format is percentages. To output a percentage, end the format pattern with a % character. The following XSLT code shows a few examples:

<!-- outputs 0% -->
<xsl:value-of select="format-number(0,'0%')"/>

<!-- outputs 10% -->
<xsl:value-of select="format-number(0.1,'0%')"/>

<!-- outputs 100% -->
<xsl:value-of select="format-number(1,'0%')"/>

As before, the first parameter to the format-number( ) function is the actual number to be formatted, and the second parameter is the pattern. The 0 in the pattern indicates that at least one digit should always be displayed. The % character also has the side effect of multiplying the value by 100 so it is displayed as a percentage. Consequently, 0.15 is displayed as 15%, and 1 is displayed as 100%.

To test more patterns, the XML data shown in Example 3-8 can be used. This works in conjunction with numberFormatting.xslt to display every combination of format and number listed in the XML data.

Example 3-8. numberFormatting.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="numberFormatting.xslt"?>
<numberFormatting>
  <formatSamples>
    <!-- add more <format> elements to test more combinations-->
    <format>$#,##0.00</format>
    <format>#.#</format>
    <format>0.#</format>
    <format>0.0</format>
    <format>0%</format>
    <format>0.0#</format>
  </formatSamples>
  <numberSamples>
    <!-- add more <number> elements to test more combinations -->
    <number>-10</number>
    <number>-1</number>
    <number>0</number>
    <number>0.000123</number>
    <number>0.1</number>
    <number>0.9</number>
    <number>0.91</number>
    <number>0.919</number>
    <number>1</number>
    <number>10</number>
    <number>100</number>
    <number>1000</number>
    <number>10000</number>
    <number>12345.12345</number>
    <number>55555.55555</number>
  </numberSamples>
</numberFormatting>

The stylesheet, numberFormatting.xslt, is shown in Example 3-9. Comments in the code explain what happens at each step. To test new patterns and numbers, just edit the XML data and apply the transformation again. Since the XML file references the stylesheet with <?xml-stylesheet?>, you can simply load the XML into an XSLT compliant web browser and click on the Reload button to see changes as they are made.

Example 3-9. numberFormatting.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:template match="/">
    <html>
      <body>
        <!-- loop over each of the sample formats -->
        <xsl:for-each select="numberFormatting/formatSamples/format">
          <h2>
            <!-- show the format as a heading -->
            <xsl:value-of select="."/>
          </h2>
          <table border="1" cellpadding="2" cellspacing="0">
            <tr>
              <th>Number</th>
              <th>Result</th>
            </tr>

            <!-- pass the format as a parameter to the template that
                  shows each number -->
            <xsl:apply-templates select="/numberFormatting/numberSamples/number">
              <xsl:with-param name="fmt" select="."/>
            </xsl:apply-templates>
          </table>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <!-- output the number followed by the result of the format-number function -->
  <xsl:template match="number">
    <xsl:param name="fmt"/>
    <tr>
      <td align="right">
        <xsl:value-of select="."/>
      </td>
      <td align="right">
        <!-- the first param is a dot, representing the text content
             of the <number> element -->
        <xsl:value-of select="format-number(.,$fmt)"/>
      </td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

This stylesheet first loops over the list of <format> elements:

<xsl:for-each select="numberFormatting/formatSamples/format">

Within the loop, all of the <number> elements are selected. This means that every format is applied to every number:

<xsl:apply-templates select="/numberFormatting/numberSamples/number">

3.4.2. Text Formatting

Several text-formatting functions are defined by the XPath specification, allowing code in an XSLT stylesheet to perform such operations as concatenating two or more strings, extracting a substring, and computing the length of a string. Unlike strings in Java, all strings in XSLT and XPath are indexed from position 1 instead of position 0.

Let's suppose that a stylesheet defines the following variables:

<xsl:variable name="firstName" select="'Eric'"/>
<xsl:variable name="lastName" select="'Burke'"/>
<xsl:variable name="middleName" select="'Matthew'"/>
<xsl:variable name="fullName"
    select="concat($firstName, ' ', $middleName, ' ', $lastName)"/>

In the first three variables, apostrophes are used to indicate that the values are strings. Without the apostrophes, the XSLT processor would treat these as XPath expressions and attempt to select nodes from the XML input data. The third variable, fullName, demonstrates how the concat( ) function is used to concatenate two or more strings together. The function simply takes a comma-separated list of strings as arguments and returns the concatenated results. In this case, the value for fullName is "Eric Matthew Burke."

Table 3-3 provides additional examples of string functions. The variables in this table are the same ones from the previous example. In the first column, the return type of the function is listed first, followed by the function name and the list of parameters. The second and third columns provide an example usage and the output from that example.

Table 3-3. String function examples

Function syntax

Example

Output

string concat
(string,string,string*)

concat($firstName, ' ', $lastName)

Eric Burke

boolean starts-with
(string,string)

starts-with($firstName, 'Er')

true

boolean contains(string,string)

contains($fullName, 'Smith')

false

string substring-before
(string,string)

substring-before($fullName, ' ')

Eric

string substring-after
(string,string)

substring-after($fullName, ' ')

Matthew Burke

string substring
(string,number,number?)

substring($middleName,1,1)

M

number string-length(string?)

string-length($fullName)

18

string normalize-space(string?)

normalize-space(' testing ')

testing

string translate
(string,string,string)

translate('test','aeiou','AEIOU')

tEst

All string comparisons, such as starts-with() and contains( ), are case-sensitive. There is no concept of case-insensitive comparison in XSLT. One potential workaround is to convert both strings to upper- or lowercase, and then perform the comparison. Converting a string to upper- or lowercase is not directly supported by a function in the current implementation of XSLT, but the translate( ) function can be used to perform the task. The following XSLT snippet converts a string from lower- to uppercase:

translate($text,
    'abcdefghijklmnopqrstuvwxyz',
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ')

In the substring-before( ) and substring-after( ) functions, the second argument contains a delimiter string. This delimiter does not have to be a single character, and an empty string is returned if the delimiter is not found. These functions could be used to parse formatted data such as dates:

<date>06/25/1999</date>

The XSLT used to extract the month, day, and year looks like this:

<xsl:variable name="dateStr" select="//date"/>
<xsl:variable name="dayYear" select="substring-after($dateStr, '/')"/>
Month: <xsl:value-of select="substring-before($dateStr, '/')"/> <br/>
Day: <xsl:value-of select="substring-before($dayYear, '/')"/> <br/>
Year: <xsl:value-of select="substring-after($dayYear, '/')"/>

In the first line of code, the dateStr variable is initialized to contain the full date. The next line then creates the dayYear variable, which contains everything after the first / character -- at this point, dateStr=06/25/1999 and dayYear=25/1999. In Java, this is slightly easier because you simply create an instance of the StringTokenizer class and iterate through the tokens or use the lastIndexOf( ) method of java.lang.String to locate the second /. With XSLT, the options are somewhat more limited. The remaining lines continue chopping up the variables into substrings, again delimiting on the / character. The output is as follows:

Month: 06
Day: 25
Year: 1999

Another form of the substring( ) function takes one or two number arguments, indicating the starting index and the optional length of the substring. If the second number is omitted, the substring continues until the end of the input string. The starting index always begins at position 1, so substring("abcde",2,3) returns bcd, and substring("abcde",2) returns bcde.

Previous | Home | Next
3.3. Combining Multiple Stylesheets | Book Index | 3.5. Schema Evolution
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schema Evolution (Java and XSLT)

 Java and XSLTSearch this book

3.5. Schema Evolution

Looking beyond HTML generation, a key use for XSLT is transforming one form of XML into another form. In many cases, these are not radical transformations, but minor enhancements such as adding new attributes, changing the order of elements, or removing unused data. If you have only a handful of XML files to transform, it is a lot easier to simply edit the XML directly rather than going through the trouble of writing a stylesheet. But in cases where a large collection of XML documents exist, a single XSLT stylesheet can perform transformations on an entire library of XML files in a single pass. For B2B applications, schema evolution is useful when different customers require the same data, but in different formats.

3.5.1. An Example XML File

Let's suppose that you wrote a logging API for your Java programs. Log files are written in XML and are formatted as shown in Example 3-10.

Example 3-10. Log file before transformation

<?xml version="1.0" encoding="UTF-8"?>
<log>
  <message text="input parameter was null">
    <type>ERROR</type>
    <when>
      <year>2000</year>
      <month>01</month>
      <day>15</day>
      <hour>03</hour>
      <minute>12</minute>
      <second>18</second>
    </when>
    <where>
      <class>com.foobar.util.StringUtil</class>
      <method>reverse(String)</method>
    </where>
  </message>
  <message text="cannot read config file">
    <type>WARNING</type>
    <when>
      <year>2000</year>
      <month>01</month>
      <day>15</day>
      <hour>06</hour>
      <minute>35</minute>
      <second>44</second>
    </when>
    <where>
      <class>com.foobar.servlet.MainServlet</class>
      <method>init( )</method>
    </where>
  </message>
  <!-- more messages ... -->
</log>

As you can see from this example, the file format is quite verbose. Of particular concern is how the date and time are written. Since log files can be quite large, it would be a good idea to select a more concise format for this information. Additionally, the text is stored as an attribute on the <message> element, and the type is stored as a child element. It would make more sense to list the type as an attribute and the message as an element. For example:

<message type="WARNING">
  <text>This is the text of a message.
        Multi-line messages are easier when an
        element is used instead of an attribute.</text>
  ...remainder omitted

3.5.2. The Identity Transformation

Whenever writing a schema evolution stylesheet, it is a good idea to start with an identity transformation . This is a very simple template that simply takes the original XML document and "transforms" it into a new document with the same elements and attributes as the original document. Example 3-11 shows a stylesheet that contains an identity transformation template.

Example 3-11. identityTransformation.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <xsl:template match="@*|node( )">
    <xsl:copy>
      <xsl:apply-templates select="@*|node( )"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Amazingly, it takes only a single template to perform the identity transformation, regardless of the complexity of the XML data. Our stylesheet encodes the result using UTF-8 and indents lines, regardless of the original XML format. In XPath, node( ) is a node test that matches all child nodes of the current context. This is fine, but it omits the attributes of the current context. For this reason, @* must be unioned with node( ) as follows:

<xsl:template match="@*|node( )">

Translated into English, this means that the template will match any attribute or any child node of the current context. Since node( ) includes elements, comments, processing instructions, and even text, this template will match anything that can occur in the XML document.

Inside of our template, we use <xsl:copy> . As you can probably guess, this instructs the XSLT processor to simply copy the current node to the result tree. To continue processing, <xsl:apply-templates> then selects all attributes or children of the current context using the following code:

<xsl:apply-templates select="@*|node( )"/>

3.5.3. Transforming Elements and Attributes

Once you have typed in the identity transformation and tested it, it is time to begin adding additional templates that actually perform the schema evolution. In XSLT, it is possible for two or more templates to match a pattern in the XML data. In these cases, the more specific template is instantiated. Without going into a great deal of technical detail, an explicit match such as <xsl:template match="when"> takes precedence over the identity transformation template, which is essentially a wildcard pattern that matches any attribute or node. To modify specific elements and attributes, simply add more specific templates to the existing identity transformation stylesheet.

In the log file example, a key problem is the quantity of XML data written for each <when> element. Instead of representing the date and time using a series of child elements, it would be much more concise to use the following syntax:

<timestamp time="06:35:44" day="15" month="01" year="2000"/>

The following template will perform the necessary transformation:

<xsl:template match="when">
  <!-- change 'when' into 'timestamp', and change its
       child elements into attributes -->
  <timestamp time="{hour}:{minute}:{second}"
    year="{year}" month="{month}" day="{day}"/>
</xsl:template>

This template can be added to the identity transformation stylesheet and will take precedence whenever a <when> element is encountered. Instead of using <xsl:copy>, this template produces a new <timestamp> element AVTs are then used to specify attributes for this element, effectively converting element values into attribute values. The AVT syntax {hour} is equivalent to selecting the <hour> child of the <when> element. You may notice that XSLT processors do not necessarily preserve the order of attributes. This is not important because the relative ordering of attributes is meaningless in XML, and you cannot force the order of XML attributes.

The next thing to tackle is the <message> element. As mentioned earlier, we would like to convert the text attribute to an element, and the <type> element to an attribute. Just like before, add a new template that matches the <message> element, which will take precedence over the identity transformation. Comments in the code explain what happens at each step.

<!-- locate <message> elements -->
<xsl:template match="message">
  <!-- copy the current node, but not its attributes -->
  <xsl:copy>
    <!-- change the <type> element to an attribute -->
    <xsl:attribute name="type">
      <xsl:value-of select="type"/>
    </xsl:attribute>

    <!-- change the text attribute to a child node -->
    <xsl:element name="text">
      <xsl:value-of select="@text"/>
    </xsl:element>

    <!-- since the select attribute is not present,
        xsl:apply-templates processes all children
        of the current node. (not attributes or processing instructions!) -->
    <xsl:apply-templates/>

  </xsl:copy>
</xsl:template>

This almost completes the stylesheet. <xsl:copy> simply copies the <message> element to the result tree but does not copy any of its attributes or children. We can explicitly add new attributes using <xsl:attribute> and explicitly create new child elements using <xsl:element>. <xsl:apply-templates> then tells the processor to continue the transformation process for the children of <message>. One problem right now is that the <type> element has been converted into an attribute but has not been removed from the document. The identity transformation still copies the <type> element to the result tree without modification. To fix this, simply add an empty template as follows:

<xsl:template match="type"/>

The complete schema evolution stylesheet simply contains the previous templates. Without duplicating all of the code, here is its overall structure:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <!-- the identity transformation -->
  <xsl:template match="@*|node( )">
    ...
  </xsl:template>

  <!-- locate <message> elements -->
  <xsl:template match="message">
    ...
  </xsl:template>

  <!-- locate <when> elements -->
  <xsl:template match="when">
    ...
  </xsl:template>

  <!-- suppress the <type> element
  <xsl:template match="type"/>
</xsl:stylesheet>

3.5.4. The Result File

Now that the stylesheet is complete, it can be applied to all of the existing XML log files using a simple shell script or batch file. The resulting XML file is shown in Example 3-12.

Example 3-12. Result of the transformation

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="schemaChange.xslt"?>
<log>
  <message type="ERROR">
        <text>input parameter was null</text>

    <timestamp time="03:12:18" day="15" month="01" year="2000"/>
    <where>
      <class>com.foobar.util.StringUtil</class>
      <method>reverse(String)</method>
    </where>
  </message>
  <message type="WARNING">
        <text>cannot read config file</text>

    <timestamp time="06:35:44" day="15" month="01" year="2000"/>
    <where>
      <class>com.foobar.servlet.MainServlet</class>
      <method>init( )</method>
    </where>
  </message>
  <message type="ERROR">
        <text>negative duration is not allowed</text>

    <timestamp time="10:01:49" day="17" month="01" year="2000"/>
    <where>
      <class>com.foobar.util.DateUtil</class>
      <method>getWeek(int)</method>
    </where>
  </message>
</log>

Previous | Home | Next
3.4. Formatting Text and Numbers | Book Index | 3.6. Ant Documentation Stylesheet
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Ant Documentation Stylesheet (Java and XSLT)

 Java and XSLTSearch this book

3.6. Ant Documentation Stylesheet

Apache's Ant has taken the Java development community by storm, supplementing traditional Java IDEs and outright replacing Makefiles on most Java development projects. Ant is a build tool, similar to the make utility, only it uses XML files instead of Makefiles. In addition to a portable build file based on XML, Ant itself is written in Java and has few platform-specific dependencies. Finally, since Ant can reuse the same running instance of the Java Virtual Machine for nearly every step of the build process, it is blazingly fast. Ant can be downloaded from http://jakarta.apache.org and is open source software.

3.6.1. Ant Basics

Ant is driven by an XML build file, which consists of one project. This project contains one or more targets, and targets can have dependencies on one another. The project and targets are represented as <project> and <target> in the XML build file; <project> must be the document root element. It is common to have a "prepare" target that builds the output directories and a "compile" target that depends on the "prepare" target. If you tell Ant to execute the "compile" target, it first checks to see that the "prepare" target has created the necessary directories. The structure of an Ant build file looks like this:

<?xml version="1.0"?>
<project name="SampleProject" default="compile" basedir=".">

  <!-- global properties -->
  <property name="srcdir" value="src"/>
  <property name="builddir" value="build"/>

  <target name="prepare" description="Creates the output directories">
    ...tasks
  </target>

  <target name="compile" depends="prepare">
    ...tasks
  </target>

  <target name="distribute" depends="compile">
    ...tasks
  </target>
</project>

For each target, Ant is smart enough to know if files have been modified and if it needs to do any work. For compilation, the timestamps of .class files are compared to timestamps of .java files. Through these dependencies, Ant can avoid unnecessary compilation and perform quite well. Although the targets shown here contain only single dependencies, it is possible for a target to depend on several other targets:

<target name="X" depends="A,B,C">

Although Ant build files are much simpler than corresponding Makefiles, complex projects can introduce many dependencies that are difficult to visualize. It can be helpful to view the complete list of targets with dependencies displayed visually, such as in a hierarchical tree view. XSLT can be used to generate this sort of report.

3.6.2. Stylesheet Functionality

Since the build file is XML, XSLT makes it easy to generate HTML web pages that summarize the targets and dependencies. Our stylesheet also shows a list of global properties and can easily be extended to display anything else contained in the build file.

Although this stylesheet creates several useful HTML tables in its report, its most interesting feature is the ability to display a complete dependency graph of all Ant build targets. The output for this graph is shown in Example 3-13.

Example 3-13. Target dependencies

clean
   all (depends on clean, dist)
prepare
   tomcat (depends on prepare)
      j2ee (depends on tomcat)
         j2ee-dist (depends on j2ee)
      main (depends on tomcat, webapps)
         dist (depends on main, webapps)
            dist-zip (depends on dist)
            all (depends on clean, dist)
   webapps (depends on prepare)
      dist (depends on main, webapps)
         dist-zip (depends on dist)
         all (depends on clean, dist)
      main (depends on tomcat, webapps)
         dist (depends on main, webapps)
            dist-zip (depends on dist)
            all (depends on clean, dist)
targets

This is actually the output from the Ant build file included with Apache's Tomcat. The list of top-level targets is shown at the root level, and dependent targets are indented and listed next. The targets shown in parentheses list what each target depends on. This tree view is created by recursively analyzing the dependencies, which appear in the Ant build file as follows:

<target name="all" depends="clean,dist">

Figure 3-1 shows a portion of the output in a web browser. A table listing all targets follows the dependency graph. The output concludes with a table of all global properties defined in the Ant build file.

Figure 3-1. Antdoc sample output

The comma-separated list of dependencies presents a challenge that is best handled through recursion. For each target in the build file, it is necessary to print a list of targets that depend on that target. It is possible to have many dependencies, so an Ant build file may contain a <target> that looks like this:

<target name="docs" depends="clean, prepare.docs, compile">

In the first prototype of the Antdoc stylesheet, the algorithm to print the dependency graph uses simple substring operations to determine if another target depends on the current target. This turns out to be a problem because two unrelated targets might have similar names, so some Ant build files cause infinite recursion in the stylesheet. In the preceding example, the original prototype of Antdoc says that "docs" depends on itself because its list of dependencies contains the text prepare.docs.

In the finished version of Antdoc, the list of target dependencies is cleaned up to remove spaces and commas. For example, "clean, prepare.docs, compile" is converted into "|clean|prepare.docs|compile|". By placing the pipe (|) character before and after every dependency, it becomes much easier to locate dependencies by searching for strings.

3.6.3. The Complete Example

The complete XSLT stylesheet is listed in Example 3-14. Comments within the code explain what happens in each step. To use this stylesheet, simply invoke your favorite XSLT processor at the command line, passing antdoc.xslt and your Ant build file as parameters.

Example 3-14. antdoc.xslt

<?xml version="1.0" encoding="UTF-8"?>
<!--
  **************************************************************
  ** Antdoc v1.0
  **
  ** Written by Eric Burke (burke_e@ociweb.com)
  **
  ** Uses XSLT to generate HTML summary reports of Ant build
  ** files.
  ***********************************************************-->
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml"
      doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
      indent="yes" encoding="UTF-8"/>

  <!-- global variable: the project name -->
  <xsl:variable name="projectName" select="/project/@name"/>
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Ant Project Summary -
            <xsl:value-of select="$projectName"/></title>
      </head>
      <body>
        <h1>Ant Project Summary</h1>
        <xsl:apply-templates select="project"/>
      </body>
    </html>
  </xsl:template>

  <!--
  ***************************************************************
  ** "project" template
  ************************************************************-->
  <xsl:template match="project">
    <!-- show the project summary table, listing basic info
         such as name, default target, and base directory -->
    <table border="1" cellpadding="4" cellspacing="0">
      <tr><th colspan="2">Project Summary</th></tr>
      <tr>
        <td>Project Name:</td>
        <td><xsl:value-of select="$projectName"/></td>
      </tr>
      <tr>
        <td>Default Target:</td>
        <td><xsl:value-of select="@default"/></td>
      </tr>
      <tr>
        <td>Base Directory:</td>
        <td><xsl:value-of select="@basedir"/></td>
      </tr>
    </table>

    <!-- show all target dependencies as a tree -->
    <h3>Target Dependency Tree</h3>
    <xsl:apply-templates select="target[not(@depends)]" mode="tree">
      <xsl:sort select="@name"/>
    </xsl:apply-templates>
    <p/>

    <!-- Show a table of all targets -->
    <table border="1" cellpadding="4" cellspacing="0">
      <tr><th colspan="3">List of Targets</th></tr>
      <tr>
        <th>Name</th>
        <th>Dependencies</th>
        <th>Description</th>
      </tr>
      <xsl:apply-templates select="target" mode="tableRow">
        <xsl:sort select="count(@description)" order="descending"/>
        <xsl:sort select="@name"/>
      </xsl:apply-templates>
    </table>
    <p/>
    <xsl:call-template name="globalProperties"/>
  </xsl:template>

  <!--
  ***************************************************************
  ** Create a table of all global properties.
  ************************************************************-->
  <xsl:template name="globalProperties">
    <xsl:if test="property">
      <table border="1" cellpadding="4" cellspacing="0">
        <tr><th colspan="2">Global Properties</th></tr>
        <tr>
          <th>Name</th>
          <th>Value</th>
        </tr>
        <xsl:apply-templates select="property" mode="tableRow">
          <xsl:sort select="@name"/>
        </xsl:apply-templates>
      </table>
    </xsl:if>
  </xsl:template>

  <!--
  ***************************************************************
  ** Show an individual property in a table row.
  ************************************************************-->
  <xsl:template match="property[@name]" mode="tableRow">
    <tr>
      <td><xsl:value-of select="@name"/></td>
      <td>
        <xsl:choose>
          <xsl:when test="not(@value)">
            <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="@value"/>
          </xsl:otherwise>
        </xsl:choose>
      </td>
    </tr>
  </xsl:template>

  <!--
  ***************************************************************
  ** "target" template, mode=tableRow
  ** Print a target name and its list of dependencies in a
  ** table row.
  ************************************************************-->
  <xsl:template match="target" mode="tableRow">
    <tr valign="top">
      <td><xsl:value-of select="@name"/></td>
      <td>
        <xsl:choose>
          <xsl:when test="@depends">
            <xsl:call-template name="parseDepends">
              <xsl:with-param name="depends" select="@depends"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>-</xsl:otherwise>
        </xsl:choose>
      </td>
      <td>
        <xsl:if test="@description">
          <xsl:value-of select="@description"/>
        </xsl:if>
        <xsl:if test="not(@description)">
          <xsl:text>-</xsl:text>
        </xsl:if>
      </td>
    </tr>
  </xsl:template>

  <!--
  ***************************************************************
  ** "parseDepends" template
  ** Tokenizes and prints a comma separated list of dependencies.
  ** The first token is printed, and the remaining tokens are
  ** recursively passed to this template.
  ************************************************************-->
  <xsl:template name="parseDepends">
    <!-- this parameter contains the list of dependencies -->
    <xsl:param name="depends"/>

    <!-- grab everything before the first comma,
         or the entire string if there are no commas -->
    <xsl:variable name="firstToken">
      <xsl:choose>
        <xsl:when test="contains($depends, ',')">
          <xsl:value-of
            select="normalize-space(substring-before($depends, ','))"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="normalize-space($depends)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:variable name="remainingTokens"
      select="normalize-space(substring-after($depends, ','))"/>

    <!-- output the first dependency -->
    <xsl:value-of select="$firstToken"/>

    <!-- recursively invoke this template with the remainder
         of the comma separated list -->
    <xsl:if test="$remainingTokens">
      <xsl:text>, </xsl:text>
      <xsl:call-template name="parseDepends">
        <xsl:with-param name="depends" select="$remainingTokens"/>
      </xsl:call-template>
    </xsl:if>

  </xsl:template>

  <!--
  ***************************************************************
  ** This template will begin a recursive process that forms a
  ** dependency graph of all targets.
  ************************************************************-->
  <xsl:template match="target" mode="tree">
    <xsl:param name="indentLevel" select="'0'"/>
    <xsl:variable name="curName" select="@name"/>
    <div style="text-indent: {$indentLevel}em;">
      <xsl:value-of select="$curName"/>

      <!-- if the 'depends' attribute is present, show the
           list of dependencies -->
      <xsl:if test="@depends">
        <xsl:text> (depends on </xsl:text>
        <xsl:call-template name="parseDepends">
          <xsl:with-param name="depends" select="@depends"/>
        </xsl:call-template>
        <xsl:text>)</xsl:text>
      </xsl:if>
    </div>

    <!-- set up the indentation -->
    <xsl:variable name="nextLevel" select="$indentLevel+1"/>

    <!-- search all other <target> elements that have "depends"
         attributes -->
    <xsl:for-each select="../target[@depends]">

      <!-- Take the comma-separated list of dependencies and
           "clean it up". See the comments for the "fixDependency"
           template -->
      <xsl:variable name="correctedDependency">
        <xsl:call-template name="fixDependency">
          <xsl:with-param name="depends" select="@depends"/>
        </xsl:call-template>
      </xsl:variable>

      <!-- Now the dependency list is pipe (|) delimited, making
           it easier to reliably search for substrings. Recursively
           instantiate this template for all targets that depend
           on the current target -->
      <xsl:if test="contains($correctedDependency,concat('|',$curName,'|'))">
        <xsl:apply-templates select="." mode="tree">
          <xsl:with-param name="indentLevel" select="$nextLevel"/>
        </xsl:apply-templates>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <!--
  ***************************************************************
  ** This template takes a comma-separated list of dependencies
  ** and converts all commas to pipe (|) characters. It also
  ** removes all spaces. For instance:
  **
  ** Input: depends="a, b,c "
  ** Ouput: |a|b|c|
  **
  ** The resulting text is much easier to parse with XSLT.
  ************************************************************-->
  <xsl:template name="fixDependency">
    <xsl:param name="depends"/>

    <!-- grab everything before the first comma,
         or the entire string if there are no commas -->
    <xsl:variable name="firstToken">
      <xsl:choose>
        <xsl:when test="contains($depends, ',')">
          <xsl:value-of
            select="normalize-space(substring-before($depends, ','))"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="normalize-space($depends)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- define a variable that contains everything after the
         first comma -->
    <xsl:variable name="remainingTokens"
      select="normalize-space(substring-after($depends, ','))"/>

    <xsl:text>|</xsl:text>
    <xsl:value-of select="$firstToken"/>
    <xsl:choose>
      <xsl:when test="$remainingTokens">
        <xsl:call-template name="fixDependency">
          <xsl:with-param name="depends" select="$remainingTokens"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>|</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

3.6.3.1. Specifying XHTML output

One of the first things this stylesheet does is set the output method to "xml" because the resulting page will be XHTML instead of HTML. The doctype-public and doctype-system are required for valid XHTML and indicate the strict DTD in this case:

<xsl:output method="xml"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
    indent="yes" encoding="UTF-8"/>

The remaining XHTML requirement is to declare the namespace of the <html> element:

<xsl:template match="/">
  <html xmlns="http://www.w3.org/1999/xhtml">
    ...
  </html>
</xsl:template>

Because of these XSLT elements, the result tree will contain the following XHTML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
  "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  ...
</html>

3.6.3.2. Creating the dependency graph

The most interesting and difficult aspect of this stylesheet is its ability to display the complete dependency graph for all Ant build targets. The first step is to locate all of the targets that do not have any dependencies. As shown in Example 3-13, these targets are named clean, prepare, and targets for the Tomcat build file. They are selected by looking for <target> elements that do not have an attribute named depends:

<!-- show all target dependencies as a tree -->
<h3>Target Dependency Tree</h3>
<xsl:apply-templates select="target[not(@depends)]" mode="tree">
  <xsl:sort select="@name"/>
</xsl:apply-templates>

The [not(@depends)] predicate will refine the list of <target> elements to include only those that do not have an attribute named depends. The <xsl:apply-templates> will instantiate the following template without any parameters:

<xsl:template match="target" mode="tree">
  <xsl:param name="indentLevel" select="'0'"/>
  <xsl:variable name="curName" select="@name"/>

If you refer to Example 3-14, you will see that this is the second-to-last template in the stylesheet. Since it is broken up into many pieces here, you may find it easier to refer to the original code as this description progresses. Since the indentLevel parameter is not specified, it defaults to '0', which makes sense for the top-level targets. As this template is instantiated recursively, the level of indentation increases. The curName variable is local to this template and contains the current Ant target name. Lines of text are indented using a style attribute:

<div style="text-indent: {$indentLevel}em;">

CSS is used to indent everything contained within the <div> tag by the specified number of em s.[13] The value of the current target name is then printed using the appropriate indentation:

[13] An em is approximately equal to the width of a lowercase letter "m" in the current font.

<xsl:value-of select="$curName"/>

If the current <target> element in the Ant build file has a depends attribute, its dependencies are printed next to the target name as part of the report. The parseDepends template handles this task. This template, also part of Example 3-14, is instantiated using <xsl:call-template>, as shown here:

<xsl:if test="@depends">
  <xsl:text> (depends on </xsl:text>
  <xsl:call-template name="parseDepends">
    <xsl:with-param name="depends" select="@depends"/>
  </xsl:call-template>
  <xsl:text>)</xsl:text>
</xsl:if>

To continue with the dependency graph, the target template must instantiate itself recursively. Before doing this, the indentation must be increased. Since XSLT does not allow variables to be modified, a new variable is created:

<xsl:variable name="nextLevel" select="$indentLevel+1"/>

When the template is recursively instantiated, nextLevel will be passed as the value for the indentLevel parameter:

<xsl:apply-templates select="." mode="tree">
  <xsl:with-param name="indentLevel" select="$nextLevel"/>
</xsl:apply-templates>

The remainder of the template is not duplicated here, but is emphasized in Example 3-14. The basic algorithm is as follows:

  • Use <xsl:for-each> to select all targets that have dependencies.

  • Instantiate the "fixDependency" template to replace commas with | characters.

  • Recursively instantiate the "target" template for all targets that depend on the current target.

3.6.3.3. Cleaning up dependency lists

The final template in the Antdoc stylesheet is responsible for tokenizing a comma-separated list of dependencies, inserting pipe (|) characters between each dependency:

<xsl:template name="fixDependency">
  <xsl:param name="depends"/>

The depends parameter may contain text such as "a, b, c." The template tokenizes this text, producing the following output:

|a|b|c|

Since XSLT does not have an equivalent to Java's StringTokenizer class, recursion is required once again. The technique is to process the text before the first comma then recursively process everything after the comma. The following code assigns everything before the first comma to the firstToken variable:

<xsl:variable name="firstToken">
  <xsl:choose>
    <xsl:when test="contains($depends, ',')">
      <xsl:value-of
        select="normalize-space(substring-before($depends, ','))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="normalize-space($depends)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

If the depends parameter contains a comma, the substring-before( ) function locates the text before the comma, and normalize-space( ) trims whitespace. If no commas are found, there must be only one dependency.

Next, any text after the first comma is assigned to the remainingTokens variable. If there are no commas, the remainingTokens variable will contain an empty string:

<xsl:variable name="remainingTokens"
  select="normalize-space(substring-after($depends, ','))"/>

The template then outputs a pipe character followed by the value of the first token:

<xsl:text>|</xsl:text>
<xsl:value-of select="$firstToken"/>

Next, if the remainingTokens variable is nonempty, the fixDependency template is instantiated recursively. Otherwise, another pipe character is output at the end:

<xsl:choose>
  <xsl:when test="$remainingTokens">
    <xsl:call-template name="fixDependency">
      <xsl:with-param name="depends" select="$remainingTokens"/>
    </xsl:call-template>
  </xsl:when>
  <xsl:otherwise>
    <xsl:text>|</xsl:text>
  </xsl:otherwise>
</xsl:choose>

Ideally, these descriptions will help clarify some of the more complex aspects of this stylesheet. The only way to really learn how this all works is to experiment, changing parts of the XSLT stylesheet and then viewing the results in a web browser. You should also make use of a command-line XSLT processor and view the results in a text editor. This is important because browsers may skip over tags they do not understand, so you might not see mistakes until you view the source.

Previous | Home | Next
3.5. Schema Evolution | Book Index | 4. Java-Based Web Technologies
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Java-Based Web Technologies (Java and XSLT)

 Java and XSLTSearch this book

Chapter 4. Java-Based Web Technologies

Contents:

Traditional Approaches
The Universal Design
XSLT and EJB
Summary of Key Approaches

In a perfect world, a single web development technology would be inexpensive, easy to maintain, offer rapid response time, and be highly scalable. It would also be portable to any operating system or hardware platform and would adapt well to future requirement changes. It would support access from wireless devices, standalone client applications, and web browsers, all with minimal changes to code.

No perfect solution exists, nor is one likely to exist anytime soon. If it did, many of us would be out of work. A big part of software engineering is recognizing that tradeoffs are inevitable and knowing when to sacrifice one set of goals in order to deliver the maximum value to your customer or business. For example, far too many programmers focus on raw performance metrics without any consideration for ease of development or maintainability by nonexperts. These decisions are hard and are often subjective, based on individual experience and preferences.

The goal of this chapter is to look at the highlights of several popular technologies for web application development using Java and see how each measures up to an XSLT-based approach. The focus is on architecture, which implies a high-level viewpoint without emphasis on specific implementation details. Although XSLT offers a good balance between performance, maintainability, and flexibility, it is not the right solution for all applications. It is hoped that the comparisons made here will help you decide if XSLT is the right choice for your web applications.

4.1. Traditional Approaches

Before delving into more sophisticated options, let's step back and look at a few basic approaches to web development using Java. For small web applications or moderately dynamic web sites, these approaches may be sufficient. As you might suspect, however, none of these approaches hold up as well as XML and XSLT when your sites get more complex.

4.1.1. CGI

Common Gateway Interface (CGI) is a protocol for interfacing external applications, which can be written in just about any language, with web servers. The most common language choices for CGI are C and Perl. This interface is accomplished in a number of ways, depending on the type of request. For example, parameters associated with an HTTP GET request are passed to the CGI script via the QUERY_STRING environment variable. HTTP POST data, on the other hand, is piped to the standard input stream of the CGI script. CGI always sends results back to the web server via its standard output.

Ordinary CGI programs are invoked from the web server as external programs, which is the most notable difference when compared with servlets. With each request from the browser, the web server spawns a new process to run the CGI program. Aside from the obvious performance penalty, this also makes it difficult to maintain state information between requests. A web-based shopping cart is a perfect example of state information that must be preserved between requests. Figure 4-1 illustrates the CGI process.

Figure 4-1. CGI process

NOTE: FastCGI is an alternative to CGI with two notable differences. First, FastCGI processes do not exit with each request/response cycle. Second, the environment variable and pipe I/O mechanism of CGI has been eschewed in favor of TCP connections, allowing FastCGI programs to be distributed to different servers. The net result is that FastCGI eliminates the most vexing problems of CGI while making it easy to salvage existing CGI programs.

Although technically possible, using Java for CGI programming is not generally a good idea. In fact, it is an awful idea! The Java Virtual Machine (JVM) would have to be launched with each and every request, which would be painfully slow. Any Java programmer knows that application startup time has never been one of the strengths of Java. Servlets had to address this issue first. What was needed was a new approach in which the JVM was loaded a single time and left running even when no requests came in. The term servlet engine referred to the JVM that hosted the servlets, often serving a dual role as an HTTP web server.

4.1.2. Servlets as CGI Replacements

Sun's Java servlet API was originally released way back in 1997 when Java was mostly a client-side development language. Servlets were originally marketed and used as replacements for CGI programs. Developers were quick to adopt servlets because of their advantages over CGI.

Since the servlet engine can run for as long as the web server runs, servlets can be loaded into memory once and kept around for subsequent requests. This is easy to accomplish in Java because servlets are really nothing more than Java classes. The JVM simply loads the servlet objects into memory, hanging on to the references for as long as the web application runs.

The persistent nature of servlets results in two additional benefits, both of which push servlets well beyond the capabilities of basic CGI. First, state information can be preserved in memory for long periods of time. Even though the browser loses its connection to the web server after each request/response cycle, servlets can store objects in memory until the browser reconnects for the next page. Secondly, since Java has built-in threading capability, it is possible for numerous clients to share the same servlet instance. Creating additional threads is far more efficient than spawning additional external processes, making servlets very good performers.

Early versions of the Java servlet API did not specify the mechanism for deployment (i.e., installation) onto servers. Although the servlet API was consistent, deployment onto different servlet engines was completely vendor specific. With Version 2.2 of the servlet API, however, proprietary servlet engines were dropped in favor of a generic servlet container specification. The idea of a container is to formalize the relationship between a servlet and the environment in which it resides. This made it possible to deploy the same servlet on any vendor's container without any changes.

Along with the servlet container came the concept of a web application. A web application consists of a collection of servlets, static web pages, images, or any other resources that may be needed. The standard unit of deployment for web applications is the Web Application Archive (WAR) file, which is actually just a Java Application Archive (JAR) file that uses a standard directory structure and has a .war file extension. In fact, you use the jar command to create WAR files. Along with the WAR file comes a deployment descriptor, which is an XML configuration file that specifies all configuration aspects of a web application. The important details of WAR files and deployment descriptors will be outlined in Chapter 6, "Servlet Basics and XSLT".

Servlets are simple to implement, portable, can be deployed to any servlet container in a consistent way, and offer high performance. Because of these advantages, servlets are the underlying technology for every other approach discussed in this chapter. When used in isolation, however, servlets do have limitations. These limitations manifest themselves as web applications grow increasingly complex and web pages become more sophisticated.

The screen shot shown in Figure 4-2 shows a simple web page that lists television shows for the current day. In this first implementation, a servlet is used. It will be followed with a JavaServer Pages (JSP) implementation presented later in this chapter.

Figure 4-2. ScheduleServlet output

The Schedule Java class has a method called getTodaysShows( ), that returns an array of Show objects. The array is already sorted, which reduces the amount of work that the servlet has to do to generate this page. The Schedule and Show classes are used for all of the remaining examples in this chapter. Ideally, this will help demonstrate that no matter which approach you take, keeping business logic and database access code out of the servlet makes it easier to move to new technologies without rewriting all of your code. The code for ScheduleServlet.java is shown in Example 4-1. This is typical of a first-generation servlet, generating its output using a series of println( ) statements.

Example 4-1. ScheduleServlet.java

package chap4;

import java.io.*;
import java.text.SimpleDateFormat;
import javax.servlet.*;
import javax.servlet.http.*;

public class ScheduleServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {

        SimpleDateFormat dateFmt = new SimpleDateFormat("hh:mm a");

        Show[] shows = Schedule.getInstance().getTodaysShows( );

        response.setContentType("text/html");
        PrintWriter pw = response.getWriter( );
        pw.println("<html><head><title>Today's Shows</title></head><body>");
        pw.println("<h1>Today's Shows</h1>");
        pw.println("<table border=\"1\" cellpadding=\"3\"");
        pw.println(" cellspacing=\"0\">");

        pw.println("<tr><th>Channel</th><th>From</th>");
        pw.println("<th>To</th><th>Title</th></tr>");

        for (int i=0; i<shows.length; i++) {
            pw.println("<tr>");
            pw.print("<td>");
            pw.print(shows[i].getChannel( ));
            pw.println("</td>");
            pw.print("<td>");
            pw.print(dateFmt.format(shows[i].getStartTime( )));
            pw.println("</td>");
            pw.print("<td>");
            pw.print(dateFmt.format(shows[i].getEndTime( )));
            pw.println("</td>");
            pw.print("<td>");
            pw.print(shows[i].getTitle( ));
            pw.println("</td>");
            pw.println("</tr>");
        }
        pw.println("</table>");
        pw.println("</body>");
        pw.println("</html>");
    }
}

If you are interested in the details of servlet coding, be sure to read Chapter 6, "Servlet Basics and XSLT". For now, focus on how the HTML is generated. All of those println( ) statements look innocuous enough in this short example, but a "real" web page will have thousands of println( ) statements, resulting in code that is quite difficult to maintain over the years. Generally, you will want to factor that code out into a series of methods or objects that generate fragments of the HTML. However, this approach is still tedious and error prone.

The main problems are development scalability and future maintainability. The code becomes increasingly difficult to write as your pages get more complex, and it becomes very difficult to make changes to the HTML when new requirements arrive. Web content authors and graphic designers are all but locked out of the process since it takes a programmer to create and modify the code. Each minor change requires your programming staff to recompile, test, and deploy to the servlet container.

Beyond the tedious nature of HTML generation, first-generation servlets tend to do too much. It is not clear where error handling, form processing, business logic, and HTML generation are supposed to reside. Although we are able to leverage two helper classes to generate the list of shows, a more rigorous approach will be required for complex web applications. All of the remaining technologies presented in this chapter are designed to address one or more of these issues, which become increasingly important as web applications get more sophisticated.

4.1.3. JSP

You have no doubt heard about JSP. This is a hot area in web development right now with some pretty hefty claims about productivity improvements. The argument is simple: instead of embedding HTML code into Java servlets, which requires a Java programmer, why not start out with static HTML? Then add special tags to this HTML that are dynamically expanded by the JSP engine, thus producing a dynamic web page. Example 4-2 contains a very simple example of JSP that produces exactly the same output as ScheduleServlet.

Example 4-2. schedule.jsp

<%@ page import="chap4.*,java.text.*" %>
<%! SimpleDateFormat dateFmt = new SimpleDateFormat("hh:mm a"); %>
<html>
  <head>
    <title>Today's Shows</title>
  </head>
<body>
<h1>Today's Shows</h1>
<% Show[] shows = Schedule.getInstance().getTodaysShows( ); %>
<table border="1" cellpadding="3" cellspacing="0">
  <tr><th>Channel</th><th>From</th><th>To</th><th>Title</th></tr>

  <% for (int i=0; i<shows.length; i++) { %>
  <tr>
    <td><%= shows[i].getChannel( ) %></td>
    <td><%= dateFmt.format(shows[i].getStartTime( )) %></td>
    <td><%= dateFmt.format(shows[i].getEndTime( )) %></td>
    <td><%= shows[i].getTitle( ) %></td>
  </tr>
  <% } %>
</table>
</body>
</html>

As schedule.jsp shows, most of the JSP is static HTML with dynamic content sprinkled in here and there using special JSP tags. When a client first requests a JSP, the entire page is translated into source code for a servlet. This generated servlet code is then compiled and loaded into memory for use by subsequent requests. During the translation process, JSP tags are replaced with dynamic content, so the end user only sees the HTML output as if the entire page was static.

Runtime performance of JSP is comparable to hand-coded servlets because the static content in the JSP is generally replaced with a series of println( ) statements in the generated servlet code. The only major performance hit occurs for the first person to visit the JSP, because it will have to be translated and compiled. Most JSP containers provide options to precompile the JSP, so even this hit can be avoided.

Debugging in JSP can be somewhat challenging. Since JSP pages are machine translated into Java classes, method signatures and class names are not always intuitive. When a programming error occurs, you are often faced with ugly stack traces that show up directly in the browser. You do have the option of specifying an error page to be displayed whenever an unexpected condition occurs. This gives the end user a more friendly error message, but does little to help you diagnose the problem.

Here is a portion of what Apache's Tomcat shows in the web browser when the closing curly brace (}) is accidentally omitted from the loop shown in the JSP example:

A Servlet Exception Has Occurred
org.apache.jasper.JasperException: Unable to compile class for
JSP..\work\localhost\chap4\_0002fschedule_0002ejspschedule_jsp_2.java:104:
'catch' without 'try'.
        } catch (Throwable t) {
          ^
..\work\localhost\chap4\_0002fschedule_0002ejspschedule_jsp_2.java:112:
'try' without 'catch' or 'finally'.
}
^
..\work\localhost\chap4\_0002fschedule_0002ejspschedule_jsp_2.java:112:
'}' expected.
}
 ^
3 errors

at org.apache.jasper.compiler.Compiler.compile(Compiler.java:294)
at org.apache.jasper.servlet.JspServlet.doLoadJSP(JspServlet.java:478)
...remainder of stack trace omitted

The remainder of the stack trace is not very helpful because it simply lists methods that are internal to Tomcat. _0002fschedule_0002ejspschedule_jsp_2 is the name of the Java servlet class that was generated. The line numbers refer to positions in this generated code, rather than in the JSP itself.

Embedding HTML directly into servlets is not appealing because it requires a programmer to maintain. With JSP, you often embed Java code into HTML. Although the embedding is reversed, you still have not cleanly separated HTML generation and programming logic. Think about the problems you encounter when the validation logic in a JSP goes beyond a simple one-page example. Do you really want hundreds of lines of Java code sprinkled throughout your HTML, surrounded by those pretty <% %> tags? Unfortunately, far too many JSP pages have a substantial amount of Java code embedded directly in the HTML.

The first few iterations of JSP did not offer bulletproof approaches for separating Java code from the HTML. Although JavaBeans tags were offered in an attempt to remove some Java code, the level of sophistication was quite limited. These tags allow JSPs to interact with helper classes written according to Sun's JavaBeans API (http://java.sun.com/products/javabeans). Recent trends in the JSP specification have made substantial improvements. The big push right now is for custom tags,[14] which finally allow you to remove the Java code from your pages. A web page with custom tags may look like Example 4-3.

[14] Technically, programmers create custom actions, which are invoked using custom JSP tags.

Example 4-3. JSP with custom tags

<%@ taglib uri="/my_taglib" prefix="abc" %>
<html>
<head>
<title>JSP Tag Library Demonstration</title>
</head>
<body>
  <abc:standardHeader/>
  <abc:companyLogo/>

  <h1>Recent Announcements</h1>
  <abc:announcements filter="recent"/>

  <h1>Job Openings</h1>
  <abc:jobOpenings department="hr"/>
  <abc:standardFooter/>
</body>
</html>

As you can see, custom tags look like normal XML tags with a namespace prefix . Namespace prefixes are used to give XML tags unique names. Because you select the prefix for each tag library, you can use libraries from many different vendors without fear of naming conflicts. These tags are mapped to Java classes called tag handlers that are responsible for the actual work. In fact, the JSP specification does not limit the underlying implementation to Java, so other languages can be used if the JSP container supports it. Using the custom tag approach, programmers in your company can produce a set of approved tags for creating corporate logos, search boxes, navigation bars, and page footers. Nonprogrammers can focus on HTML layout, oblivious to the underlying tag handler code. The main drawback to this approach is the current lack of standard tags. Although several open source projects are underway to develop custom tag libraries, it is unlikely that you will be able to find an existing custom tag for every requirement.

One persistent problem with a pure JSP approach is that of complex validation. Although JSP with custom tags can be an ideal approach for displaying pages, the approach falls apart when a JSP is used to validate the input from a complex HTML form. In this situation, it is almost inevitable that Java code -- perhaps a lot of it -- will creep into the page. This is where a hybrid approach (JSP and servlets), which will be covered in the next section, is desirable.

Compared with an XML/XSLT approach, JSP requires a lot more effort to cleanly separate presentation from the underlying data and programming logic. For web sites that are mostly static, JSP can be easy for nonprogrammers to create, since they work directly in HTML. When dynamic content becomes more prevalent, your options are to embed lots of Java code into the JSP, create custom tags, or perhaps write Java beans that output fragments of HTML. Embedding code into the JSP is not desirable because of the ugly syntax and maintenance difficulties. The other approaches do hide code from the JSP author, but some part of your web application (to be consistent) is still cranking out HTML from Java code, either in custom tags or JavaBeans components. This still raises serious questions about the ability to make quick changes to your HTML without recompiling and deploying your Java code.

Another weakness of JSPs in comparison with XML and XSLT becomes obvious when you try to test your web application. With JSP, it is virtually impossible to test your code outside the bounds of a web browser and servlet container. In order to write a simple automated unit test against a JSP, you have to start a web server and invoke your JSPs via HTTP requests. With XML and XSLT, on the other hand, you can programmatically generate the XML data without a web browser or server. This XML can then be validated against a DTD or schema. You can also test the XSLT stylesheets using command-line tools without deploying to a servlet container or starting a web server. The result of the transformation can even be validated again with a DTD if you use XHTML instead of HTML.

4.1.4. Template Engines

Before moving on, let's discuss template engines. A quick search on the Internet reveals that template engines are abundant, each claiming to be better than JSP for various reasons. For the most part, template engines have a lot in common with JSP, particularly if you restrict yourself to custom tags. There are some differences, however:

  • Template engines typically forbid you from embedding Java code into pages. Although JSP allows Java code along with HTML, it is not considered good form.

  • Most template engines are not compiled, so they do not have the same problems that JSP has with error messages. They also start up faster on the first invocation, which can make development easier. The effect on end users is minimal. From a deployment perspective, you do not need a Java compiler on the web server as you do with JSP.

  • Template engines come with an existing library of tags or simple scripting languages. JSP does not provide any standard tags, although numerous libraries are available from other vendors and open source projects. The JSP API is open, so you can create your own custom tags with a fair amount of effort. Template engines have their own unique mechanisms for integrating with underlying Java code.

  • JSP has the backing of Sun and is pretty much available out of the box on any servlet container. The main benefit of a "standard" is the wide availability of documentation, knowledgeable people, and examples. There are many implementations of JSP to choose from.

4.1.5. The Hybrid Approach

Since JSP now has custom tags, you can remove (hide, actually) all of the Java code when "rendering," or generating a page to send to the browser. When a complex HTML form is posted to the JSP, however, you still have problems. You must verify that all fields are present, verify that the data is within bounds, and clean up the data by checking for null values and trimming all strings. Validation is not particularly difficult, but it can be tedious and requires a lot of custom code. You do not want to embed that code directly into a JSP because of the debugging and maintenance issues.

The solution is a hybrid approach, in which a servlet works in conjunction with a JSP. The servlet API has a nice class called RequestDispatcher that allows server-side forwarding and including. This is the normal mechanism for interaction between the servlet and JSP. Figure 4-3 illustrates this design at a high level.

Figure 4-3. Hybrid JSP/servlet approach

This approach combines the best features of servlets with the best features of JSPs. The arrows indicate the flow of control whenever the browser issues a request. The job of the servlet is to intercept the request, validate that the form data is correct, and delegate control to an appropriate JSP. Delegation occurs via javax.servlet.RequestDispatcher, which is a standard part of the servlet API. The JSP simply renders the page, ideally using custom tags and no Java code mixed with the HTML.

The main issue with this approach becomes evident when your web site begins to grow beyond a few pages. You must make a decision between one large servlet that intercepts all requests, a separate servlet per page, or helper classes responsible for processing individual pages. This is not a difficult technological challenge, but rather a problem of organization and consistency. This is where web frameworks can lend a helping hand.

Previous | Home | Next
3.6. Ant Documentation Stylesheet | Book Index | 4.2. The Universal Design
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Universal Design (Java and XSLT)

 Java and XSLTSearch this book

4.2. The Universal Design

Despite the proliferation of APIs, frameworks, and template engines, most web application approaches seem to be consolidating around the idea of model-view-controller (MVC). Clean separation between data, presentation, and programming logic is a key goal of this design. Most web frameworks implement this pattern, and the hybrid approach of JSP and servlets follows it. XSLT implementations also use this pattern, which leads to the conclusion that model-view-controller is truly a universal approach to development on the web tier.

4.2.1. Web Frameworks

A framework is a value-added class library that makes it easier to develop certain types of applications. For example, an imaging framework may contain APIs for reading, writing, and displaying several image formats. This makes it much easier to build applications because someone else already figured out how to structure your application.

Servlet frameworks are no different. Now that servlets, JSP, and hybrid approaches have been available for a few years, common architectural patterns are emerging as "best practices." These include separation of Java code and HTML generation, using servlets in conjunction with JSP, and other variations. Once basic patterns and themes are understood, it becomes desirable to write common frameworks that automate the mundane tasks of building web applications.

The most important tradeoff you make when selecting a framework is vendor lock-in versus open standards. At this time, there are no open standards for frameworks. Although there are numerous open source frameworks, none is backed by a standards organization or even Sun's Java Community Process. The low-level servlet and JSP APIs are very well defined and widely implemented Java standard extensions. But a framework can offer much more sophisticated features such as enhanced error checking, database connection pooling, custom tag libraries, and other value-added features. As you add more framework-specific features, however, your flexibility to choose another framework or vendor quickly diminishes.

One typical framework is Turbine, which is one of many different frameworks supported by Apache. Turbine is a large framework with many value-added features including:

  • Database connection pooling, integration with object to relational mapping tools, and relational database abstractions

  • Integration with numerous template engines

  • Role-based security and access control lists

  • Web browser detection

  • Integration with JavaMail

This is only a short list of Turbine's features. At its core, however, the compelling reason to use a framework like Turbine is the underlying object model. The fundamental approach of Turbine is to cleanly separate validation logic, the servlet itself, and page rendering into distinctly different modules. In fact, Turbine uses a single servlet, so your validation and rendering logic have to go elsewhere. The approach is to define helper classes called actions, which are responsible for validation of incoming requests. Once an action has validated the inbound request, other classes such as Layout, Page, and Navigation are responsible for rendering a view back to the browser.

When compared to a pure XML/XSLT approach, frameworks have the advantage of value-added features. If you remove all of the non-web features, such as database connection pooling and object-to-relational mapping tools, you will see that the underlying model-view-controller architecture is very easy to implement. You should be wary of any framework that provides too much non-web-related functionality because many of these features should be placed on the application server instead of the web server anyway. The remainder of this chapter is devoted to showing you how to structure a complex web application without committing yourself to a specific framework.

4.2.2. Model-View-Controller

Cleanly separating data and presentation logic is important. What exactly are the benefits? First and foremost, when data is completely isolated from the user interface, changes can be made to the visual appearance of an application without affecting the underlying data. This is particularly important in web applications that have to support multiple incompatible browsers or even WML, XHTML Basic, or HTML. It is much harder to adapt to new user interface requirements when data and presentation are mixed.

Programming logic should also be separated from data and presentation logic. To a certain extent, programming logic must depend in part on both data and presentation. But you can generally isolate business logic, which depends on the data, and presentation logic, which depends on the user interface. Figure 4-4 illustrates these dependencies.

Figure 4-4. Dependencies

The arrows indicate dependencies. For example, if your underlying data changes, then the business logic will probably have to change. However, that does not always flow up and break your presentation logic. In general, if changes are sweeping, it is hard to avoid affecting upper layers, but minor changes can almost always be encapsulated. If the implementation of your business logic changes, however, there is no reason to change the underlying data. Likewise, you should be able to make changes to the presentation logic without breaking the business logic. Later in this chapter, we will see how Java, XML, and XSLT can be utilized to satisfy these dependencies.

The dominant pattern in scalable web sites is model-view-controller. The MVC pattern originated with Smalltalk-80 as a way to develop graphical user interfaces in an object-oriented way. The basics are simple. GUI components represent the view and are responsible for displaying visual information to the user. The model contains application data. The controller is responsible for coordinating between the model and the view. It intercepts events from the view components, queries the model for its current state, makes modifications to the model, and notifies the view of changes to the model. Figure 4-5 illustrates the interaction between these three components.

Figure 4-5. Model-view-controller

As shown, the Model, View, and Controller are either abstract classes or interfaces. The concrete classes are application-specific, and the open arrows indicate the direction of association between the various classes. For example, the abstract Model sends notifications only to the abstract View, but ConcreteView knows about its ConcreteModel. This makes sense when you consider how hard it would be to create a specific view, such as a customer editor panel, without knowledge of a specific data model like Customer. Since the Model only knows about View instances in an abstract way, however, it can send generic notifications when it changes, allowing new views to be attached later.

It is important to remember that this is just a pattern; specific implementations may vary somewhat and use different class names. One variation is to eliminate the explicit references from ConcreteView to ConcreteModel and from Model to View. In this approach, the Controller would take a more prevalent role. A common theme in Java is to remove the explicit controller using data models and view components that send notifications to event listeners. Although typically thought of in terms of GUI applications, the MVC architecture is not limited to this domain. For web applications, it is commonly used in:

  • The hybrid servlet + JSP approach

  • Most servlet frameworks

  • The XSLT approach

In the hybrid approach, the servlet is the controller and the JSP is the view. It is assumed that the data will be retrieved from a database or Enterprise JavaBeans (EJB) components, which act as the model. A good framework may make the distinction between model, view, and controller more explicit. Instead of using the servlet as a controller, a common pattern is to use a single servlet that delegates work to helper classes that act as controllers. Each of these classes is equivalent to ConcreteController in Figure 4-5 and has knowledge of specific web pages and data.

Although originally intended for Smalltalk GUIs, MVC has always been one of the most frequently used patterns in all sorts of GUIs, from Motif to Java. On the web, MVC is also prevalent, although a few mechanics are slightly different. In a web environment, we are restricted to the HTTP protocol, which is stateless . With each click of a hyperlink, the browser must establish a new connection to the web server. Once the page has been delivered, the connection is broken. It is impossible for the server to initiate a conversation with the client, so the server merely waits until the next request arrives.

Implementing MVC in this stateless architecture results in looser coupling between the controller and the view. In a GUI environment, the controller immediately notifies the view of any changes to the underlying model. In a web environment, the controller must maintain state information as it waits for the browser to make another request. As each browser request arrives, it is the controller's job to validate the request and forward commands on to the model. The controller then sends the results back to the view.

This may all sound academic and vague at this point. The next few sections will present much more detailed diagrams that show exactly how MVC is implemented for an XSLT-driven web site.

4.2.3. XSLT Implementation

All of the approaches presented up to this point are, of course, building up to the XSLT approach. In many respects, the XSLT approach is simultaneously the most powerful and the easiest to understand. For a single web page, the XSLT approach is probably harder than a servlet or JSP to configure. Configuration of the XML parser and XSLT processor can be quite difficult, mostly due to CLASSPATH issues.[15] But as the complexity of a web application increases, the benefits of using XSLT become obvious. Figuring out how to tackle these complex web applications is the real goal of this chapter.

[15] This can be a frustrating experience when the servlet container comes with an older XML parser that uses DOM or SAX Version 1. Most XSLT processors require Version 2 parsers.

The XSLT approach maps fairly directly to the MVC pattern. The XML represents the model, the servlet represents the controller, and the XSLT produces HTML, which represents the view. The XSLT stylesheets may contain a minimal amount of logic, potentially blurring the line between view and controller. Figure 4-6 represents a conceptual view of how the XSLT approach maps to MVC.

Figure 4-6. XSLT conceptual model

One weakness common to every approach other than XSLT is the HTML-centric viewpoint. In every example presented thus far, it was assumed that we generated HTML. What happens when the requirement to support cellular phones arises? It is very likely that these devices will not use HTML. Instead, they will use WML, XHTML Basic, or some other technology that has not been invented yet. For now, consider that you would have to write brand new servlets or JSPs to support these devices when using traditional approaches. Any programming logic embedded into JSP pages would be duplicated or would have to be factored out into common helper classes. In a pure servlet approach, the hardcoded HTML generation logic would have to be completely rewritten.

XSLT offers an elegant solution -- simply create a second stylesheet. Instead of transforming XML into HTML, this new stylesheet transforms XML into WML. You can even support different web browsers with the XSLT approach. Again, just write different stylesheets for browser-specific functions. Since XSLT stylesheets can import and include functionality from other stylesheets, much of the code can be shared and reused across a project.

Regardless of what your XSLT will produce, start by producing the XML. For the schedule web application, the XML is dynamic and must be programmatically generated. JDOM code is shown in Example 4-4, which produces the XML necessary to create the schedule web page.

Example 4-4. ScheduleJDOM.java

package chap4;

import java.text.SimpleDateFormat;
import org.jdom.*;
import org.jdom.output.*;

/**
 * Produces a JDOM Document for a tv schedule.
 */
public class ScheduleJDOM {
    private SimpleDateFormat dateFmt = new SimpleDateFormat("hh:mm a");

    /**
     * Simple main( ) method for printing the XML document to System.out,
     * useful for testing.
     */
    public static void main(String[] args) throws Exception {
        Document doc = new ScheduleJDOM().getTodaysShows( );
        new XMLOutputter("  ", true, "UTF-8").output(doc, System.out);
    }

    /**
     * @return a new JDOM Document for all TV shows scheduled for today.
     */
    public Document getTodaysShows( ) {
        Schedule sched = Schedule.getInstance( );
        Show[] shows = sched.getTodaysShows( );

        Element rootElem = new Element("schedule");

        for (int i=0; i<shows.length; i++) {
            rootElem.addContent(createShowElement(shows[i]));
        }
        return new Document(rootElem);
    }

    /**
     * A helper method to convert a Show object into a JDOM Element.
     */
    public Element createShowElement(Show show) {
        Element e = new Element("show");
        e.addContent(new Element("channel").setText(
                Integer.toString(show.getChannel( ))));
        e.addContent(new Element("from").setText(
                this.dateFmt.format(show.getStartTime( ))));
        e.addContent(new Element("to").setText(
                this.dateFmt.format(show.getEndTime( ))));
        e.addContent(new Element("title").setText(show.getTitle( )));
        return e;
    }
}

You might be wondering why this JDOM code is that much better than the servlet code, which also used Java to programmatically produce output. The difference is fundamental and important. With this JDOM example, println( ) statements are not used. Instead, a data structure representing the television schedule is created. By virtue of the JDOM API, the data structure is guaranteed to produce well-formed XML. We could very easily add a DTD, writing a unit test that validates the integrity of the generated data structure.

In addition to ensuring the integrity of the data, the JDOM code will typically be much smaller than the servlet or JSP code. In this basic web page, the servlet and JSP were quite small because the HTML did not contain any significant formatting or layout. In a real-world web page, however, the servlet and JSP will continue to grow in complexity as the HTML layout gets more sophisticated, while the JDOM code remains exactly the same.

Although the XSLT stylesheet will get larger as the HTML gets more complex, this is arguably less of a problem because the presentation logic is completely separated from the underlying XML data. Once fully tested, the XSLT can be deployed to the web server without recompiling the Java code or restarting the servlet. The XML data produced by JDOM is shown in Example 4-5.

Example 4-5. XML for schedule web page

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="schedule.xslt"?>
<schedule>
  <show>
    <channel>2</channel>
    <from>06:00 AM</from>
    <to>06:30 AM</to>
    <title>Baseball</title>
  </show>
  <show>
    <channel>3</channel>
    <from>06:00 AM</from>
    <to>08:00 AM</to>
    <title>Stand up Comedy</title>
  </show>
  ...remaining XML omitted
</schedule>

The stylesheet that produces the exact same output as the JSP and servlet is listed in Example 4-6.

Example 4-6. schedule.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>

  <!-- ========== Produce the HTML Document ========== -->
  <xsl:template match="/">
    <html>
      <head><title>Today's Shows</title></head>
      <body>
        <h1>Today's Shows</h1>
        <table cellpadding="3" border="1" cellspacing="0">
          <tr>
            <th>Channel</th>
            <th>From</th>
            <th>To</th>
            <th>Title</th>
          </tr>

          <!-- ===== select the shows ===== -->
          <xsl:apply-templates select="schedule/show"/>
        </table>
      </body>
    </html>
  </xsl:template>

  <!-- ======== Display each show as a row in the table ======== -->
  <xsl:template match="show">
    <tr>
      <td><xsl:value-of select="channel"/></td>
      <td><xsl:value-of select="from"/></td>
      <td><xsl:value-of select="to"/></td>
      <td><xsl:value-of select="title"/></td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

The remaining piece of the puzzle is to write a servlet that combines all of these pieces and delivers the result of the XSLT transformation to the client (see Chapter 6, "Servlet Basics and XSLT"). In a nutshell, the servlet acts as a controller between the various components, doing very little of the actual work. The client request is intercepted by the servlet, which tells ScheduleJDOM to produce the XML data. This XML is then fed into an XSLT processor such as Xalan, along with schedule.xslt. Finally, the output is sent to the browser as HTML, XHTML, WML, or some other format.

NOTE: Another interesting option made possible by this architecture is allowing the client to request raw XML without any kind of XSLT transformation. This allows your web site to support nonbrowser clients that wish to extract meaningful business data in a portable way.

We examined the weaknesses of other approaches, so it is only fair to take a critical look at the XSLT approach. First, XSLT is a new language that developers or web content authors have to learn. Although the syntax is strange, it can be argued that XSLT is easier to learn than a sophisticated programming language like Java. There is resistance on this front, however, which is typical of a new technology that is unfamiliar.

The second potential weakness of the XSLT approach is runtime performance. There is a performance penalty associated with XSLT transformation. Fortunately, there are numerous optimizations that can be applied. The most common involves the caching of stylesheets so they do not have to be parsed with each request. This and other techniques for optimization will be covered in later chapters.

Since XSLT stylesheets are actually XML documents, any available XML editor will work for XSLT. But eventually we should see more and more specialized XSLT editors that hide some of the implementation details for nonprogrammers. As with first-generation Java GUI builders, these early tools may not generate stylesheets as cleanly as a handcoded effort.

4.2.4. Development and Maintenance Benefits of XSLT

As mentioned earlier, testing JSPs can be difficult. Since they can be executed only within a JSP container, automated unit tests must start a web server and invoke the JSP via HTTP requests in order to test their output. The XSLT-based web approach does not suffer from this problem.

Referring back to Figure 4-6, you can see that the data model in an XSLT web application is represented as XML. This XML is generated independently of the servlet container, so a unit test can simply create the XML and validate it against a DTD or XML Schema. Tools such as XML Spy make it easy to create XSLT stylesheets and test them interactively against sample XML files long before they are ever deployed to a servlet container. XML Spy is available from http://www.xmlspy.com. If you are looking for alternatives, a directory of XML tools can be found at http://www.xmlsoftware.com.

The XSLT processor is another piece of the puzzle that is not tied to the servlet in any way. Because the processor is an independent component, additional unit tests can perform transformations by applying the XSLT stylesheets to the XML data, again without any interference from a web server or servlet container. If your stylesheets produce XHTML instead of HTML, the output can be easily validated against one of the W3C DTDs for XHTML. JUnit, an open source unit-testing tool, can be used for all of these tests. It can be downloaded from http://www.junit.org.

Previous | Home | Next
4. Java-Based Web Technologies | Book Index | 4.3. XSLT and EJB
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT and EJB (Java and XSLT)

 Java and XSLTSearch this book

4.3. XSLT and EJB

Now that the options for web tier development have been examined, let's look at how the web tier interacts with other tiers in large enterprise class systems. A typical EJB architecture involves a thin browser client, a servlet-driven web tier, and EJB on an application server tier. Figure 4-7 expands upon the conceptual XSLT model presented earlier.

Figure 4-7. XSLT and EJB architecture

This diagram is much closer to the true physical model of a multitier web application that uses XSLT. The arrows indicate the overall flow of a single request, originating with the client. This client is typically a web browser, but it could be a cell phone or some other device. The client request goes to a single servlet and is handed off to something called RequestHandler. In the pattern outlined here, you create numerous subclasses of RequestHandler. Each subclass is responsible for validation and presentation logic for a small set of related functions. One manageable strategy is to design one subclass of RequestHandler for each web page in the application. Another approach is to create fine-grained request handlers that handle one specific task, which can be beneficial if the same piece of functionality is invoked from many different screens in your application.

The request handler interacts with the application server via EJB components. The normal pattern is to execute commands on session beans, which in turn get their data from entity beans. The internal behavior of the EJB layer is irrelevant to the web tier, however. Once the EJB method call is complete, one or more "data objects" are returned to the web tier. From this point, the data object must be converted to XML.

The conversion to XML can be handled in a few different ways. One common approach is to write methods in the data objects themselves that know how to generate a fragment of XML, or perhaps an entire document. Another approach is to write an XML adapter class for each data object. Instead of embedding the XML generation code into the data object, the adapter class generates the XML. This approach has the advantage of keeping the data objects lightweight and clean, but it does result in additional classes to write. In either approach, it is preferable to return XML as a DOM or JDOM tree, rather than raw XML text. If the XML is returned as raw text, it will have to be parsed right back into memory by the XSLT processor. Returning the XML as a data structure allows the tree to be passed directly to the XSLT processor without the additional parsing step.

Yet another approach is to return XML directly from the EJB components, thus eliminating the intermediate data objects. Chapter 9, "Development Environment, Testing, and Performance" will examine this in detail, primarily from a performance perspective. The main drawback to consider is that XML tends to be very verbose. Sending large-text XML files from the application server to the web server may be less efficient than sending serialized Java objects. You could compress the data, but that would add processor overhead for compression and decompression.

Regardless of how the XML is generated, the final step shown in Figure 4-7 is to pass the XML and stylesheet to the XSLT processor for transformation. The result tree is sent directly to the client, thus fulfilling the request. If the client is a browser, the XSLT stylesheet will probably transform the XML into HTML or XHTML. For a nonbrowser client, however, it is conceivable that the XML data is delivered directly without any XSLT transformation.

4.3.1. Tradeoffs

Scalability is a key motivation for a multitier EJB architecture. In such an architecture, each tier can execute on a different machine. Additional performance gains are possible when multiple servers are clustered on each tier. Another motivating factor is reliability. If one machine fails, a redundant machine can continue processing. When updates are made, new versions of software can be deployed to one machine at a time, preventing long outages. Security is improved by strictly regulating access to the data tier via EJB components.

Yet another motivation for a distributed system is simplicity, although a basic EJB application is far more complex than a simple two-tier application. Yes, distributed systems are complex, but for highly complex applications this approach simplifies your work by dividing independent tasks across tiers. One group of programmers can work on the EJB components, while another works on the request handler classes on the web tier. Yet another group of designers can work on XML and XSLT, while your database expert focuses on the database.

For simple applications, a multitier EJB approach is overkill and will likely harm performance. If your web site serves only a few hundred visitors per day, then eliminating EJB could be much faster because there is no additional application tier to hop through.[16]

[16] Keep in mind that other benefits of EJB, such as security, will be lost.

Previous | Home | Next
4.2. The Universal Design | Book Index | 4.4. Summary of Key Approaches
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary of Key Approaches (Java and XSLT)

 Java and XSLTSearch this book

4.4. Summary of Key Approaches

If separation of HTML from Java code is a goal, then neither a pure servlet nor a pure JSP approach is desirable. Although a hybrid approach does allow a clean separation, you may have to create custom JSP tags to take full advantage of this capability. This approach does not support WML output unless you duplicate all of the HTML generation code. Even though the custom JSP tags hide the Java code from the page author, you still end up with Java code somewhere producing HTML programmatically.

Web frameworks typically build on the hybrid approach, including proprietary value-added features and conveniences. Frameworks have the advantage of defining a consistent way to structure the overall application, which is probably more important in terms of software maintenance than any value-added features. The primary disadvantage of frameworks is that you could be locked into a particular approach and vendor.

The XSLT approach achieves the maximum attainable separation of presentation from underlying data. It also supports multiple browsers and even WML targets. XSLT transformation does incur additional processing load on the web tier. This must be carefully weighed against benefits gained from the modular, clean design that XSLT offers.

Table 4-1 summarizes the strengths and weaknesses of different approaches to Web application development.

Table 4-1. Different web technologies

Technology | Strengths | Weaknesses
Pure servlet | Fastest runtime performance. | Changes to HTML require Java code changes. Hard to maintain complex pages. No separation of data, logic, and presentation.
Pure JSP | Best for pages that are mostly display-only, static HTML with small amounts of dynamic content. Fast runtime performance. | Does not enforce separation of Java code and HTML. Not good for validation of incoming requests. Requires deployment to web server for development and testing.
Hybrid servlet/JSP | Allows greater separation between Java code and HTML than "pure" servlet or JSP approaches. More modular design is easier to maintain for large projects. Fast runtime performance. | Still requires deployment to web server for testing and development. Does not force programmers to keep code out of JSPs. Cannot target multiple client device types as effectively as XSLT.
XSLT | Maximum separation between data, programming logic, and presentation. XML and XSLT can be developed and tested outside of the web server. Maximum modularity improves maintainability. Easy to target multiple client devices and languages via different XSLT stylesheets. | Slowest runtime performance.[17] For pages that are mostly static HTML, XSLT might be harder to write than JSP. Requires an extra step to generate XML.

[17] Once more browsers support XSLT transformation, the server load will be greatly reduced.

Previous | Home | Next
4.3. XSLT and EJB | Book Index | 5. XSLT Processing with Java
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Processing with Java (Java and XSLT)

 Java and XSLTSearch this book

Chapter 5. XSLT Processing with Java

Contents:

A Simple Example
Introduction to JAXP 1.1
Input and Output
Stylesheet Compilation

Since many of the XSLT processors are written in Java, they can be directly invoked from a Java application or servlet. Embedding the processor into a Java application is generally a matter of including one or two JAR files on the CLASSPATH and then invoking the appropriate methods. This chapter shows how to do this, along with a whole host of other programming techniques.

When invoked from the command line, an XSLT processor such as Xalan expects the location of an XML file and an XSLT stylesheet to be passed as parameters. The two files are then parsed into memory using an XML parser such as Xerces or Crimson, and the transformation is performed. But when the XSLT processor is invoked programmatically, you are not limited to using static files. Instead, you can send a precompiled stylesheet and a dynamically generated DOM tree directly to the processor, or even fire SAX events as processor input. A major goal is to eliminate the overhead of parsing, which can dramatically improve performance.

This chapter is devoted to Java and XSLT programming techniques that work for both standalone applications as well as servlets, with a particular emphasis on Sun's Java API for XML Processing (JAXP) API. In Chapter 6, "Servlet Basics and XSLT", we will apply these techniques to servlets, taking into account issues such as concurrency, deployment, and performance.

5.1. A Simple Example

Let's start with perhaps the simplest program that can be written. For this task, we will write a simple Java program that transforms a static XML data file into HTML using an XSLT stylesheet. The key benefit of beginning with a simple program is that it isolates problems with your development environment, particularly CLASSPATH issues, before you move on to more complex tasks.

Two versions of our Java program will be written, one for Xalan and another for SAXON. A JAXP implementation will follow in the next section, showing how the same code can be utilized for many different processors.

CLASSPATH Problems

CLASSPATH problems are a common culprit when your code is not working, particularly with XML-related APIs. Since so many tools now use XML, it is very likely that a few different DOM and SAX implementations reside on your system. Before trying any of the examples in this chapter, you may want to verify that older parsers are not listed on your CLASSPATH.

More subtle problems can occur if an older library resides in the Java 2 optional packages directory. Any JAR file found in the jre/lib/ext directory is automatically available to the JVM without being added to the CLASSPATH. You should look for files such as jaxp.jar and parser.jar, which could contain older, incompatible XML APIs. If you experience problems, remove all JAR files from the optional packages directory.

Unfortunately, you will have to do some detective work to figure out where the JAR files came from. Although Java 2 Version 1.3 introduced enhanced JAR features that included versioning information, most of the JAR files you encounter probably will not utilize this capability.

5.1.1. The Design

The design of this application is pretty simple. A single class contains a main( ) method that performs the transformation. The application requires two arguments: the XML file name followed by the XSLT file name. The results of the transformation are simply written to System.out. We will use the following XML data for our example:

<?xml version="1.0" encoding="UTF-8"?>
<message>Yep, it worked!</message>

The following XSLT stylesheet will be used. It's output method is text, and it simply prints out the contents of the <message> element. In this case, the text will be Yep, it worked!.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="UTF-8"/>

  <!-- simply copy the message to the result tree -->
  <xsl:template match="/">
    <xsl:value-of select="message"/>
  </xsl:template>
</xsl:stylesheet>

Since the filenames are passed as command-line parameters, the application can be used with other XML and XSLT files. You might want to try this out with one of the president examples from Chapter 2, "XSLT Part 1 -- The Basics" and 3.

5.1.2. Xalan 1 Implementation

The complete code for the Xalan implementation is listed in Example 5-1. As comments in the code indicate, this code was developed and tested using Xalan 1.2.2, which is not the most recent XSLT processor from Apache. Fully qualified Java class names, such as org.apache.xalan.xslt.XSLTProcessor, are used for all Xalan-specific code.

NOTE: A Xalan 2 example is not shown here because Xalan 2 is compatible with Sun's JAXP. The JAXP version of this program works with Xalan 2, as well as any other JAXP compatible processor.

Example 5-1. SimpleXalan1.java

package chap5;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import org.xml.sax.SAXException;

/**
 * A simple demo of Xalan 1. This code was originally written using
 * Xalan 1.2.2.  It will not work with Xalan 2.
 */
public class SimpleXalan1 {

    /**
     * Accept two command line arguments: the name of an XML file, and
     * the name of an XSLT stylesheet. The result of the transformation
     * is written to stdout.
     */
    public static void main(String[] args)
            throws MalformedURLException, SAXException {
        if (args.length != 2) {
            System.err.println("Usage:");
            System.err.println("  java " + SimpleXalan1.class.getName( )
                    + " xmlFileName xsltFileName");
            System.exit(1);
        }

        String xmlFileName = args[0];
        String xsltFileName = args[1];

        String xmlSystemId = new File(xmlFileName).toURL().toExternalForm( );
        String xsltSystemId = new File(xsltFileName).toURL().toExternalForm( );

        org.apache.xalan.xslt.XSLTProcessor processor =
                org.apache.xalan.xslt.XSLTProcessorFactory.getProcessor( );

        org.apache.xalan.xslt.XSLTInputSource xmlInputSource =
                new org.apache.xalan.xslt.XSLTInputSource(xmlSystemId);

        org.apache.xalan.xslt.XSLTInputSource xsltInputSource =
                new org.apache.xalan.xslt.XSLTInputSource(xsltSystemId);

        org.apache.xalan.xslt.XSLTResultTarget resultTree =
                new org.apache.xalan.xslt.XSLTResultTarget(System.out);

        processor.process(xmlInputSource, xsltInputSource, resultTree);
    }
}

The code begins with the usual list of imports and the class declaration, followed by a simple check to ensure that two command line arguments are provided. If all is OK, then the XML file name and XSLT file name are converted into system identifier values:

String xmlSystemId = new File(xmlFileName).toURL().toExternalForm( );
String xsltSystemId = new File(xsltFileName).toURL().toExternalForm( );

System identifiers are part of the XML specification and really mean the same thing as a Uniform Resource Identifier (URI). A Uniform Resource Locator (URL) is a specific type of URI and can be used for methods that require system identifiers as parameters. From a Java programming perspective, this means that a platform-specific filename such as C:/data/simple.xml needs to be converted to file:///C:/data/simple.xml before it can be used by most XML APIs. The code shown here does the conversion and will work on Unix, Windows, and other platforms supported by Java. Although you could try to manually prepend the filename with the literal string file:///, that may not result in portable code. The documentation for java.io.File clearly states that its toURL( ) method generates a system-dependent URL, so the results will vary when the same code is executed on a non-Windows platform. In fact, on Windows the code actually produces a nonstandard URL (with a single slash), although it does work within Java programs: file:/C:/data/simple.xml.

Now that we have system identifiers for our two input files, an instance of the XSLT processor is created:

org.apache.xalan.xslt.XSLTProcessor processor =
        org.apache.xalan.xslt.XSLTProcessorFactory.getProcessor( );

XSLTProcessor is an interface, and XSLTProcessorFactory is a factory for creating new instances of classes that implement it. Because Xalan is open source software, it is easy enough to determine that XSLTEngineImpl is the class that implements the XSLTProcessor interface, although you should try to avoid code that depends on the specific implementation.

The next few lines of code create XSLTInputSource objects, one for the XML file and another for the XSLT file:

org.apache.xalan.xslt.XSLTInputSource xmlInputSource =
        new org.apache.xalan.xslt.XSLTInputSource(xmlSystemId);

org.apache.xalan.xslt.XSLTInputSource xsltInputSource =
        new org.apache.xalan.xslt.XSLTInputSource(xsltSystemId);

XSLTInputSource is a subclass of org.xml.sax.InputSource, adding the ability to read directly from a DOM Node. XSLTInputSource has the ability to read XML or XSLT data from a system ID, java.io.InputStream, java.io.Reader, org.w3c.dom.Node, or an existing InputSource. As shown in the code, the source of the data is specified in the constructor. XSLTInputSource also has a no-arg constructor, along with get/set methods for each of the supported data source types.

An instance of XSLTResultTarget is created next, sending the result of the transformation to System.out:

org.apache.xalan.xslt.XSLTResultTarget resultTree =
        new org.apache.xalan.xslt.XSLTResultTarget(System.out);

In a manner similar to XSLTInputSource, the XSLTResultTarget can also be wrapped around an instance of org.w3c.dom.Node, an OutputStream or Writer, a filename (not a system ID!), or an instance of org.xml.sax.DocumentHandler.

The final line of code simply instructs the processor to perform the transformation:

processor.process(xmlInputSource, xsltInputSource, resultTree);

5.1.3. SAXON Implementation

For comparison, a SAXON 5.5.1 implementation is presented in Example 5-2. As you scan through the code, you will notice the word "trax" appearing in the Java packages. This is an indication that Version 5.5.1 of SAXON was moving towards something called Transformation API for XML (TrAX). More information on TrAX is coming up in the JAXP discussion. In a nutshell, TrAX provides a uniform API that should work with any XSLT processor.

Example 5-2. SimpleSaxon.java

package chap5;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import org.xml.sax.SAXException;

/**
 * A simple demo of SAXON. This code was originally written using
 * SAXON 5.5.1.
 */
public class SimpleSaxon {

    /**
     * Accept two command line arguments: the name of an XML file, and
     * the name of an XSLT stylesheet. The result of the transformation
     * is written to stdout.
     */
    public static void main(String[] args)
            throws MalformedURLException, IOException, SAXException {
        if (args.length != 2) {
            System.err.println("Usage:");
            System.err.println("  java " + SimpleSaxon.class.getName( )
                    + " xmlFileName xsltFileName");
            System.exit(1);
        }

        String xmlFileName = args[0];
        String xsltFileName = args[1];

        String xmlSystemId = new File(xmlFileName).toURL().toExternalForm( );
        String xsltSystemId = new File(xsltFileName).toURL().toExternalForm( );

        com.icl.saxon.trax.Processor processor =
                com.icl.saxon.trax.Processor.newInstance("xslt");

        // unlike Xalan, SAXON uses the SAX InputSource.  Xalan
        // uses its own class, XSLTInputSource
        org.xml.sax.InputSource xmlInputSource =
                new org.xml.sax.InputSource(xmlSystemId);
        org.xml.sax.InputSource xsltInputSource =
                new org.xml.sax.InputSource(xsltSystemId);

        com.icl.saxon.trax.Result result =
                new com.icl.saxon.trax.Result(System.out);

        // create a new compiled stylesheet
        com.icl.saxon.trax.Templates templates =
                processor.process(xsltInputSource);

        // create a transformer that can be used for a single transformation
        com.icl.saxon.trax.Transformer trans = templates.newTransformer( );
        trans.transform(xmlInputSource, result);
    }
}

The SAXON implementation starts exactly as the Xalan implementation does. Following the class declaration, the command-line parameters are validated and then converted to system IDs. The XML and XSLT system IDs are then wrapped in org.xml.sax.InputSource objects as follows:

org.xml.sax.InputSource xmlInputSource =
        new org.xml.sax.InputSource(xmlSystemId);
org.xml.sax.InputSource xsltInputSource =
        new org.xml.sax.InputSource(xsltSystemId);

This code is virtually indistinguishable from the Xalan code, except Xalan uses XSLTInputSource instead of InputSource. As mentioned before, XSLTInputSource is merely a subclass of InputSource that adds support for reading from a DOM Node. SAXON also has the ability to read from a DOM node, although its approach is slightly different.

Creating a Result object sets up the destination for the XSLT result tree, which is directed to System.out in this example:

com.icl.saxon.trax.Result result =
        new com.icl.saxon.trax.Result(System.out);

The XSLT stylesheet is then compiled, resulting in an object that can be used repeatedly from many concurrent threads:

com.icl.saxon.trax.Templates templates =
        processor.process(xsltInputSource);

In a typical XML and XSLT web site, the XML data is generated dynamically, but the same stylesheets are used repeatedly. For instance, stylesheets generating common headers, footers, and navigation bars will be used by many pages. To maximize performance, you will want to process the stylesheets once and reuse the instances for many clients at the same time. For this reason, the thread safety that Templates offers is critical.

An instance of the Transformer class is then created to perform the actual transformation. Unlike the stylesheet itself, the transformer cannot be shared by many clients and is not thread-safe. If this was a servlet implementation, the Transformer instance would have to be created with each invocation of doGet or doPost. In our example, the code is as follows:

com.icl.saxon.trax.Transformer trans = templates.newTransformer( );
trans.transform(xmlInputSource, result);

5.1.4. SAXON, Xalan, or TrAX?

As the previous examples show, SAXON and Xalan have many similarities. While similarities make learning the various APIs easy, they do not result in portable code. If you write code directly against either of these interfaces, you lock yourself into that particular implementation unless you want to rewrite your application.

The other option is to write a facade around both processors, presenting a consistent interface that works with either processor behind the scenes. The only problem with this approach is that as new processors are introduced, you must update the implementation of your facade. It would be very difficult for one individual or organization to keep up with the rapidly changing world of XSLT processors.

But if the facade was an open standard and supported by a large enough user base, the people and organizations that write the XSLT processors would feel pressure to adhere to the common API, rather than the other way around. TrAX was initiated in early 2000 as an effort to define a consistent API to any XSLT processor. Since some of the key people behind TrAX were also responsible for implementing some of the major XSLT processors, it was quickly accepted that TrAX would be a de facto standard, much in the way that SAX is.

Previous | Home | Next
4.4. Summary of Key Approaches | Book Index | 5.2. Introduction to JAXP 1.1
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Introduction to JAXP 1.1 (Java and XSLT)

 Java and XSLTSearch this book

5.2. Introduction to JAXP 1.1

TrAX was a great idea, and the original work and concepts behind it were absorbed into JAXP Version 1.1. If you search for TrAX on the Web and get the feeling that the effort is waning, this is only because focus has shifted from TrAX to JAXP. Although the name has changed, the concept has not: JAXP provides a standard Java interface to many XSLT processors, allowing you to choose your favorite underlying implementation while retaining portability.

First released in March 2000, Sun's JAXP 1.0 utilized XML 1.0, XML Namespaces 1.0, SAX 1.0, and DOM Level 1. JAXP is a standard extension to Java, meaning that Sun provides a specification through its Java Community Process (JCP) as well as a reference implementation. JAXP 1.1 follows the same basic design philosophies of JAXP 1.0, adding support for DOM Level 2, SAX 2, and XSLT 1.0. A tool like JAXP is necessary because the XSLT specification defines only a transformation language; it says nothing about how to write a Java XSLT processor. Although they all perform the same basic tasks, every processor uses a different API and has its own set of programming conventions.

JAXP is not an XML parser, nor is it an XSLT processor. Instead, it provides a common Java interface that masks differences between various implementations of the supported standards. When using JAXP, your code can avoid dependencies on specific vendor tools, allowing flexibility to upgrade to newer tools when they become available.

The key to JAXP's design is the concept of plugability layers. These layers provide consistent Java interfaces to the underlying SAX, DOM, and XSLT implementations. In order to utilize one of these APIs, you must obtain a factory class without hardcoding Xalan or SAXON code into your application. This is accomplished via a lookup mechanism that relies on Java system properties. Since three separate plugability layers are used, you can use a DOM parser from one vendor, a SAX parser from another vendor, and yet another XSLT processor from someone else. In reality, you will probably need to use a DOM parser compatible with your XSLT processor if you try to transform the DOM tree directly. Figure 5-1 illustrates the high-level architecture of JAXP 1.1.

Figure 5-1. JAXP 1.1 architecture

As shown, application code does not deal directly with specific parser or processor implementations, such as SAXON or Xalan. Instead, you write code against abstract classes that JAXP provides. This level of indirection allows you to pick and choose among different implementations without even recompiling your application.

The main drawback to an API such as JAXP is the "least common denominator" effect, which is all too familiar to AWT programmers. In order to maximize portability, JAXP mostly provides functionality that all XSLT processors support. This means, for instance, that Xalan's custom XPath APIs are not included in JAXP. In order to use value-added features of a particular processor, you must revert to nonportable code, negating the benefits of a plugability layer. Fortunately, most common tasks are supported by JAXP, so reverting to implementation-specific code is the exception, not the rule.

Although the JAXP specification does not define an XML parser or XSLT processor, reference implementations do include these tools. These reference implementations are open source Apache XML tools,[18] so complete source code is available.

[18] Crimson and Xalan.

5.2.1. JAXP 1.1 Implementation

You guessed it -- we will now reimplement the simple example using Sun's JAXP 1.1. Behind the scenes, this could use any JAXP 1.1-compliant XSLT processor; this code was developed and tested using Apache's Xalan 2 processor. Example 5-3 contains the complete source code.

Example 5-3. SimpleJaxp.java

package chap5;

import java.io.*;

/**
 * A simple demo of JAXP 1.1
 */
public class SimpleJaxp {

    /**
     * Accept two command line arguments: the name of an XML file, and
     * the name of an XSLT stylesheet. The result of the transformation
     * is written to stdout.
     */
    public static void main(String[] args)
            throws javax.xml.transform.TransformerException {
        if (args.length != 2) {
            System.err.println("Usage:");
            System.err.println("  java " + SimpleJaxp.class.getName( )
                    + " xmlFileName xsltFileName");
            System.exit(1);
        }

        File xmlFile = new File(args[0]);
        File xsltFile = new File(args[1]);

        javax.xml.transform.Source xmlSource =
                new javax.xml.transform.stream.StreamSource(xmlFile);
        javax.xml.transform.Source xsltSource =
                new javax.xml.transform.stream.StreamSource(xsltFile);
        javax.xml.transform.Result result =
                new javax.xml.transform.stream.StreamResult(System.out);

        // create an instance of TransformerFactory
        javax.xml.transform.TransformerFactory transFact =
                javax.xml.transform.TransformerFactory.newInstance( );

        javax.xml.transform.Transformer trans =
                transFact.newTransformer(xsltSource);

        trans.transform(xmlSource, result);
    }
}

As in the earlier examples, explicit package names are used in the code to point out which classes are parts of JAXP. In future examples, import statements will be favored because they result in less typing and more readable code. Our new program begins by declaring that it may throw TransformerException:

public static void main(String[] args)
        throws javax.xml.transform.TransformerException {

This is a general-purpose exception representing anything that might go wrong during the transformation process. In other processors, SAX-specific exceptions are typically propagated to the caller. In JAXP, TransformerException can be wrapped around any type of Exception object that various XSLT processors may throw.

Next, the command-line arguments are converted into File objects. In the SAXON and Xalan examples, we created a system ID for each of these files. Since JAXP can read directly from a File object, the extra conversion to a URI is not needed:

File xmlFile = new File(args[0]);
File xsltFile = new File(args[1]);

javax.xml.transform.Source xmlSource =
        new javax.xml.transform.stream.StreamSource(xmlFile);
javax.xml.transform.Source xsltSource =
        new javax.xml.transform.stream.StreamSource(xsltFile);

The Source interface is used to read both the XML file and the XSLT file. Unlike the SAX InputSource class or Xalan's XSLTInputSource class, Source is an interface that can have many implementations. In this simple example we are using StreamSource, which has the ability to read from a File object, an InputStream, a Reader, or a system ID. Later we will examine additional Source implementations that use SAX and DOM as input. Just like Source, Result is an interface that can have several implementations. In this example, a StreamResult sends the output of the transformations to System.out:

javax.xml.transform.Result result =
        new javax.xml.transform.stream.StreamResult(System.out);

Next, an instance of TransformerFactory is created:

javax.xml.transform.TransformerFactory transFact =
        javax.xml.transform.TransformerFactory.newInstance( );

The TransformerFactory is responsible for creating Transformer and Template objects. In our simple example, we create a Transformer object:

javax.xml.transform.Transformer trans =
        transFact.newTransformer(xsltSource);

Transformer objects are not thread-safe, although they can be used multiple times. For a simple example like this, we will not encounter any problems. In a threaded servlet environment, however, multiple users cannot concurrently access the same Transformer instance. JAXP also provides a Templates interface, which represents a stylesheet that can be accessed by many concurrent threads.

The transformer instance is then used to perform the actual transformation:

trans.transform(xmlSource, result);

This applies the XSLT stylesheet to the XML data, sending the result to System.out.

5.2.2. XSLT Plugability Layer

JAXP 1.1 defines a specific lookup procedure to locate an appropriate XSLT processor. This must be accomplished without hardcoding vendor-specific code into applications, so Java system properties and JAR file service providers are used. Within your code, first locate an instance of the TransformerFactory class as follows:

javax.xml.transform.TransformerFactory transFact =
        javax.xml.transform.TransformerFactory.newInstance( );

Since TransformerFactory is abstract, its newInstance( ) factory method is used to instantiate an instance of a specific subclass. The algorithm for locating this subclass begins by looking at the javax.xml.transform.TransformerFactory system property. Let us suppose that com.foobar.AcmeTransformer is a new XSLT processor compliant with JAXP 1.1. To utilize this processor instead of JAXP's default processor, you can specify the system property on the command line[19] when you start your Java application:

[19] System properties can also be specified in Ant build files.

java -Djavax.xml.transform.TransformerFactory=com.foobar.AcmeTransformer MyApp

Provided that JAXP is able to instantiate an instance of AcmeTransformer, this is the XSLT processor that will be used. Of course, AcmeTransformer must be a subclass of TransformerFactory for this to work, so it is up to vendors to offer support for JAXP.

If the system property is not specified, JAXP next looks for a property file named lib/jaxp.properties in the JRE directory. A property file consists of name=value pairs, and JAXP looks for a line like this:

javax.xml.transform.TransformerFactory=com.foobar.AcmeTransformer

You can obtain the location of the JRE with the following code:

String javaHomeDir = System.getProperty("java.home");
NOTE: Some popular development tools change the value of java.home when they are installed, which could prevent JAXP from locating jaxp.properties. JBuilder, for instance, installs its own version of Java 2 that it uses by default.

The advantage of creating jaxp.properties in this directory is that you can use your preferred processor for all of your applications that use JAXP without having to specify the system property on the command line. You can still override this file with the -D command-line syntax, however.

If jaxp.properties is not found, JAXP uses the JAR file service provider mechanism to locate an appropriate subclass of TransformerFactory. The service provider mechanism is outlined in the JAR file specification from Sun and simply means that you must create a file in the META-INF/services directory of a JAR file. In JAXP, this file is called javax.xml.transform.TransformerFactory. It contains a single line that specifies the implementation of TransformerFactory: com.foobar.AcmeTransformer in our fictitious example. If you look inside of xalan.jar in JAXP 1.1, you will find this file. In order to utilize a different parser that follows the JAXP 1.1 convention, simply make sure its JAR file is located first on your CLASSPATH.

Finally, if JAXP cannot find an implementation class from any of the three locations, it uses its default implementation of TransformerFactory. To summarize, here are the steps that JAXP performs when attempting to locate a factory:

  1. Use the value of the javax.xml.transform.TransformerFactory system property if it exists.

  2. If JRE/lib/jaxp.properties exists, then look for a javax.xml.transform.TransformerFactory=ImplementationClass entry in that file.

  3. Use a JAR file service provider to look for a file called META-INF/services/javax.xml.transform.TransformerFactory in any JAR file on the CLASSPATH.

  4. Use the default TransformerFactory instance.

The JAXP 1.1 plugability layers for SAX and DOM follow the exact same process as the XSLT layer, only they use the javax.xml.parsers.SAXParserFactory and javax.xml.parsers.DocumentBuilderFactory system properties respectively. It should be noted that JAXP 1.0 uses a much simpler algorithm where it checks only for the existence of the system property. If that property is not set, the default implementation is used.

5.2.3. The Transformer Class

As shown in Example 5-3, a Transformer object can be obtained from the TransformerFactory as follows:

javax.xml.transform.TransformerFactory transFact =
        javax.xml.transform.TransformerFactory.newInstance( );
javax.xml.transform.Transformer trans =
        transFact.newTransformer(xsltSource);

The Transformer instance is wrapped around an XSLT stylesheet and allows you to perform as many transformations as you wish. The main caveat is thread safety, because many threads cannot use a single Transformer instance concurrently. For each transformation, invoke the transform method:

abstract void transform(Source xmlSource, Result outputTarget)
    throws TransformerException

This method is abstract because the TransformerFactory returns a subclass of Transformer that does the actual work. The Source interface defines where the XML data comes from and the Result interface specifies where the transformation result is sent. The TransformerException will be thrown if anything goes wrong during the transformation process and may contain the location of the error and a reference to the original exception. The ability to properly report the location of the error is entirely dependent upon the quality of the underlying XSLT transformer implementation's error reporting. We will talk about specific classes that implement the Source and Result interfaces later in this chapter.

Aside from actually performing the transformation, the Transformer implementation allows you to set output properties and stylesheet parameters. In XSLT, a stylesheet parameter is declared and used as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:param name="image_dir" select="'images'"/>

  <xsl:template match="/">
    <html>
      <body>
        <h1>Stylesheet Parameter Example</h1>
        <img src="{$image_dir}/sample.gif"/>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

The <xsl:param> element declares the parameter name and an optional select attribute. This attribute specifies the default value if the stylesheet parameter is not provided. In this case, the string 'images' is the default value and is enclosed in apostrophes so it is treated as a string instead of an XPath expression. Later, the image_dir variable is referred to with the attribute value template syntax: {$image_dir}.

Passing a variable for the location of your images is a common technique because your development environment might use a different directory name than your production web server. Another common use for a stylesheet parameter is to pass in data that a servlet generates dynamically, such as a unique ID for session tracking.

From JAXP, pass this parameter via the Transformer instance. The code is simple enough:

javax.xml.transform.Transformer trans =
        transFact.newTransformer(xsltSource);
trans.setParameter("image_dir", "graphics");

You can set as many parameters as you like, and these parameters will be saved and reused for every transformation you make with this Transformer instance. If you wish to remove a parameter, you must call clearParameters( ), which clears all parameters for this Transformer instance. Parameters work similarly to a java.util.Map; if you set the same parameter twice, the second value overwrites the first value.

Another use for the Transformer class is to get and set output properties through one of the following methods:

void setOutputProperties(java.util.Properties props)
void setOutputProperty(String name, String value)
java.util.Properties getOutputProperties( )
String getOutputProperty(String name)

As you can see, properties are specified as name/value pairs of Strings and can be set and retrieved individually or as a group. Unlike stylesheet parameters, you can un-set an individual property by simply passing in null for the value. The permitted property names are defined in the javax.xml.transform.OutputKeys class and are explained in Table 5-1.

Table 5-1. Constants defined in javax.xml.transform.OutputKeys

Constant

Meaning

CDATA_SECTION_ELEMENTS

Specifies a whitespace-separated list of element names whose content should be output as CDATA sections. See the XSLT specification from the W3C for examples.

DOCTYPE_PUBLIC

Only used if DOCTYPE_SYSTEM is also used, this instructs the processor to output a PUBLIC document type declaration. For example: <!DOCTYPE rootElem PUBLIC "public id" "system id">.

DOCTYPE_SYSTEM

Instructs the processor to output a document-type declaration. For example: <!DOCTYPE rootElem SYSTEM "system id">.

ENCODING

Specifies the character encoding of the result tree, such as UTF-8 or UTF-16.

INDENT

Specifies whether or not whitespace may be added to the result tree, making the output more readable. Acceptable values are yes and no. Although indentation makes the output more readable, it does make the file size larger, thus harming performance.

MEDIA_TYPE

The MIME type of the result tree.

METHOD

The output method, either xml, html, or text. Although other values are possible, such as xhtml, these are implementation-defined and may be rejected by your processor.

OMIT_XML_DECLARATION

Acceptable values are yes and no, specifying whether or not to include the XML declaration on the first line of the result tree.

STANDALONE

Acceptable values are yes and no, specifying whether or not the XML declaration indicates that the document is standalone. For example: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>.

VERSION

Specifies the version of the output method, typically 1.0 for XML output. This shows up in the XML declaration as follows: <?xml version="1.0" encoding="UTF-8"?>.

It is no coincidence that these output properties are the same as the properties you can set on the <xsl:output> element in your stylesheets. For example:

<xsl:output method="xml" indent="yes" encoding="UTF-8"/>

Using JAXP, you can either specify additional output properties or override those set in the stylesheet. To change the encoding, write this code:

// this will take precedence over any encoding specified in the stylesheet
trans.setOutputProperty(OutputKeys.ENCODING, "UTF-16");

Keep in mind that this will, in addition to adding encoding="UTF-16" to the XML declaration, actually cause the processor to use that encoding in the result tree. For a value of UTF-16, this means that 16-bit Unicode characters will be generated, so you may have trouble viewing the result tree in many ASCII-only text editors.

5.2.4. JAXP XSLT Design

Now that we have seen some example code and have begun our exploration of the Transformer class, let's step back and look at the overall design of the XSLT plugability layer. JAXP support for XSLT is broken down into the packages listed in Table 5-2.

Table 5-2. JAXP transformation packages

Package

Description

javax.xml.transform

Defines a general-purpose API for XML transformations without any dependencies on SAX or DOM. The Transformer class is obtained from the TransformerFactory class. The Transformer transforms from a Source to a Result.

javax.xml.transform.dom

Defines how transformations can be performed using DOM. Provides implementations of Source and Result: DOMSource and DOMResult.

javax.xml.transform.sax

Supports SAX2 transformations. Defines SAX versions of Source and Result: SAXSource and SAXResult. Also defines a subclass of TransformerFactory that allows SAX2 events to be fed into an XSLT processor.

javax.xml.transform.stream

Defines I/O stream implementations of Source and Result: StreamSource and StreamResult.

The heart of JAXP XSLT support lies in the javax.xml.transform package, which lays out the mechanics and overall process for any transformation that is performed. This package mostly consists of interfaces and abstract classes, except for OutputKeys and a few exception and error classes. Figure 5-2 presents a UML class diagram that shows all of the pieces in this important package.

Figure 5-2. javax.xml.transform class diagram

As you can see, this is a small package, indicative of the fact that JAXP is merely a wrapper around the tools that actually perform transformations. The entry point is TransformerFactory, which creates instances of Transformer, as we have already seen, as well as instances of the Templates abstract class. A Templates object represents a compiled stylesheet and will be covered in detail later in this chapter.[20] The advantage of compilation is performance: the same Templates object can be used over and over by many threads without reparsing the XSLT file.

[20] The exact definition of a "compiled" stylesheet is vague. XSLT processors are free to optimize cached stylesheets however they see fit.

The URIResolver is responsible for resolving URIs found within stylesheets and is generally something you will not need to deal with directly. It is used when a stylesheet imports or includes another document, and the processor needs to figure out where to look for that document. For example:

<xsl:import href="commonFooter.xslt"/>

ErrorListener, as you may guess, is an interface that allows your code to register as a listener for error conditions. This interface defines the following three methods:

void error(TransformerException ex)
void fatalError(TransformerException ex)
void warning(TransformerException ex)

The TransformerException has the ability to wrap around another Exception or Throwable object and may return an instance of the SourceLocator class. If the underlying XSLT implementation does not provide a SourceLocator, null is returned. The SourceLocator interface defines methods to locate where a TransformerException originated. In the case of error() and warning(), the XSLT processor is required to continue processing the document until the end. For fatalError(), on the other hand, the XSLT processor is not required to continue. If you do not register an ErrorListener object, then all errors, fatal errors, and warnings are normally written to System.err. TransformerFactoryConfigurationError and TransformerConfigurationException round out the error-handling APIs for JAXP, indicating problems configuring the underlying XSLT processor implementation. The TransformerFactoryConfigurationError class is generally used when the implementation class cannot be found on the CLASSPATH or cannot be instantiated at all. TransformerConfigurationException simply indicates a "serious configuration error" according to its documentation.

Previous | Home | Next
5. XSLT Processing with Java | Book Index | 5.3. Input and Output
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Input and Output (Java and XSLT)

 Java and XSLTSearch this book

5.3. Input and Output

XSLT processors, like other XML tools, can read their input data from many different sources. In the most basic scenario, you will load a static stylesheet and XML document using the java.io.File class. More commonly, the XSLT stylesheet will come from a file, but the XML data will be generated dynamically as the result of a database query. In this case, it does not make sense to write the database query results to an XML file and then parse it into the XSLT processor. Instead, it is desirable to pipe the XML data directly into the processor using SAX or DOM. In fact, we will even see how to read nonXML data and transform it using XSLT.

5.3.1. System Identifiers, Files, and URLs

The simple examples presented earlier in this chapter introduced the concept of a system identifier. As mentioned before, system identifiers are nothing more than URIs and are used frequently by XML tools. For example, javax.xml.transform.Source, one of the key interfaces in JAXP, has the following API:

public interface Source {
    String getSystemId( );
    void setSystemId(String systemId);
}

The second method, setSystemId( ), is crucial. By providing a URI to the Source, the XSLT processor can resolve URIs encountered in XSLT stylesheets. This allows XSLT code like this to work:

<xsl:import href="commonFooter.xslt"/>

When it comes to XSLT programming, you will use methods in java.io.File and java.net.URL to convert platform-specific file names into system IDs. These can then be used as parameters to any methods that expect a system ID as a parameter. For example, you would write the following code to convert a platform-specific filename into a system ID:

public static void main(String[] args) {
    // assume that the first command-line arg contains a file name
    // - on Windows, something like "C:\home\index.xml"
    // - on Unix, something like "/usr/home/index.xml"
    String fileName = args[0];
    File fileObject = new File(fileName);
    URL fileURL = fileObject.toURL( );
    String systemID = fileURL.toExternalForm( );

This code was written on several lines for clarity; it can be consolidated as follows:

String systemID = new File(fileName).toURL().toExternalForm( );

Converting from a system identifier back to a filename or a File object can be accomplished with this code:

URL url = new URL(systemID);
String fileName = url.getFile( );
File fileObject = new File(fileName);

And once again, this code can be condensed into a single line as follows:

File fileObject = new File((new URL(systemID)).getFile( ));

5.3.2. JAXP I/O Design

The Source and Result interfaces in javax.xml.transform provide the basis for all transformation input and output in JAXP 1.1. Regardless of whether a stylesheet is obtained via a URI, filename, or InputStream, its data is fed into JAXP via an implementation of the Source interface. The output is then sent to an implementation of the Result interface. The implementations provided by JAXP are shown in Figure 5-3.

Figure 5-3. Source and Result interfaces

As you can see, JAXP is not particular about where it gets its data or sends its results. Remember that two instances of Source are always specified: one for the XML data and another for the XSLT stylesheet.

5.3.3. JAXP Stream I/O

As shown in Figure 5-3, StreamSource is one of the implementations of the Source interface. In addition to the system identifiers that Source provides, StreamSource allows input to be obtained from a File, an InputStream, or a Reader. The SimpleJaxp class in Example 5-3 showed how to use StreamSource to read from a File object. There are also four constructors that allow you to construct a StreamSource from either an InputStream or Reader. The complete list of constructors is shown here:

public StreamSource( )
public StreamSource(File f)
public StreamSource(String systemId)
public StreamSource(InputStream byteStream)
public StreamSource(InputStream byteStream, String systemId)
public StreamSource(Reader characterStream)
public StreamSource(Reader characterStream, String systemId)

For the constructors that take InputStream and Reader as arguments, the first argument provides either the XML data or the XSLT stylesheet. The second argument, if present, is used to resolve relative URI references in the document. As mentioned before, your XSLT stylesheet may include the following code:

<xsl:import href="commonFooter.xslt"/>

By providing a system identifier as a parameter to the StreamSource, you are telling the XSLT processor where to look for commonFooter.xslt. Without this parameter, you may encounter an error when the processor cannot resolve this URI. The simple fix is to call the setSystemId( ) method as follows:

// construct a Source that reads from an InputStream
Source mySrc = new StreamSource(anInputStream);
// specify a system ID (a String) so the Source can resolve relative URLs
// that are encountered in XSLT stylesheets
mySrc.setSystemId(aSystemId);

The documentation for StreamSource also advises that InputStream is preferred to Reader because this allows the processor to properly handle the character encoding as specified in the XML declaration.

StreamResult is similar in functionality to StreamSource, although it is not necessary to resolve relative URIs. The available constructors are as follows:

public StreamResult( )
public StreamResult(File f)
public StreamResult(String systemId)
public StreamResult(OutputStream byteStream)
public StreamResult(Writer characterStream)

Let's look at some of the other options for StreamSource and StreamResult. Example 5-4 is a modification of the SimpleJaxp program that was presented earlier. It downloads the XML specification from the W3C web site and stores it in a temporary file on your local disk. To download the file, construct a StreamSource with a system identifier as a parameter. The stylesheet is a simple one that merely performs an identity transformation, copying the unmodified XML data to the result tree. The result is then sent to a StreamResult using its File constructor.

Example 5-4. Streams.java

package chap5;

import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * A simple demo of JAXP 1.1 StreamSource and StreamResult. This
 * program downloads the XML specification from the W3C and prints
 * it to a temporary file.
 */
public class Streams {

    // an identity copy stylesheet
    private static final String IDENTITY_XSLT =
        "<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"
        + " version='1.0'>"
        + "<xsl:template match='/'><xsl:copy-of select='.'/>"
        + "</xsl:template></xsl:stylesheet>";

    // the XML spec in XML format
    // (using an HTTP URL rather than a file URL)
    private static String xmlSystemId =
            "http://www.w3.org/TR/2000/REC-xml-20001006.xml";

    public static void main(String[] args) throws IOException,
            TransformerException {

        // show how to read from a system identifier and a Reader
        Source xmlSource = new StreamSource(xmlSystemId);
        Source xsltSource = new StreamSource(
                new StringReader(IDENTITY_XSLT));

        // send the result to a file
        File resultFile = File.createTempFile("Streams", ".xml");
        Result result = new StreamResult(resultFile);

        System.out.println("Results will go to: "
                + resultFile.getAbsolutePath( ));

        // get the factory
        TransformerFactory transFact = TransformerFactory.newInstance( );

        // get a transformer for this particular stylesheet
        Transformer trans = transFact.newTransformer(xsltSource);

        // do the transformation
        trans.transform(xmlSource, result);
    }
}

The "identity copy" stylesheet simply matches "/", which is the document itself. It then uses <xsl:copy-of select='.'/> to select the document and copy it to the result tree. In this case, we coded our own stylesheet. You can also omit the XSLT stylesheet altogether as follows:

// construct a Transformer without any XSLT stylesheet
Transformer trans = transFact.newTransformer( );

In this case, the processor will provide its own stylesheet and do the same thing that our example does. This is useful when you need to use JAXP to convert a DOM tree to XML text for debugging purposes because the default Transformer will simply copy the XML data without any transformation.

5.3.4. JAXP DOM I/O

In many cases, the fastest form of transformation available is to feed an instance of org.w3c.dom.Document directly into JAXP. Although the transformation is fast, it does take time to generate the DOM; DOM is also memory intensive, and may not be the best choice for large documents. In most cases, the DOM data will be generated dynamically as the result of a database query or some other operation (see Chapter 1, "Introduction "). Once the DOM is generated, simply wrap the Document object in a DOMSource as follows:

org.w3c.dom.Document domDoc = createDomDocument( );
Source xmlSource = new javax.xml.transform.dom.DOMSource(domDoc);

The remainder of the transformation looks identical to the file-based transformation shown in Example 5-4. JAXP needs only the alternate input Source object shown here to read from DOM.

5.3.5. JAXP SAX I/O

XSLT is designed to transform well-formed XML data into another format, typically HTML. But wouldn't it be nice if we could also use XSLT stylesheets to transform nonXML data into HTML? For example, most spreadsheets have the ability to export their data into Comma Separated Values (CSV) format, as shown here:

Burke,Eric,M
Burke,Jennifer,L
Burke,Aidan,G

One approach is parsing the file into memory, using DOM to create an XML representation of the data, and then feeding that information into JAXP for transformation. This approach works but requires an intermediate programming step to convert the CSV file into a DOM tree. A better option is to write a custom SAX parser, feeding its output directly into JAXP. This avoids the overhead of constructing the DOM tree, offering better memory utilization and performance.

5.3.5.1. The approach

It turns out that writing a SAX parser is quite easy.[21] All a SAX parser does is read an XML file top to bottom and fire event notifications as various elements are encountered. In our custom parser, we will read the CSV file top to bottom, firing SAX events as we read the file. A program listening to those SAX events will not realize that the data file is CSV rather than XML; it sees only the events. Figure 5-4 illustrates the conceptual model.

[21] Our examples use SAX 2.

Figure 5-4. Custom SAX parser

In this model, the XSLT processor interprets the SAX events as XML data and uses a normal stylesheet to perform the transformation. The interesting aspect of this model is that we can easily write custom SAX parsers for other file formats, making XSLT a useful transformation language for just about any legacy application data.

In SAX, org.xml.sax.XMLReader is a standard interface that parsers must implement. It works in conjunction with org.xml.sax.ContentHandler, which is the interface that listens to SAX events. For this model to work, your XSLT processor must implement the ContentHandler interface so it can listen to the SAX events that the XMLReader generates. In the case of JAXP, javax.xml.transform.sax.TransformerHandler is used for this purpose.

Obtaining an instance of TransformerHandler requires a few extra programming steps. First, create a TransformerFactory as usual:

TransformerFactory transFact = TransformerFactory.newInstance( );

As before, the TransformerFactory is the JAXP abstraction to some underlying XSLT processor. This underlying processor may not support SAX features, so you have to query it to determine if you can proceed:

if (transFact.getFeature(SAXTransformerFactory.FEATURE)) {

If this returns false, you are out of luck. Otherwise, you can safely downcast to a SAXTransformerFactory and construct the TransformerHandler instance:

SAXTransformerFactory saxTransFact =
            (SAXTransformerFactory) transFact;
  // create a ContentHandler, don't specify a stylesheet.  Without
  // a stylesheet, raw XML is sent to the output.
  TransformerHandler transHand = saxTransFact.newTransformerHandler( );

In the code shown here, a stylesheet was not specified. JAXP defaults to the identity transformation stylesheet, which means that the SAX events will be "transformed" into raw XML output. To specify a stylesheet that performs an actual transformation, pass a Source to the method as follows:

Source xsltSource = new StreamSource(myXsltSystemId);
TransformerHandler transHand = saxTransFact.newTransformerHandler(
        xsltSource);

5.3.5.2. Detailed CSV to SAX design

Before delving into the complete example program, let's step back and look at a more detailed design diagram. The conceptual model is straightforward, but quite a few classes and interfaces come into play. Figure 5-5 shows the pieces necessary for SAX-based transformations.

Figure 5-5. SAX and XSLT transformations

This diagram certainly appears to be more complex than previous approaches, but is similar in many ways. In previous approaches, we used the TransformerFactory to create instances of Transformer; in the SAX approach, we start with a subclass of TransformerFactory. Before any work can be done, you must verify that your particular implementation supports SAX-based transformations. The reference implementation of JAXP does support this, although other implementations are not required to do so. In the following code fragment, the getFeature method of TransformerFactory will return true if you can safely downcast to a SAXTransformerFactory instance:

TransformerFactory transFact = TransformerFactory.newInstance( );
if (transFact.getFeature(SAXTransformerFactory.FEATURE)) {
    // downcast is allowed
    SAXTransformerFactory saxTransFact = (SAXTransformerFactory) transFact;

If getFeature returns false, your only option is to look for an implementation that does support SAX-based transformations. Otherwise, you can proceed to create an instance of TransformerHandler:

TransformerHandler transHand = saxTransFact.newTransformerHandler(myXsltSource);

This object now represents your XSLT stylesheet. As Figure 5-5 shows, TransformerHandler extends org.xml.sax.ContentHandler, so it knows how to listen to events from a SAX parser. The series of SAX events will provide the "fake XML" data, so the only remaining piece of the puzzle is to set the Result and tell the SAX parser to begin parsing. The TransformerHandler also provides a reference to a Transformer, which allows you to set output properties such as the character encoding, whether to indent the output or any other attributes of <xsl:output>.

5.3.5.3. Writing the custom parser

Writing the actual SAX parser sounds harder than it really is. The process basically involves implementing the org.xml.sax.XMLReader interface, which provides numerous methods you can safely ignore for most applications. For example, when parsing a CSV file, it is probably not necessary to deal with namespaces or validation. The code for AbstractXMLReader.java is shown in Example 5-5. This is an abstract class that provides basic implementations of every method in the XMLReader interface except for the parse( ) method. This means that all you need to do to write a parser is create a subclass and override this single method.

Example 5-5. AbstractXMLReader.java

package com.oreilly.javaxslt.util;

import java.io.IOException;
import java.util.*;
import org.xml.sax.*;

/**
 * An abstract class that implements the SAX2 XMLReader interface. The
 * intent of this class is to make it easy for subclasses to act as
 * SAX2 XMLReader implementations. This makes it possible, for example, for
 * them to emit SAX2 events that can be fed into an XSLT processor for
 * transformation.
 */
public abstract class AbstractXMLReader implements org.xml.sax.XMLReader {
    private Map featureMap = new HashMap( );
    private Map propertyMap = new HashMap( );
    private EntityResolver entityResolver;
    private DTDHandler dtdHandler;
    private ContentHandler contentHandler;
    private ErrorHandler errorHandler;

    /**
     * The only abstract method in this class. Derived classes can parse
     * any source of data and emit SAX2 events to the ContentHandler.
     */
    public abstract void parse(InputSource input) throws IOException,
            SAXException;

    public boolean getFeature(String name)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        Boolean featureValue = (Boolean) this.featureMap.get(name);
        return (featureValue == null) ? false
                : featureValue.booleanValue( );
    }

    public void setFeature(String name, boolean value)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        this.featureMap.put(name, new Boolean(value));
    }

    public Object getProperty(String name)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        return this.propertyMap.get(name);
    }

    public void setProperty(String name, Object value)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        this.propertyMap.put(name, value);
    }

    public void setEntityResolver(EntityResolver entityResolver) {
        this.entityResolver = entityResolver;
    }

    public EntityResolver getEntityResolver( ) {
        return this.entityResolver;
    }

    public void setDTDHandler(DTDHandler dtdHandler) {
        this.dtdHandler = dtdHandler;
    }

    public DTDHandler getDTDHandler( ) {
        return this.dtdHandler;
    }

    public void setContentHandler(ContentHandler contentHandler) {
        this.contentHandler = contentHandler;
    }

    public ContentHandler getContentHandler( ) {
        return this.contentHandler;
    }

    public void setErrorHandler(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    public ErrorHandler getErrorHandler( ) {
        return this.errorHandler;
    }

    public void parse(String systemId) throws IOException, SAXException {
        parse(new InputSource(systemId));
    }
}

Creating the subclass, CSVXMLReader, involves overriding the parse( ) method and actually scanning through the CSV file, emitting SAX events as elements in the file are encountered. While the SAX portion is very easy, parsing the CSV file is a little more challenging. To make this class as flexible as possible, it was designed to parse through any CSV file that a spreadsheet such as Microsoft Excel can export. For simple data, your CSV file might look like this:

Burke,Eric,M
Burke,Jennifer,L
Burke,Aidan,G

The XML representation of this file is shown in Example 5-6. The only real drawback here is that CSV files are strictly positional, meaning that names are not assigned to each column of data. This means that the XML output merely contains a sequence of three <value> elements for each line, so your stylesheet will have to select items based on position.

Example 5-6. Example XML output from CSV parser

<?xml version="1.0" encoding="UTF-8"?>
<csvFile>
  <line>
    <value>Burke</value>
    <value>Eric</value>
    <value>M</value>
  </line>
  <line>
    <value>Burke</value>
    <value>Jennifer</value>
    <value>L</value>
  </line>
  <line>
    <value>Burke</value>
    <value>Aidan</value>
    <value>G</value>
  </line>
</csvFile>

One enhancement would be to design the CSV parser so it could accept a list of meaningful column names as parameters, and these could be used in the XML that is generated. Another option would be to write an XSLT stylesheet that transformed this initial output into another form of XML that used meaningful column names. To keep the code example relatively manageable, these features were omitted from this implementation. But there are some complexities to the CSV file format that have to be considered. For example, fields that contain commas must be surrounded with quotes:

"Consultant,Author,Teacher",Burke,Eric,M
Teacher,Burke,Jennifer,L
None,Burke,Aidan,G

To further complicate matters, fields may also contain quotes ("). In this case, they are doubled up, much in the same way you use double backslash characters (\\) in Java to represent a single backslash. In the following example, the first column contains a single quote, so the entire field is quoted, and the single quote is doubled up:

"test""quote",Teacher,Burke,Jennifer,L

This would be interpreted as:

test"quote,Teacher,Burke,Jennifer,L

The code in Example 5-7 shows the complete implementation of the CSV parser.

Example 5-7. CSVXMLReader.java

package com.oreilly.javaxslt.util;

import java.io.*;
import java.net.URL;

import org.xml.sax.*;
import org.xml.sax.helpers.*;

/**
 * A utility class that parses a Comma Separated Values (CSV) file
 * and outputs its contents using SAX2 events. The format of CSV that
 * this class reads is identical to the export format for Microsoft
 * Excel. For simple values, the CSV file may look like this:
 * <pre>
 * a,b,c
 * d,e,f
 * </pre>
 * Quotes are used as delimiters when the values contain commas:
 * <pre>
 * a,"b,c",d
 * e,"f,g","h,i"
 * </pre>
 * And double quotes are used when the values contain quotes. This parser
 * is smart enough to trim spaces around commas, as well.
 *
 * @author Eric M. Burke
 */
public class CSVXMLReader extends AbstractXMLReader {

    // an empty attribute for use with SAX
    private static final Attributes EMPTY_ATTR = new AttributesImpl( );

    /**
     * Parse a CSV file. SAX events are delivered to the ContentHandler
     * that was registered via <code>setContentHandler</code>.
     *
     * @param input the comma separated values file to parse.
     */
    public void parse(InputSource input) throws IOException,
            SAXException {
        // if no handler is registered to receive events, don't bother
        // to parse the CSV file
        ContentHandler ch = getContentHandler( );
        if (ch == null) {
            return;
        }

        // convert the InputSource into a BufferedReader
        BufferedReader br = null;
        if (input.getCharacterStream( ) != null) {
            br = new BufferedReader(input.getCharacterStream( ));
        } else if (input.getByteStream( ) != null) {
            br = new BufferedReader(new InputStreamReader(
                    input.getByteStream( )));
        } else if (input.getSystemId( ) != null) {
            java.net.URL url = new URL(input.getSystemId( ));
            br = new BufferedReader(new InputStreamReader(url.openStream( )));
        } else {
            throw new SAXException("Invalid InputSource object");
        }

        ch.startDocument( );

        // emit <csvFile>
        ch.startElement("","","csvFile",EMPTY_ATTR);

        // read each line of the file until EOF is reached
        String curLine = null;
        while ((curLine = br.readLine( )) != null) {
            curLine = curLine.trim( );
            if (curLine.length( ) > 0) {
                // create the <line> element
                ch.startElement("","","line",EMPTY_ATTR);
                // output data from this line
                parseLine(curLine, ch);
                // close the </line> element
                ch.endElement("","","line");
            }
        }

        // emit </csvFile>
        ch.endElement("","","csvFile");
        ch.endDocument( );
    }

    // Break an individual line into tokens. This is a recursive function
    // that extracts the first token, then recursively parses the
    // remainder of the line.
    private void parseLine(String curLine, ContentHandler ch)
        throws IOException, SAXException {

        String firstToken = null;
        String remainderOfLine = null;
        int commaIndex = locateFirstDelimiter(curLine);
        if (commaIndex > -1) {
            firstToken = curLine.substring(0, commaIndex).trim( );
            remainderOfLine = curLine.substring(commaIndex+1).trim( );
        } else {
            // no commas, so the entire line is the token
            firstToken = curLine;
        }

        // remove redundant quotes
        firstToken = cleanupQuotes(firstToken);

        // emit the <value> element
        ch.startElement("","","value",EMPTY_ATTR);
        ch.characters(firstToken.toCharArray(), 0, firstToken.length( ));
        ch.endElement("","","value");

        // recursively process the remainder of the line
        if (remainderOfLine != null) {
            parseLine(remainderOfLine, ch);
        }
    }

    // locate the position of the comma, taking into account that
    // a quoted token may contain ignorable commas.
    private int locateFirstDelimiter(String curLine) {
        if (curLine.startsWith("\"")) {
            boolean inQuote = true;
            int numChars = curLine.length( );
            for (int i=1; i<numChars; i++) {
                char curChar = curLine.charAt(i);
                if (curChar == '"') {
                    inQuote = !inQuote;
                } else if (curChar == ',' && !inQuote) {
                    return i;
                }
            }
            return -1;
        } else {
            return curLine.indexOf(',');
        }
    }

    // remove quotes around a token, as well as pairs of quotes
    // within a token.
    private String cleanupQuotes(String token) {
        StringBuffer buf = new StringBuffer( );
        int length = token.length( );
        int curIndex = 0;

        if (token.startsWith("\"") && token.endsWith("\"")) {
            curIndex = 1;
            length--;
        }

        boolean oneQuoteFound = false;
        boolean twoQuotesFound = false;

        while (curIndex < length) {
            char curChar = token.charAt(curIndex);
            if (curChar == '"') {
                twoQuotesFound = (oneQuoteFound) ? true : false;
                oneQuoteFound = true;
            } else {
                oneQuoteFound = false;
                twoQuotesFound = false;
            }

            if (twoQuotesFound) {
                twoQuotesFound = false;
                oneQuoteFound = false;
                curIndex++;
                continue;
            }

            buf.append(curChar);
            curIndex++;
        }

        return buf.toString( );
    }
}

CSVXMLReader is a subclass of AbstractXMLReader, so it must provide an implementation of the abstract parse method:

public void parse(InputSource input) throws IOException,
            SAXException {
        // if no handler is registered to receive events, don't bother
        // to parse the CSV file
        ContentHandler ch = getContentHandler( );
        if (ch == null) {
            return;
        }

The first thing this method does is check for the existence of a SAX ContentHandler. The base class, AbstractXMLReader, provides access to this object, which is responsible for listening to the SAX events. In our example, an instance of JAXP's TransformerHandler is used as the SAX ContentHandler implementation. If this handler is not registered, our parse method simply returns because nobody is registered to listen to the events. In a real SAX parser, the XML would be parsed anyway, which provides an opportunity to check for errors in the XML data. Choosing to return immediately was merely a performance optimization selected for this class.

The SAX InputSource parameter allows our custom parser to locate the CSV file. Since an InputSource has many options for reading its data, parsers must check each potential source in the order shown here:

// convert the InputSource into a BufferedReader
BufferedReader br = null;
if (input.getCharacterStream( ) != null) {
    br = new BufferedReader(input.getCharacterStream( ));
} else if (input.getByteStream( ) != null) {
    br = new BufferedReader(new InputStreamReader(
            input.getByteStream( )));
} else if (input.getSystemId( ) != null) {
    java.net.URL url = new URL(input.getSystemId( ));
    br = new BufferedReader(new InputStreamReader(url.openStream( )));
} else {
    throw new SAXException("Invalid InputSource object");
}

Assuming that our InputSource was valid, we can now begin parsing the CSV file and emitting SAX events. The first step is to notify the ContentHandler that a new document has begun:

ch.startDocument( );

// emit <csvFile>
ch.startElement("","","csvFile",EMPTY_ATTR);

The XSLT processor interprets this to mean the following:

<?xml version="1.0" encoding="UTF-8"?>
<csvFile>

Our parser simply ignores many SAX 2 features, particularly XML namespaces. This is why many values passed as parameters to the various ContentHandler methods simply contain empty strings. The EMPTY_ATTR constant indicates that this XML element does not have any attributes.

The CSV file itself is very straightforward, so we merely loop over every line in the file, emitting SAX events as we read each line. The parseLine method is a private helper method that does the actual CSV parsing:

// read each line of the file until EOF is reached
String curLine = null;
while ((curLine = br.readLine( )) != null) {
    curLine = curLine.trim( );
    if (curLine.length( ) > 0) {
        // create the <line> element
        ch.startElement("","","line",EMPTY_ATTR);
        parseLine(curLine, ch);
        ch.endElement("","","line");
    }
}

And finally, we must indicate that the parsing is complete:

// emit </csvFile>
ch.endElement("","","csvFile");
ch.endDocument( );

The remaining methods in CSVXMLReader are not discussed in detail here because they are really just responsible for breaking down each line in the CSV file and checking for commas, quotes, and other mundane parsing tasks. One thing worth noting is the code that emits text, such as the following:

<value>Some Text Here</value>

SAX parsers use the characters method on ContentHandler to represent text, which has this signature:

public void characters(char[] ch, int start, int length)

Although this method could have been designed to take a String, using an array allows SAX parsers to preallocate a large character array and then reuse that buffer repeatedly. This is why an implementation of ContentHandler cannot simply assume that the entire ch array contains meaningful data. Instead, it must read only the specified number of characters beginning at the start position.

Our parser uses a relatively straightforward approach, simply converting a String to a character array and passing that as a parameter to the characters method:

// emit the <value>text</value> element
ch.startElement("","","value",EMPTY_ATTR);
ch.characters(firstToken.toCharArray(), 0, firstToken.length( ));
ch.endElement("","","value");

5.3.5.4. Using the parser

To wrap things up, let's look at how you will actually use this CSV parser with an XSLT stylesheet. The code shown in Example 5-8 is a standalone Java application that allows you to perform XSLT transformations on CSV files. As the comments indicate, it requires the name of a CSV file as its first parameter and can optionally take the name of an XSLT stylesheet as its second parameter. All output is sent to System.out.

Example 5-8. SimpleCSVProcessor.java

package com.oreilly.javaxslt.util;

import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;

/**
 * Shows how to use the CSVXMLReader class. This is a command-line
 * utility that takes a CSV file and optionally an XSLT file as
 * command line parameters. A transformation is applied and the
 * output is sent to System.out.
 */
public class SimpleCSVProcessor {

    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            System.err.println("Usage: java "
                    + SimpleCSVProcessor.class.getName( )
                    + " <csvFile> [xsltFile]");
            System.err.println(" - csvFile is required");
            System.err.println(" - xsltFile is optional");
            System.exit(1);
        }

        String csvFileName = args[0];
        String xsltFileName = (args.length > 1) ? args[1] : null;

        TransformerFactory transFact = TransformerFactory.newInstance( );
        if (transFact.getFeature(SAXTransformerFactory.FEATURE)) {
            SAXTransformerFactory saxTransFact =
                    (SAXTransformerFactory) transFact;
            TransformerHandler transHand = null;
            if (xsltFileName == null) {
                transHand = saxTransFact.newTransformerHandler( );
            } else {
                transHand = saxTransFact.newTransformerHandler(
                        new StreamSource(new File(xsltFileName)));
            }

            // set the destination for the XSLT transformation
            transHand.setResult(new StreamResult(System.out));

            // hook the CSVXMLReader to the CSV file
            CSVXMLReader csvReader = new CSVXMLReader( );
            InputSource csvInputSrc = new InputSource(
                    new FileReader(csvFileName));

            // attach the XSLT processor to the CSVXMLReader
            csvReader.setContentHandler(transHand);
            csvReader.parse(csvInputSrc);
        } else {
            System.err.println("SAXTransformerFactory is not supported.");
            System.exit(1);
        }
    }
}

As mentioned earlier in this chapter, the TransformerHandler is provided by JAXP and is an implementation of the org.xml.sax.ContentHandler interface. It is constructed by the SAXTransformerFactory as follows:

TransformerHandler transHand = null;
if (xsltFileName == null) {
    transHand = saxTransFact.newTransformerHandler( );
} else {
    transHand = saxTransFact.newTransformerHandler(
            new StreamSource(new File(xsltFileName)));
}

When the XSLT stylesheet is not specified, the transformer performs an identity transformation. This is useful when you just want to see the raw XML output without applying a stylesheet. You will probably want to do this first to see how your XSLT will need to be written. If a stylesheet is provided, however, it is used for the transformation.

The custom parser is then constructed as follows:

CSVXMLReader csvReader = new CSVXMLReader( );

The location of the CSV file is then converted into a SAX InputSource:

InputSource csvInputSrc = new InputSource(
        new FileReader(csvFileName));

And finally, the XSLT processor is attached to our custom parser. This is accomplished by registering the TransformerHandler as the ContentHandler on csvReader. A single call to the parse method causes the parsing and transformation to occur:

// attach the XSLT processor to the CSVXMLReader
csvReader.setContentHandler(transHand);
csvReader.parse(csvInputSrc);

For a simple test, assume that a list of presidents is available in CSV format:

Washington,George,,
Adams,John,,
Jefferson,Thomas,,
Madison,James,,
  etc...
Bush,George,Herbert,Walker
Clinton,William,Jefferson,
Bush,George,W,

To see what the XML looks like, invoke the program as follows:

java com.oreilly.javaxslt.util.SimpleCSVProcessor presidents.csv

This will parse the CSV file and apply the identity transformation stylesheet, sending the following output to the console:

<?xml version="1.0" encoding="UTF-8"?>
<csvFile>
  <line>
    <value>Washington</value>
    <value>George</value>
    <value/>
    <value/>
  </line>
  <line>
    etc...
</csvFile>

Actually, the output is crammed onto a single long line, but it is broken up here to make it more readable. Any good XML editor application should provide a feature to pretty-print the XML as shown. In order to transform this into something useful, a stylesheet is required. The XSLT stylesheet shown in Example 5-9 takes any output from this program and converts it into an HTML table.

Example 5-9. csvToHTMLTable.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <table border="1">
      <xsl:apply-templates select="csvFile/line"/>
    </table>
  </xsl:template>

  <xsl:template match="line">
    <tr>
      <xsl:apply-templates select="value"/>
    </tr>
  </xsl:template>

  <xsl:template match="value">
    <td>
      <!-- If a value is empty, print a non-breaking space
           so the HTML table looks OK -->
      <xsl:if test=".=''">
        <xsl:text> disable-output-escaping="yes">&amp;nbsp;</xsl:text>
      </xsl:if>
      <xsl:value-of select="."/>
    </td>
  </xsl:template>
</xsl:stylesheet>

In order to apply this stylesheet, type the following command:

java com.oreilly.javaxslt.util.SimpleCSVProcessor presidents.csv csvToHTMLTable.xslt

As before, the results are sent to System.out and contain code for an HTML table. This stylesheet will work with any CSV file parsed with SimpleCSVProcessor, not just presidents.xml. Now that the concept has been proved, you can add fancy formatting and custom output to the resulting HTML without altering any Java code -- just edit the stylesheet or write a new one.

5.3.5.5. Conclusion

Although writing a SAX parser and connecting it to JAXP does involve quite a few interrelated classes, the resulting application requires only two command-line arguments and will work with any CSV or XSLT file. What makes this example interesting is that the same approach will work with essentially any data source. The steps are broken down as follows:

  1. Create a custom SAX parser by implementing org.xml.sax.XMLReader or extending com.oreilly.javaxslt.util.AbstractXMLReader.

  2. In your parser, emit the appropriate SAX events as you read your data.

  3. Modify SimpleCSVProcessor to utilize your custom parser instead of CSVXMLReader.

For example, you might want to write a custom parser that accepts a SQL statement as input rather than a CSV file. Your parser could then connect to a database, issue the query, and fire SAX events for each row in the ResultSet. This makes it very easy to extract data from any relational database without writing a lot of custom code. This also eliminates the intermediate step of JDOM or DOM production because the SAX events are fed directly into JAXP for transformation.

5.3.6. Feeding JDOM Output into JAXP

The DOM API is tedious to use, so many Java programmers opt for JDOM instead. The typical usage pattern is to generate XML dynamically using JDOM and then somehow transform that into a web page using XSLT. This presents a problem because JAXP does not provide any direct implementation of the javax.xml.Source interface that integrates with JDOM.[22] There are at least three available options:

[22] As this is being written, members of the JDOM community are writing a JDOM implementation of javax.xml.Source that will directly integrate with JAXP.

  • Use org.jdom.output.SAXOutputter to pipe SAX 2 events from JDOM to JAXP.

  • Use org.jdom.output.DOMOutputter to convert the JDOM tree to a DOM tree, and then use javax.xml.transform.dom.DOMSource to read the data into JAXP.

  • Use org.jdom.output.XMLOutputter to serialize the JDOM tree to XML text, and then use java.xml.transform.stream.StreamSource to parse the XML back into JAXP.

5.3.6.1. JDOM to SAX approach

The SAX approach is generally preferable to other approaches. Its primary advantage is that it does not require an intermediate transformation to convert the JDOM tree into a DOM tree or text. This offers the lowest memory utilization and potentially the fastest performance.

In support of SAX, JDOM offers the org.jdom.output.SAXOutputter class. The following code fragment demonstrates its usage:

TransformerFactory transFact = TransformerFactory.newInstance( );
if (transFact.getFeature(SAXTransformerFactory.FEATURE)) {
    SAXTransformerFactory stf = (SAXTransformerFactory) transFact;
    // the 'stylesheet' parameter is an instance of JAXP's
    // javax.xml.transform.Templates interface
    TransformerHandler transHand = stf.newTransformerHandler(stylesheet);

    // result is a Result instance
    transHand.setResult(result);
    SAXOutputter saxOut = new SAXOutputter(transHand);
    // the 'jdomDoc' parameter is an instance of JDOM's
    // org.jdom.Document class. In contains the XML data
    saxOut.output(jdomDoc);
} else {
    System.err.println("SAXTransformerFactory is not supported");
}

5.3.6.2. JDOM to DOM approach

The DOM approach is generally a little slower and will not work if JDOM uses a different DOM implementation than JAXP. JDOM, like JAXP, can utilize different DOM implementations behind the scenes. If JDOM refers to a different version of DOM than JAXP, you will encounter exceptions when you try to perform the transformation. Since JAXP uses Apache's Crimson parser by default, you can configure JDOM to use Crimson with the org.jdom.adapters.CrimsonDOMAdapter class. The following code shows how to convert a JDOM Document into a DOM Document:

org.jdom.Document jdomDoc = createJDOMDocument( );
// add data to the JDOM Document
 ...

// convert the JDOM Document into a DOM Document
org.jdom.output.DOMOutputter domOut = new org.jdom.output.DOMOutputter(
        "org.jdom.adapters.CrimsonDOMAdapter");
org.w3c.dom.Document domDoc = domOut.output(jdomDoc);

The second line is highlighted because it is likely to give you the most problems. When JDOM converts its internal object tree into a DOM object tree, it must use some underlying DOM implementation. In many respects, JDOM is similar to JAXP because it delegates many tasks to underlying implementation classes. The DOMOutputter constructors are overloaded as follows:

// use the default adapter class
public DOMOutputter( )

// use the specified adapter class
public DOMOutputter(String adapterClass)

The first constructor shown here will use JDOM's default DOM parser, which is not necessarily the same DOM parser that JAXP uses. The second method allows you to specify the name of an adapter class, which must implement the org.jdom.adapters.DOMAdapter interface. JDOM includes standard adapters for all of the widely used DOM implementations, or you could write your own adapter class.

5.3.6.3. JDOM to text approach

In the final approach listed earlier, you can utilize java.io.StringWriter and java.io.StringReader. First create the JDOM data as usual, then use org.jdom.output.XMLOutputter to convert the data into a String of XML:

StringWriter sw = new StringWriter( );
org.jdom.output.XMLOutputter xmlOut
        = new org.jdom.output.XMLOutputter("", false);
xmlOut.output(jdomDoc, sw);

The parameters for XMLOutputter allow you to specify the amount of indentation for the output along with a boolean flag indicating whether or not linefeeds should be included in the output. In the code example, no spaces or linefeeds are specified in order to minimize the size of the XML that is produced. Now that the StringWriter contains your XML, you can use a StringReader along with javax.xml.transform.stream.StreamSource to read the data into JAXP:

StringReader sr = new StringReader(sw.toString( ));
Source xmlSource = new javax.xml.transform.stream.StreamSource(sr);

The transformation can then proceed just as it did in Example 5-4. The main drawback to this approach is that the XML, once converted to text form, must then be parsed back in by JAXP before the transformation can be applied.

Previous | Home | Next
5.2. Introduction to JAXP 1.1 | Book Index | 5.4. Stylesheet Compilation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Stylesheet Compilation (Java and XSLT)

 Java and XSLTSearch this book

5.4. Stylesheet Compilation

XSLT is a programming language, expressed using XML syntax. This is not for the benefit of the computer, but rather for human interpretation. Before the stylesheet can be processed, it must be converted into some internal machine-readable format. This process should sound familiar, because it is the same process used for every high-level programming language. You, the programmer, work in terms of the high-level language, and an interpreter or compiler converts this language into some machine format that can be executed by the computer.

Interpreters analyze source code and translate it into machine code with each execution. In this case of XSLT, this requires that the stylesheet be read into memory using an XML parser, translated into machine format, and then applied to your XML data. Performance is the obvious problem, particularly when you consider that stylesheets rarely change. Typically, the stylesheets are defined early on in the development process and remain static, while XML data is generated dynamically with each client request.

A better approach is to parse the XSLT stylesheet into memory once, compile it to machine-format, and then preserve that machine representation in memory for repeated use. This is called stylesheet compilation and is no different in concept than the compilation of any programming language.

5.4.1. Templates API

Different XSLT processors implement stylesheet compilation differently, so JAXP includes the javax.xml.transform.Templates interface to provide consistency. This is a relatively simple interface with the following API:

public interface Templates {
    java.util.Properties getOutputProperties( );
    javax.xml.transform.Transformer newTransformer( )
            throws TransformerConfigurationException;
}

The getOutputProperties( ) method returns a clone of the properties associated with the <xsl:output> element, such as method="xml", indent="yes", and encoding="UTF-8". You might recall that java.util.Properties (a subclass of java.util.Hashtable) provides key/value mappings from property names to property values. Since a clone, or deep copy, is returned, you can safely modify the Properties instance and apply it to a future transformation without affecting the compiled stylesheet that the instance of Templates represents.

The newTransformer( ) method is more commonly used and allows you to obtain a new instance of a class that implements the Transformer interface. It is this Transformer object that actually allows you to perform XSLT transformations. Since the implementation of the Templates interface is hidden by JAXP, it must be created by the following method on javax.xml.transform.TransformerFactory:

public Templates newTemplates(Source source)
        throws TransformerConfigurationException

As in earlier examples, the Source may obtain the XSLT stylesheet from one of many locations, including a filename, a system identifier, or even a DOM tree. Regardless of the original location, the XSLT processor is supposed to compile the stylesheet into an optimized internal representation.

Whether the stylesheet is actually compiled is up to the implementation, but a safe bet is that performance will continually improve over the next several years as these tools stabilize and vendors have time to apply optimizations.

Figure 5-6 illustrates the relationship between Templates and Transformer instances.

Figure 5-6. Relationship between Templates and Transformer

Thread safety is an important issue in any Java application, particularly in a web context where many users share the same stylesheet. As Figure 5-6 illustrates, an instance of Templates is thread-safe and represents a single stylesheet. During the transformation process, however, the XSLT processor must maintain state information and output properties specific to the current client. For this reason, a separate Transformer instance must be used for each concurrent transformation.

Transformer is an abstract class in JAXP, and implementations should be lightweight. This is an important goal because you will typically create many copies of Transformer, while the number of Templates is relatively small. Transformer instances are not thread-safe, primarily because they hold state information about the current transformation. Once the transformation is complete, however, these objects can be reused.

5.4.2. A Stylesheet Cache

XSLT transformations commonly occur on a shared web server with a large number of concurrent users, so it makes sense to use Templates whenever possible to optimize performance. Since each instance of Templates is thread-safe, it is desirable to maintain a single copy shared by many clients. This reduces the number of times your stylesheets have to be parsed into memory and compiled, as well as the overall memory footprint of your application.

The code shown in Example 5-10 illustrates a custom XSLT stylesheet cache that automates the mundane tasks associated with creating Templates instances and storing them in memory. This cache has the added benefit of checking the lastModified flag on the underlying file, so it will reload itself whenever the XSLT stylesheet is modified. This is highly useful in a web-application development environment because you can make changes to the stylesheet and simply click on Reload on your web browser to see the results of the latest edits.

Example 5-10. StylesheetCache.java

package com.oreilly.javaxslt.util;

import java.io.*;
import java.util.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * A utility class that caches XSLT stylesheets in memory.
 *
 */
public class StylesheetCache {
    // map xslt file names to MapEntry instances
    // (MapEntry is defined below)
    private static Map cache = new HashMap( );

    /**
     * Flush all cached stylesheets from memory, emptying the cache.
     */
    public static synchronized void flushAll( ) {
        cache.clear( );
    }

    /**
     * Flush a specific cached stylesheet from memory.
     *
     * @param xsltFileName the file name of the stylesheet to remove.
     */
    public static synchronized void flush(String xsltFileName) {
        cache.remove(xsltFileName);
    }

    /**
     * Obtain a new Transformer instance for the specified XSLT file name.
     * A new entry will be added to the cache if this is the first request
     * for the specified file name.
     *
     * @param xsltFileName the file name of an XSLT stylesheet.
     * @return a transformation context for the given stylesheet.
     */
    public static synchronized Transformer newTransformer(String xsltFileName)
            throws TransformerConfigurationException {
        File xsltFile = new File(xsltFileName);

        // determine when the file was last modified on disk
        long xslLastModified = xsltFile.lastModified( );
        MapEntry entry = (MapEntry) cache.get(xsltFileName);

        if (entry != null) {
            // if the file has been modified more recently than the
            // cached stylesheet, remove the entry reference
            if (xslLastModified > entry.lastModified) {
                entry = null;
            }
        }

        // create a new entry in the cache if necessary
        if (entry == null) {
            Source xslSource = new StreamSource(xsltFile);

            TransformerFactory transFact = TransformerFactory.newInstance( );
            Templates templates = transFact.newTemplates(xslSource);

            entry = new MapEntry(xslLastModified, templates);
            cache.put(xsltFileName, entry);
        }

        return entry.templates.newTransformer( );
    }

    // prevent instantiation of this class
    private StylesheetCache( ) {
    }

    /**
     * This class represents a value in the cache Map.
     */
    static class MapEntry {
        long lastModified;  // when the file was modified
        Templates templates;

        MapEntry(long lastModified, Templates templates) {
            this.lastModified = lastModified;
            this.templates = templates;
        }
    }
}

Because this class is a singleton, it has a private constructor and uses only static methods. Furthermore, each method is declared as synchronized in an effort to avoid potential threading problems.

The heart of this class is the cache itself, which is implemented using java.util.Map:

private static Map cache = new HashMap( );

Although HashMap is not thread-safe, the fact that all of our methods are synchronized basically eliminates any concurrency issues. Each entry in the map contains a key/value pair, mapping from an XSLT stylesheet filename to an instance of the MapEntry class. MapEntry is a nested class that keeps track of the compiled stylesheet along with when its file was last modified:

static class MapEntry {
    long lastModified;  // when the file was modified
    Templates templates;

    MapEntry(long lastModified, Templates templates) {
        this.lastModified = lastModified;
        this.templates = templates;
    }
}

Removing entries from the cache is accomplished by one of two methods:

public static synchronized void flushAll( ) {
    cache.clear( );
}

public static synchronized void flush(String xsltFileName) {
    cache.remove(xsltFileName);
}

The first method merely removes everything from the Map, while the second removes a single stylesheet. Whether you use these methods is up to you. The flushAll method, for instance, should probably be called from a servlet's destroy( ) method to ensure proper cleanup. If you have many servlets in a web application, each servlet may wish to flush specific stylesheets it uses via the flush(...) method. If the xsltFileName parameter is not found, the Map implementation silently ignores this request.

The majority of interaction with this class occurs via the newTransformer method, which has the following signature:

public static synchronized Transformer newTransformer(String xsltFileName)
        throws TransformerConfigurationException {

The parameter, an XSLT stylesheet filename, was chosen to facilitate the "last accessed" feature. We use the java.io.File class to determine when the file was last modified, which allows the cache to automatically reload itself as edits are made to the stylesheets. Had we used a system identifier or InputStream instead of a filename, the auto-reload feature could not have been implemented. Next, the File object is created and its lastModified flag is checked:

File xsltFile = new File(xsltFileName);

// determine when the file was last modified on disk
long xslLastModified = xsltFile.lastModified( );

The compiled stylesheet, represented by an instance of MapEntry, is then retrieved from the Map. If the entry is found, its timestamp is compared against the current file's timestamp, thus allowing auto-reload:

MapEntry entry = (MapEntry) cache.get(xsltFileName);

if (entry != null) {
    // if the file has been modified more recently than the
    // cached stylesheet, remove the entry reference
    if (xslLastModified > entry.lastModified) {
        entry = null;
    }
}

Next, we create a new entry in the cache if the entry object reference is still null. This is accomplished by wrapping a StreamSource around the File object, instantiating a TransformerFactory instance, and using that factory to create our Templates object. The Templates object is then stored in the cache so it can be reused by the next client of the cache:

// create a new entry in the cache if necessary
if (entry == null) {
    Source xslSource = new StreamSource(xsltFile);

    TransformerFactory transFact = TransformerFactory.newInstance( );
    Templates templates = transFact.newTemplates(xslSource);

    entry = new MapEntry(xslLastModified, templates);
    cache.put(xsltFileName, entry);
}

Finally, a brand new Transformer is created and returned to the caller:

return entry.templates.newTransformer( );

Returning a new Transformer is critical because, although the Templates object is thread-safe, the Transformer implementation is not. Each caller gets its own copy of Transformer so multiple clients do not collide with one another.

One potential improvement on this design could be to add a lastAccessed timestamp to each MapEntry object. Another thread could then execute every couple of hours to flush map entries from memory if they have not been accessed for a period of time. In most web applications, this will not be an issue, but if you have a large number of pages and some are seldom accessed, this could be a way to reduce the memory usage of the cache.

Another potential modification is to allow javax.xml.transform.Source objects to be passed as a parameter to the newTransformer method instead of as a filename. However, this would make the auto-reload feature impossible to implement for all Source types.

Previous | Home | Next
5.3. Input and Output | Book Index | 6. Servlet Basics and XSLT
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Servlet Basics and XSLT (Java and XSLT)

 Java and XSLTSearch this book

Chapter 6. Servlet Basics and XSLT

Contents:

Servlet Syntax
WAR Files and Deployment
Another Servlet Example
Stylesheet Caching Revisited
Servlet Threading Issues

XSLT and servlets are a natural fit. Java is a cross-platform programming language, XML provides portable data, and XSLT provides a way to transform that data without cluttering up your servlet code with HTML. Because your data can be transformed into many different formats, you can also achieve portability across a variety of browsers and other devices. Best of all, a clean separation between data, presentation, and programming logic allow changes to be made to the look and feel of a web site without digging in to Java code. This makes it possible, for example, to sell highly customizable web applications. You can encourage your customers to modify the XSLT stylesheets to create custom page layouts and corporate logos, while preventing access to your internal Java business logic.

As discussed in previous chapters, an initial challenge faced with XSLT and servlets is the initial configuration. Getting started with a web application is typically harder than client-only applications because there are more pieces to assemble. With a Swing application, for instance, you can start with a single class that has a main( ) method. But with a web application, you must create an XML deployment descriptor in addition to the servlet, package everything up into a WAR file, and properly deploy to a servlet container. When errors occur, you see something like "HTTP 404 -- File not found," which is not particularly helpful.

The goal of this chapter is to introduce servlet syntax with particular emphasis on configuration and deployment issues. Once servlet syntax has been covered, integration with XSLT stylesheets and XML is covered, illustrated by the implementation of a basic web application. By the time you have worked through this material, you should have confidence to move on to the more complicated examples found in the remainder of this book.

6.1. Servlet Syntax

Servlet architecture was covered in Chapter 4, "Java-Based Web Technologies", along with comparisons to many other approaches. The architecture of a system is a mile-high view, ignoring implementation details so you can focus on the big picture. We now need to dig into the low-level syntax issues to proceed with the really interesting examples in later chapters. For a complete discussion of servlets, check out Jason Hunter's Java Servlet Programming (O'Reilly). Be sure to look for the second edition because so much has changed in the servlet world since this book was first published.

6.1.1. Splash Screen Servlet Example

Our first servlet example will produce an application splash screen. The servlet will receive a request from a browser and output a simple HTML web page. Figure 6-1 contains the class diagram for SplashScreenServlet, which extends from HttpServlet.

Figure 6-1. SplashScreenServlet class diagram

When writing servlets, you almost always extend from HttpServlet. In our example, we override the doGet( ) method, which is called every time the browser issues an HTTP GET request to the server. GET requests occur whenever the user types in a URL, clicks on a hyperlink, or submits an HTML form with GET as the method attribute. The other common type of request is POST, which is used by HTML forms with POST as the method attribute. For example:

<form action="someServlet" method="POST">
...form contents
</form>

In the case of POST requests, your servlet simply overrides the doPost( ) method instead of doGet( ). Each of these methods takes two parameters: HttpServletRequest and HttpServletResponse. The request contains information from the client to the servlet, and the response allows the servlet to send data back to the client. This correlates directly to the request/response nature of HTTP itself. Example 6-1 contains the complete source code for our simple servlet.

Example 6-1. SplashScreenServlet.java

package chap6;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * A simple Servlet example that displays a "Splash Screen"
 * for a web application.
 */
public class SplashScreenServlet extends HttpServlet {
  public String getServletInfo( ) {
    return "Shows an application splash screen.";
  }

  protected void doGet(HttpServletRequest request,
                    HttpServletResponse response)
      throws IOException, ServletException {

    // demonstrate how to get parameters from the request
    String nextURL = request.getParameter("nextURL");
    if (nextURL == null) {
      nextURL = "/";
    }

    response.setContentType("text/html");
    PrintWriter pw = response.getWriter( );
    pw.println("<html>");
    pw.println("<head><title>Splash Screen</title></head>");
    pw.println("<body>");

    pw.println("<div align='center' style='border: 1px navy solid;'>");
    pw.println("<h1>Welcome to Java and XSLT</h1>");
    pw.println("<h3>O'Reilly and Associates</h3>");
    pw.println("<p>First Edition, 2001</p><hr>");
    pw.println("<a href='" + nextURL + "'>Click here to continue...</a>");
    pw.println("</div>");
    pw.println("</body>");
    pw.println("</html>");
  }
}

Beginning with the import statements, note that the servlet API is in the javax.servlet and javax.servlet.http packages. These packages are not part of the Java 2 Standard Edition; they are considered a required API of the Java 2 Platform Enterprise Edition. Although many servlet implementations are available, Apache's Tomcat is the reference implementation officially sanctioned by Sun. Every example in this book works with Version 4.0 of Tomcat and should also work on any compliant servlet implementation. You can download Tomcat at http://jakarta.apache.org.

As shown in Figure 6-1, SplashScreenServlet is a subclass of HttpServlet. The first method we override is getServletInfo( ) , which simply returns a brief description of this servlet. Although optional, this text will show up in the administrative console of many servlet containers.

The doGet( ) method is next, which is designed to handle each client request. It is important to remember that this method needs to be thread-safe, because many clients could potentially share this servlet instance and call doGet( ) concurrently. You may notice that doGet( ) is a protected method. The call sequence is as follows:

  1. The servlet container invokes the service( ) method on HttpServlet.

  2. HttpServlet figures out the type of request (GET, POST, ...).

  3. HttpServlet invokes the corresponding method (doGet( ), doPost( ), ...).

Since the doGet( ) method is called from its parent class, it can be protected. If you do not override the doGet( ) method, the default behavior in HttpServlet is to return an error page back to the client. If you also want to support POST, you must override the doPost( ) method. One common technique is to have the doGet( ) method call the doPost( ) method, or vice-versa. This allows the same servlet to support both GET and POST without duplicating any code.

The doGet( ) implementation is very straightforward. The first thing it does is check for the existence of a parameter called nextURL. This is part of the request that the browser issues to the servlet. For example, typing the following URL into your browser will include the next URL parameter:

http://localhost:8080/chap6/splash?nextURL=http://www.oreilly.com

If the nextURL parameter is not specified, its value will be null. For this reason, servlets must always check for null when getting parameters from the request:

String nextURL = request.getParameter("nextURL");
if (nextURL == null) {
  nextURL = "/";
}

In our example, null causes an error, so we replace nextURL with a forward slash character (/). As you might guess, this is a relative URL that points to the root directory. In the case of a servlet running on Tomcat, the root directory will point to Tomcat's home page. This is not the same as the root directory on your file system. In Tomcat, the root directory can be found under TOMCAT_HOME/webapps/ROOT, where TOMCAT_HOME points to the installation directory of Tomcat.

The getParameter( ) method is also used for retrieving values from an HTML form. When an HTML form is submitted, each component on the form should have a name associated with it, such as firstName, lastName, or ssn. The servlet retrieves the form values simply by calling request.getParameter( ) for each form element. It is always a good idea to check for null and trim whitespace before accepting any of these parameters. When the form element is missing, the parameter value will be null. This could indicate an error in your HTML or perhaps an intentional attack on your web site.

The HttpServletResponse class provides access to either a PrintWriter or an OutputStream, depending on whether you wish to send text or binary data to the client. For HTML or XML data, use the HttpServletResponse.getWriter( ) method. For images or other types of binary data, use the HttpServletResponse.getOutputStream( ) method. You may also note that we set the content type of the response prior to getting the writer:

response.setContentType("text/html");
PrintWriter pw = response.getWriter( );

This is important because the HTTP response consists of a header followed by the actual content. The content type is one of the header values, so it must be sent prior to the actual data. Without going into too many servlet details, it is a good practice to always set the content type before getting the writer. In future examples, we will occasionally use text/xml as the content type, but only when sending raw XML data to the client.

The remainder of SplashScreenServlet simply prints out an HTML response:

pw.println("<html>");
pw.println("<head><title>Splash Screen</title></head>");
pw.println("<body>");

pw.println("<div align='center' style='border: 1px navy solid;'>");
pw.println("<h1>Welcome to Java and XSLT</h1>");
pw.println("<h3>O'Reilly and Associates</h3>");
pw.println("<p>First Edition, 2001</p><hr>");
pw.println("<a href='" + nextURL + "'>Click here to continue...</a>");
pw.println("</div>");
pw.println("</body>");
pw.println("</html>");

As you can see, the nextURL parameter is used to create a hyperlink to the next page. This is why a value of null is unacceptable for this example.

This approach works fine for simple examples, but quickly gets out of hand for complex pages. This is because all but the most basic web pages require hundreds, if not thousands, of lines of HTML to create fancy tables, colors, and graphics. For reasons discussed in Chapter 5, "XSLT Processing with Java", hardcoding that HTML into the servlet is simply unacceptable in a sophisticated web application.

Previous | Home | Next
5.4. Stylesheet Compilation | Book Index | 6.2. WAR Files and Deployment
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


WAR Files and Deployment (Java and XSLT)

 Java and XSLTSearch this book

6.2. WAR Files and Deployment

In the servlet model, Web Application Archive (WAR) files are the unit of deployment. WAR files enable portability across a wide range of servlet containers regardless of the vendor. The good news is that WAR files are very easy to create and require only that you carefully follow the guidelines for file and directory names. If you are careful to avoid spelling errors and misplaced files, you should not have any problem with WAR files.

6.2.1. WAR Files

Figure 6-2 shows the standard structure of a WAR file. Since a WAR file is really just a JAR file with a .war extension, you can utilize the jar command to create your WAR files.

Figure 6-2. WAR file structure

To create a WAR file, simply arrange your files into the directory structure shown in Figure 6-2 and issue the following command from the directory that contains index.html:[23]

[23] index.html is the "home page" for a web application.

jar -cvfM ../appname.war

This command assumes that the WAR file will be placed in the parent of your current working directory; the forward slash (/ ) works on Windows as well as Unix clients. Once the WAR file has been created, you can view its contents by changing to its directory and issuing the following command:

jar -tvf appname.war .

This shows the table of contents for the WAR file, which must match the structure shown in Figure 6-2.

The topmost directory in the WAR file is publicly accessible to web browsers and should contain your JSP and HTML files. You can also create subdirectories, which will also be visible to the client. A common practice is to create an images directory for storing your graphic files.

The WEB-INF directory is always hidden from clients that access your web application. The deployment descriptor, web.xml, is located here, as are the classes and lib directories. As Figure 6-2 indicates, the classes directory becomes available to your application's ClassLoader. Any JAR files contained in the lib directory are also available to your code, making it very easy to deploy third-party libraries along with a web application. The folder other_directories can be anything you want and will also be hidden from clients since it resides under the WEB-INF directory. Although clients cannot see any of these directories and files directly, your servlet can access these resources programmatically and then deliver that content.

6.2.2. Deployment Descriptor

The deployment descriptor is always called web.xml and must be placed directly in the WEB-INF directory of your web application. The job of the deployment descriptor is to provide the servlet container with complete configuration information about a web application. This may include security attributes, aliases for servlets and other resources, initialization parameters, and even graphical icons for Integrated Development Environments (IDEs) to utilize. For our needs, a very small subset of this functionality will be sufficient. For SplashScreenServlet, we need to list the Java class of the servlet, an alias for that servlet, and a URL mapping. The complete deployment descriptor for SplashScreenServlet is listed in Example 6-2.

Example 6-2. web.xml for SplashScreenServlet.java

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
  <servlet>
    <!-- define an alias for the Servlet -->
    <servlet-name>splashScreen</servlet-name>
    <servlet-class>chap6.SplashScreenServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <!-- associate the Servlet with a URL pattern -->
    <servlet-name>splashScreen</servlet-name>
    <url-pattern>/splash/*</url-pattern>
  </servlet-mapping>
</web-app>

The DOCTYPE is a required element of a deployment descriptor and must match what is shown in Example 6-2. The only caveat is that newer versions of the servlet specification, such as Version 2.3, use a different version number in the deployment descriptor. Unless you are using 2.3 features, however, you should stick with 2.2 to remain compatible with as many servlet containers as possible.

A servlet definition lists the fully qualified package and class name of the servlet class, as well a name for that servlet. Whenever another section in the deployment descriptor wishes to reference this particular servlet, it uses the name specified here:

<servlet>
  <servlet-name>splashScreen</servlet-name>
  <servlet-class>chap6.SplashScreenServlet</servlet-class>
</servlet>

As you can see in Example 6-2, the servlet mapping uses this name in order to associate a URL pattern with this particular servlet. This pattern will show up in the address that users type into their web browsers when they access this servlet. In this case, the URL to SplashScreenServlet is:

http://hostname:port/chap6/splash

This is the form that Tomcat defaults to, having the following components:

hostname:port
Typically localhost:8080, although Tomcat can be configured to run on any port number

chap6
The name of your web application, which is deployed in chap6.war for this example

splash
Part of the URL pattern for the servlet

Wildcards in the URL pattern indicate that any text will match. Since the deployment descriptor listed /splash/* as the pattern, any of the following URLs also invoke SplashScreenServlet:

  • http://localhost:8080/chap6/splash/

  • http://localhost:8080/chap6/splash/whatever.html

  • http://localhost:8080/chap6/splash/a/b/c

6.2.3. Deploying SplashScreenServlet to Tomcat

The simple steps for getting SplashScreenServlet up and running are to compile the code, create the deployment descriptor listed in Example 6-2, and create the WAR file using the jar utility. The WAR file contents for this servlet are shown in Figure 6-3.

Figure 6-3. SplashScreenServlet WAR file

Once you have created chap6.war, be sure to execute jar -tvf chap6.war to confirm that the contents are structured properly. The final step is to simply copy the entire JAR file to Tomcat's webapps directory.

NOTE: If a WAR file is copied into the webapps directory while Tomcat is running, it will not be recognized. Simply restart Tomcat to begin using the web application.

Once the WAR file has been copied, you can execute startup.bat or startup.sh in Tomcat's bin directory and then enter http://localhost:8080/chap6/splash into your favorite web browser. If you see error messages, check to see that the JAVA_HOME and TOMCAT_HOME environment variables are properly set. You can also look in Tomcat's webapps directory to see if the WAR file is properly expanded. When a web application is first invoked, Tomcat expands the WAR file into its actual directory structure. When you look in the webapps directory, you should see chap6.war as well as the chap6 directory.

If all else fails, check the documentation for Tomcat, double check your deployment descriptor, and try the example servlets that come with Tomcat. To see the Tomcat home page, start Tomcat and visit http://localhost:8080. If this does not work, then something more fundamental is wrong with your Tomcat installation.

6.2.4. Servlet API Highlights

We will see more complex servlets throughout this book, but a recurring theme is to minimize dependence on obscure servlet tricks and focus instead on using XML and XSLT to generate a majority of the content in your web application. To make this happen, it is necessary to look at a few of the commonly used classes that are part of the servlet package.

The javax.servlet.ServletConfig class provides initialization parameters to a servlet at startup time. Each servlet has the following method, which is called once when the servlet is first initialized:

public void init(ServletConfig config) throws ServletException

The ServletConfig object provides name/value String pairs used to configure servlets without hardcoding values into the application code. For example, you might write code that looks like this:

String xmlLocation = config.getInitParameter("xmlLocation");

Since xmlLocation is an initialization parameter that is part of the XML deployment descriptor, its value does not have to be hardcoded into your application. For additional examples, see Section 6.3.6, "Locating Stylesheets with Initialization Parameters" later in this chapter.

Another important class is javax.servlet.ServletContext . This class does a lot more than ServletConfig, and its instance is shared among a group of servlets. Use ServletConfig to obtain a reference to the ServletContext:

// config is an instance of ServletConfig
ServletContext ctx = config.getServletContext( );

Later in this book, we will focus on ServletContext's ability to locate resources in a portable way. You may be familiar with the getResource( ) and getResourceAsStream( ) methods on java.lang.Class. These methods allow you to locate files and directories based on the system CLASSPATH.

ServletContext provides its own getResource( ) and getResourceAsStream( ) methods, but they are not based on CLASSPATH. Instead, the directory locations are based on the location of the current web application. For example, you can write something such as:

context.getResource("/WEB-INF/stylesheets/home.xslt")

to load a stylesheet from the current WAR file. Regardless of where Tomcat was installed, this approach will always locate the stylesheet without hardcoding a path name such as C:\path\....

Previous | Home | Next
6. Servlet Basics and XSLT | Book Index | 6.3. Another Servlet Example
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Another Servlet Example (Java and XSLT)

 Java and XSLTSearch this book

6.3. Another Servlet Example

In our next example, the servlet will utilize DOM and XSLT to create its web pages. This achieves our goal of separation between data and presentation, making it possible to fully customize the HTML output without making any changes to the Java code. Although an XML approach makes the code more complex for a small example program such as this, the benefits quickly outweigh the costs as web applications get more sophisticated. The same is true for an Enterprise JavaBeans approach. For a trivial program, the configuration requirements seem very complex; but as the application complexity increases, the benefits of a sophisticated architecture become obvious.

Our program will consist of two web pages, allowing visitors to enter personal information. The first page will prompt for their name, phone, and email, and the second page will display a summary of the data that was entered. The first page does validation, forcing the user to enter all of the required fields.

6.3.1. Design

The primary goal of this small application is to demonstrate how to use XSLT from a servlet. Specifically, JAXP will be used with DOM to create some dynamic XML data, then XSLT stylesheets will be used to transform that data into HTML. The design is presented in Figure 6-4.

Figure 6-4. Personal data design

As Figure 6-4 shows, PersonalDataServlet is a subclass of HttpServlet. This servlet overrides both doGet( ) and doPost( ). When the visitor first visits this web site, an HTTP GET request causes the doGet( ) method to be called, which shows a form that allows the user to enter his or her information. When they hit the submit button on the web page, the doPost( ) method validates all form fields and shows the confirmation page if everything is valid. If one or more fields are missing, the form is redisplayed with an error message.

The PersonalData class simply holds data that the user has entered, and is located via the HttpSession. Each visitor gets his or her own copy of HttpSession, therefore they get their own copy of PersonalData. In order to convert this data to XML, a separate helper class called PersonalDataXML was created.

The decision to use a separate helper class for XML generation was not arbitrary. Many people like to put code directly into classes like PersonalData, such as a getXML( ) method, that performs this task. By placing the XML generation logic into a totally separate class, however, it will be easier to migrate to a technology like JDOM without breaking current clients in any way. A new class called PersonalDataJDOM could be added to the system while preserving all of the existing code. This approach also keeps the PersonalData class smaller, since all it has to do is hang on to the data.

The first web page is shown in Figure 6-5. As you can see, required fields are marked with an asterisk (*). This screen is rendered using editPersonalData.xslt.

Figure 6-5. Blank personal information form

Figure 6-6 shows how this same screen looks after the user clicks on the Submit button. If data is missing, an error message is displayed in red and required fields are marked in bold. Any other error messages are also displayed in red. This view is also rendered using editPersonalData.xslt.

Figure 6-6. Personal information form with errors

Once all of the data has been entered properly, the screen shown in Figure 6-7 is displayed. Unlike the previous examples, this screen is rendered using confirmPersonalData.xslt. To make changes to any of these screens, one needs to edit only the appropriate stylesheet.

Figure 6-7. Confirmation page

6.3.2. XML and Stylesheets

Deciding how to structure your XML can have significant impact on your ability to customize the output of a web application. In our current example, the same XML file is used for all web pages. This XML is shown in Example 6-3.

Example 6-3. Example XML output

<?xml version="1.0" encoding="UTF-8"?>
<page>
  <!-- the next element is optional: -->
  <!-- <requiredFieldsMissing/> -->
  <personalData>
    <firstName required="true">Eric</firstName>
    <lastName required="true">Burke</lastName>
    <daytimePhone required="true">636-123-4567</daytimePhone>
    <eveningPhone/>
    <email required="true">burke_e@yahoo.com</email>
  </personalData>
</page>

As you can see, the XML is very minimal. None of the captions, such as "First Name:", are included, because they are all specified in the XSLT stylesheets. Even the asterisk character (*) is omitted, giving the XSLT author complete control over how things are rendered. The XML is used only for data, so you can use the stylesheets to include graphics, render the output in a foreign language, or combine page fragments from other sources, such as headers and footers, into your web pages.

The <requiredFieldsMissing/> element is optional. If omitted, the XSLT stylesheet will not display error messages about missing fields. This is useful when the data is generated the first time because all fields will be blank, and you probably don't want to show a bunch of error messages. In our servlet, the doGet( ) method is called when the user first requests this web page, so it is here where we disable this element.

It is important to mention that this XML is used only for documentation purposes and for testing the XSLT stylesheets. Once you move into a production environment, the XML will be generated dynamically using the PersonalData and PersonalDataXML classes, so this static file will not be required. You will probably want to hang on to your static XML, however, as this will make it easier to experiment with changes to the XSLT.

The XSLT stylesheet that creates the HTML form is shown in Example 6-4. The stylesheets are substantially longer than the XML data, which is typical. In a more simplistic approach to servlet development, all of this logic would be hardcoded into the source code as a series of println( ) statements, making the servlet much larger and less flexible.

Example 6-4. editPersonalData.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns="http://www.w3.org/1999/xhtml">
  <xsl:output method="xml" indent="yes" encoding="UTF-8"
     doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
     doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
  <!--

  ***************************************************************
  ** Top level template. Creates the framework for the XHTML page
  ************************************************************-->
  <xsl:template match="/">
    <html>
      <head><title>Edit Personal Information</title></head>
      <body>
        <xsl:apply-templates select="page/personalData"/>
      </body>
    </html>
  </xsl:template>
  <!--

  ***************************************************************
  ** Match the <personalData> element.
  ************************************************************-->
  <xsl:template match="personalData">
    <h3>Personal Information</h3>
    <xsl:if test="../requiredFieldsMissing">
      <div style="color: red; font-size: larger;">
        Error: one or more required fields are missing.
      </div>
    </xsl:if>
    <i>Fields marked with (*) are required.</i>
    <form action="/chap6/personalData" method="post">
      <table border="0" cellpadding="2" cellspacing="0">
        <!-- Select all immediate children, such as firstName,
             lastName, daytimePhone, etc... -->
        <xsl:apply-templates/>
      </table>
      <div align="center">
        <hr/>
        <input type="submit" name="submitBtn" value="Submit"/>
      </div>
    </form>
  </xsl:template>
  <!--

  ***************************************************************
  ** Output a new row in the table for each field.
  ************************************************************-->
  <xsl:template
      match="firstName|lastName|daytimePhone|eveningPhone|email">
    <tr>
      <xsl:if test="@required='true'
                    and ../../requiredFieldsMissing
                    and .=''">
        <xsl:attribute name="style">
          <xsl:text>color:red; font-weight:bold;</xsl:text>
        </xsl:attribute>
      </xsl:if>
      <td>
        <xsl:choose>
          <xsl:when test="name( )='firstName'">
            First Name:</xsl:when>
          <xsl:when test="name( )='lastName'">
            Last Name:</xsl:when>
          <xsl:when test="name( )='daytimePhone'">
            Daytime Phone:</xsl:when>
          <xsl:when test="name( )='eveningPhone'">
            Evening Phone:</xsl:when>
          <xsl:when test="name( )='email'">
            Email:</xsl:when>
        </xsl:choose>
      </td>
      <td>
        <input type="text" name="{name( )}" value="{.}"/>
      </td>
      <td>
        <xsl:if test="@required='true'">*</xsl:if>
      </td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

The first seven lines of editPersonalData.xslt contain boilerplate code that configures the XSLT processor to produce XHTML 1.0 using the transitional DTD. In particular, our result tree uses the <i>...</i> tag, so we cannot use the XHTML strict DTD. The top level template matches the "/" pattern as usual, outputting the framework for the XHTML document.

The next template matches the <personalData> element, producing a heading followed by an optional error message. The error message is displayed if the XML data contains the <requiredFieldsMissing/> element, which is easily determined via the <xsl:if> element:

<xsl:template match="personalData">
  <h3>Personal Information</h3>
  <xsl:if test="../requiredFieldsMissing">
    <div style="color: red; font-size: larger;">
      Error: one or more required fields are missing.
    </div>
  </xsl:if>

This template then produces the <form> element, which specifies that HTTP POST should be used to submit the information. The action attribute indicates that this form will send its data to our servlet. As you will see, the form action[24] matches the URL pattern that we will set up in the deployment descriptor later in this chapter:

[24] To avoid hardcoding the form action in the XSLT stylesheet, pass it as a stylesheet parameter.

<i>Fields marked with (*) are required.</i>
<form action="/chap6/personalData" method="post">

The template finally produces a table so that all of the headings and text fields are properly aligned. As in earlier stylesheet examples, this template creates the table, while another template creates each row in the table:

<table border="0" cellpadding="2" cellspacing="0">
  <!-- Select all immediate children, such as firstName,
       lastName, daytimePhone, etc... -->
  <xsl:apply-templates/>
</table>
<div align="center">
  <hr/>
  <input type="submit" name="submitBtn" value="Submit"/>
</div>
</form>
</xsl:template>

Since this particular instance of <xsl:apply-templates/> does not utilize the select attribute, all child elements will be selected. The next template is designed to match each of the possible types of elements that can appear and will be instantiated once for each occurrence of <firstName>, <lastName>, etc.:

<xsl:template
    match="firstName|lastName|daytimePhone|eveningPhone|email">

This template first produces a <tr> element. If this particular element has a required="true" attribute, the XML data contains <requiredFieldsMissing/>. The value of this element is an empty string, the font is changed to bold and red. This indicates to the user that a required field was missing. The font weight and color are inserted as the style attribute on the <tr> element as follows:

<tr>
  <xsl:if test="@required='true'
                and ../../requiredFieldsMissing
                and .=''">
    <xsl:attribute name="style">
      <xsl:text>color:red; font-weight:bold;</xsl:text>
    </xsl:attribute>
  </xsl:if>

The template then produces its first <td> tag, which contains the caption for the current field. It would be nice if XSLT offered a lookup table mechanism for situations such as this, but <xsl:choose> does get the job done:

<td>
  <xsl:choose>
    <xsl:when test="name( )='firstName'">
      First Name:</xsl:when>
    <xsl:when test="name( )='lastName'">
      Last Name:</xsl:when>
    <xsl:when test="name( )='daytimePhone'">
      Daytime Phone:</xsl:when>
    <xsl:when test="name( )='eveningPhone'">
      Evening Phone:</xsl:when>
    <xsl:when test="name( )='email'">
      Email:</xsl:when>
  </xsl:choose>
</td>

This is still better than hardcoding the captions into the XML or servlet because we can make changes to the stylesheet without recompiling anything. You can even change the captions to a foreign language without affecting any of the Java code, offering remarkable flexibility to web page designers.

Design Choices

The two stylesheets, editPersonalData.xslt and confirmPersonalData.xslt, had a lot in common. To keep things simple, they were written as two independent stylesheets. This is not the only way to implement this code, however. For instance, we could have searched for common functionality and included that functionality from both stylesheets using <xsl:import> or <xsl:include>. This approach did not work here because, although the stylesheets were structured similarly, each template produced different output. As the web site gets more sophisticated, however, you will begin to encounter common page elements such as navigation bars that should not be duplicated in multiple places.

Another approach would be to combine both stylesheets into a single stylesheet and pass a top-level parameter indicating whether to use "edit" mode or "confirm" mode. In this approach, the servlet would pass the parameter to the stylesheet via JAXP's Transformer class and the <xsl:param> element. Inside of the stylesheet, we would write lots of <xsl:choose> or <xsl:if> elements to control the output based on the value of the parameter. The JAXP code would look something like this:

javax.xml.transform.Transformer trans = ...
trans.setParameter("personalDataMode", "edit");

While this approach has its place, it did not make sense for this particular example because every template produced different output. It would have resulted in a more complex solution than simply writing two separate stylesheets. On the other hand, if you encounter a situation where several web pages are almost identical except for a small section that changes, passing a stylesheet parameter is probably the way to go, because you only have to write the conditional logic around the section that changes.

The next column in the table contains the input field:

<td>
  <input type="text" name="{name( )}" value="{.}"/>
</td>

In the XHTML output, this yields a cell containing <input type="text" name="firstName" value="Eric"/>. Finally, the last column in the table contains an asterisk if the field has the required="true" attribute:

<td>
  <xsl:if test="@required='true'">*</xsl:if>
</td>
</tr>
</xsl:template>

The next stylesheet, confirmPersonalData.xslt, is listed in Example 6-5. This stylesheet is shorter because it shows only a summary of what the user entered on the previous page. It does not have to display any error messages or show an HTML form. The overall structure of the stylesheet is identical to editPersonalData.xslt, however, so a line-by-line description is not necessary.

Example 6-5. confirmPersonalData.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns="http://www.w3.org/1999/xhtml">
  <xsl:output method="xml" indent="yes" encoding="UTF-8"
     doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
     doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  <!--

  ***************************************************************
  ** Top level template. Creates the framework for the XHTML page
  ************************************************************-->
  <xsl:template match="/">
    <html>
      <head>
        <title>Personal Data Summary</title>
      </head>
      <body>
        <xsl:apply-templates select="page/personalData"/>
      </body>
    </html>
  </xsl:template>
  <!--

  ***************************************************************
  ** Match the <personalData> element.
  ************************************************************-->
  <xsl:template match="personalData">
    <h2>Thank You!</h2>
    <h3>Your Information...</h3>

    <table border="0" cellpadding="2" cellspacing="0">
      <!-- Select all immediate children, such as firstName,
           lastName, daytimePhone, etc... -->
      <xsl:apply-templates/>
    </table>

    <p><a href="/chap6/personalData">Click here
         to edit this information...</a></p>

  </xsl:template>
  <!--

  ***************************************************************
  ** Output a new row in the table for each field.
  ************************************************************-->
  <xsl:template
    match="firstName|lastName|daytimePhone|eveningPhone|email">

    <tr>
      <td>
        <xsl:choose>
          <xsl:when test="name( )='firstName'">
            First Name:</xsl:when>
          <xsl:when test="name( )='lastName'">
            Last Name:</xsl:when>
          <xsl:when test="name( )='daytimePhone'">
            Daytime Phone:</xsl:when>
          <xsl:when test="name( )='eveningPhone'">
            Evening Phone:</xsl:when>
          <xsl:when test="name( )='email'">
            Email:</xsl:when>
        </xsl:choose>
      </td>
      <td>
        <b><xsl:value-of select="."/></b>
      </td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

6.3.3. Source Code

The first piece of source code to examine is shown in Example 6-6. The PersonalData class is simply a data holder and does not contain any XML code or database code. By keeping classes like this simple, you can easily write standalone unit tests that verify if your code is written properly. If this code were written as part of the servlet instead of a standalone class, it would be very difficult to test outside of the web browser environment.

Example 6-6. PersonalData.java

package chap6;

/**
 * A helper class that stores personal information. XML generation
 * is intentionally left out of this class. This class ensures
 * that its data cannot be null, nor can it contain extra
 * whitespace.
 */
public class PersonalData {
    private String firstName;
    private String lastName;
    private String daytimePhone;
    private String eveningPhone;
    private String email;

    public PersonalData( ) {
        this("", "", "", "", "");
    }

    public PersonalData(String firstName, String lastName,
            String daytimePhone, String eveningPhone, String email) {
        this.firstName = cleanup(firstName);
        this.lastName = cleanup(lastName);
        this.daytimePhone = cleanup(daytimePhone);
        this.eveningPhone = cleanup(eveningPhone);
        this.email = cleanup(email);
    }

    /**
     * <code>eveningPhone</code> is the only optional field.
     *
     * @return true if all required fields are present.
     */
    public boolean isValid( ) {
        return this.firstName.length( ) > 0
                && this.lastName.length( ) > 0
                && this.daytimePhone.length( ) > 0
                && this.email.length( ) > 0;
    }

    public void setFirstName(String firstName) {
        this.firstName = cleanup(firstName);
    }

    public void setLastName(String lastName) {
        this.lastName = cleanup(lastName);
    }

    public void setDaytimePhone(String daytimePhone) {
        this.daytimePhone = cleanup(daytimePhone);
    }

    public void setEveningPhone(String eveningPhone) {
        this.eveningPhone = cleanup(eveningPhone);
    }

    public void setEmail(String email) {
        this.email = cleanup(email);
    }

    public String getFirstName( ) { return this.firstName; }
    public String getLastName( ) { return this.lastName; }
    public String getDaytimePhone( ) { return this.daytimePhone; }
    public String getEveningPhone( ) { return this.eveningPhone; }
    public String getEmail( ) { return this.email; }

    /**
     * Cleanup the String parameter by replacing null with an
     * empty String, and by trimming whitespace from non-null Strings.
     */
    private static String cleanup(String str) {
        return (str != null) ? str.trim( ) : "";
    }
}

Although the PersonalData class is merely a data holder, it can include simple validation logic. For example, the default constructor initializes all fields to non-null values:

public PersonalData( ) {
    this("", "", "", "", "");
    }

Additionally, all of the set methods make use of the private cleanup( ) method:

private static String cleanup(String str) {
    return (str != null) ? str.trim( ) : "";
}

As a result, instances of this class will avoid null references and whitespace, eliminating the need to perform constant error checking in the servlet and XML generation classes. Trimming whitespace is particularly helpful because a user may simply press the spacebar in one of the required fields, potentially bypassing your validation rules. The PersonalData class also contains an explicit validation method that checks for all required fields:

public boolean isValid( ) {
    return this.firstName.length( ) > 0
            && this.lastName.length( ) > 0
            && this.daytimePhone.length( ) > 0
            && this.email.length( ) > 0;
}

The only field that is not required is eveningPhone, so it is not checked here. By putting this method into this class, we further reduce the work required of the servlet.

The next class, PersonalDataXML, is presented in Example 6-7. It is responsible for converting PersonalData objects into DOM Document objects. By converting to DOM instead of a text XML file, we avoid having to parse the XML as it is fed into an XSLT processor. Instead, we will use the javax.xml.transform.DOMSource class to pass the DOM tree directly.

Example 6-7. PersonalDataXML.java

package chap6;

import javax.xml.parsers.*;
import org.w3c.dom.*;

/**
 * Responsible for converting a PersonalData object into an XML
 * representation using DOM.
 */
public class PersonalDataXML {

    /**
     * @param personalData the data to convert to XML.
     * @param includeErrors if true, an extra field will be included in
     * the XML, indicating that the browser should warn the user about
     * required fields that are missing.
     * @return a DOM Document that contains the web page.
     */
    public Document produceDOMDocument(PersonalData personalData,
            boolean includeErrors) throws ParserConfigurationException {

        // use Sun's JAXP to create the DOM Document
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance( );
        DocumentBuilder docBuilder = dbf.newDocumentBuilder( );
        Document doc =  docBuilder.newDocument( );

        // create <page>, the root of the document
        Element pageElem = doc.createElement("page");
        doc.appendChild(pageElem);

        // if needed, append <requiredFieldsMissing/>
        if (includeErrors && !personalData.isValid( )) {
            pageElem.appendChild(doc.createElement(
                    "requiredFieldsMissing"));
        }

        Element personalDataElem = doc.createElement("personalData");
        pageElem.appendChild(personalDataElem);

        // use a private helper function to avoid some of DOM's
        // tedious code
        addElem(doc, personalDataElem, "firstName",
                personalData.getFirstName( ), true);
        addElem(doc, personalDataElem, "lastName",
                personalData.getLastName( ), true);
        addElem(doc, personalDataElem, "daytimePhone",
                personalData.getDaytimePhone( ), true);
        addElem(doc, personalDataElem, "eveningPhone",
                personalData.getEveningPhone( ), false);
        addElem(doc, personalDataElem, "email",
                personalData.getEmail( ), true);

        return doc;
    }

    /**
     * A helper method that simplifies this class.
     *
     * @param doc the DOM Document, used as a factory for
     *        creating Elements.
     * @param parent the DOM Element to add the child to.
     * @param elemName the name of the XML element to create.
     * @param elemValue the text content of the new XML element.
     * @param required if true, insert 'required="true"' attribute.
     */
    private void addElem(Document doc, Element parent, String elemName,
            String elemValue, boolean required) {
        Element elem = doc.createElement(elemName);
        elem.appendChild(doc.createTextNode(elemValue));
        if (required) {
            elem.setAttribute("required", "true");
        }
        parent.appendChild(elem);
    }
}

The following code begins with its two import statements. The javax.xml.parsers package contains the JAXP interfaces, and the org.w3c.dom package contains the standard DOM interfaces and classes:

import javax.xml.parsers.*;
import org.w3c.dom.*;

The key to this class is its public API, which allows a PersonalData object to be converted into a DOM Document object:

    public Document produceDOMDocument(PersonalData personalData,
            boolean includeErrors) throws ParserConfigurationException {

The includeErrors parameter indicates whether or not to include the <requiredFieldsMissing/> element in the result. If this method throws a ParserConfigurationException, the most likely cause is a CLASSPATH problem. This frequently occurs when an older version of JAXP is present.

When using JAXP, it takes a few lines of code to obtain the appropriate implementation of the DocumentBuilder abstract class. By using the factory pattern, our code is safely insulated from vendor-specific DOM implementations:

// use Sun's JAXP to create the DOM Document
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance( );
DocumentBuilder docBuilder = dbf.newDocumentBuilder( );
Document doc =  docBuilder.newDocument( );

Once the doc object has been created, we use it to create all remaining elements in the XML data. For example, the <page> element is created first:

// create <page>, the root of the document
Element pageElem = doc.createElement("page");
doc.appendChild(pageElem);

Since <page> is the root element, it is the only thing added directly to our document. All remaining elements will be added as children or descendents of <page>. Even though we are not adding anything else directly to the doc object, we must continue using it as the factory for creating the remaining elements:

// if needed, append <requiredFieldsMissing/>
if (includeErrors && !personalData.isValid( )) {
    pageElem.appendChild(doc.createElement(
            "requiredFieldsMissing"));
}

Since DOM can be tedious, the children of <personalData> are created in a helper method called addElem( ) :

Element personalDataElem = doc.createElement("personalData");
pageElem.appendChild(personalDataElem);

// use a private helper function to avoid some of DOM's
// tedious code
addElem(doc, personalDataElem, "firstName",
        personalData.getFirstName( ), true);
...

You can refer back to Example 6-7 for the complete implementation of the addElem( ) method. A sample of its output is:

<firstName required="true">Eric</firstName>

The final piece of code, PersonalDataServlet.java, is presented in Example 6-8. This is a basic approach to servlet development that works for smaller programs such as this, but has a few scalability problems that we will discuss later in this chapter. Although we have removed all of the HTML and XML generation from this servlet, it is still responsible for handling incoming requests from the browser. As your web application grows to more and more screens, the code gets correspondingly larger.

Example 6-8. PersonalDataServlet.java

package chap6;

import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

/**
 * A demonstration servlet that produces two pages. In the first page,
 * the user is prompted to enter "personal information", including
 * name, phone number, and Email. In the second page, a summary of this
 * information is displayed. XSLT is used for all HTML rendering,
 * so this servlet does not enforce any particular look and feel.
 */
public class PersonalDataServlet extends HttpServlet {
    private PersonalDataXML personalDataXML = new PersonalDataXML( );
    private Templates editTemplates;
    private Templates thanksTemplates;

    /**
     * One-time initialization of this Servlet.
     */
    public void init( ) throws UnavailableException {
        TransformerFactory transFact = TransformerFactory.newInstance( );
        String curName = null;
        try {
            curName = "/WEB-INF/xslt/editPersonalData.xslt";
            URL xsltURL = getServletContext( ).getResource(curName);
            String xsltSystemID = xsltURL.toExternalForm( );
            this.editTemplates = transFact.newTemplates(
                    new StreamSource(xsltSystemID));

            curName = "/WEB-INF/xslt/confirmPersonalData.xslt";
            xsltURL = getServletContext( ).getResource(curName);
            xsltSystemID = xsltURL.toExternalForm( );
            this.thanksTemplates = transFact.newTemplates(
                    new StreamSource(xsltSystemID));
        } catch (TransformerConfigurationException tce) {
            log("Unable to compile stylesheet", tce);
            throw new UnavailableException("Unable to compile stylesheet");
        } catch (MalformedURLException mue) {
            log("Unable to locate XSLT file: " + curName);
            throw new UnavailableException(
                    "Unable to locate XSLT file: " + curName);
        }
    }

    /**
     * Handles HTTP GET requests, such as when the user types in
     * a URL into his or her browser or clicks on a hyperlink.
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {
        PersonalData personalData = getPersonalData(request);
        // the third parameter, 'false', indicates that error
        // messages should not be displayed when showing the page.
        showPage(response, personalData, false, this.editTemplates);
    }

    /**
     * Handles HTTP POST requests, such as when the user clicks on
     * a Submit button to update his or her personal data.
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {

        // locate the personal data object and update it with
        // the information the user just submitted.
        PersonalData pd = getPersonalData(request);
        pd.setFirstName(request.getParameter("firstName"));
        pd.setLastName(request.getParameter("lastName"));
        pd.setDaytimePhone(request.getParameter("daytimePhone"));
        pd.setEveningPhone(request.getParameter("eveningPhone"));
        pd.setEmail(request.getParameter("email"));

        if (!pd.isValid( )) {
            // show the 'Edit' page with an error message
            showPage(response, pd, true, this.editTemplates);
        } else {
            // show a confirmation page
            showPage(response, pd, false, this.thanksTemplates);
        }
    }

    /**
     * A helper method that sends the personal data to the client
     * browser as HTML. It does this by applying an XSLT stylesheet
     * to the DOM tree.
     */
    private void showPage(HttpServletResponse response,
            PersonalData personalData, boolean includeErrors,
            Templates stylesheet) throws IOException, ServletException {
        try {
            org.w3c.dom.Document domDoc =
                    this.personalDataXML.produceDOMDocument(
                    personalData, includeErrors);

            Transformer trans = stylesheet.newTransformer( );

            response.setContentType("text/html");
            PrintWriter writer = response.getWriter( );

            trans.transform(new DOMSource(domDoc), new StreamResult(writer));
        } catch (Exception ex) {
            showErrorPage(response, ex);
        }
    }

    /**
     * If any exceptions occur, this method can be called to display
     * the stack trace in the browser window.
     */
    private void showErrorPage(HttpServletResponse response,
            Throwable throwable) throws IOException {
        PrintWriter pw = response.getWriter( );
        pw.println("<html><body><h1>An Error Has Occurred</h1><pre>");
        throwable.printStackTrace(pw);
        pw.println("</pre></body></html>");
    }

    /**
     * A helper method that retrieves the PersonalData object from
     * the HttpSession.
     */
    private PersonalData getPersonalData(HttpServletRequest request) {
        HttpSession session = request.getSession(true);
        PersonalData pd = (PersonalData) session.getAttribute(
                "chap6.PersonalData");
        if (pd == null) {
            pd = new PersonalData( );
            session.setAttribute("chap6.PersonalData", pd);
        }
        return pd;
    }
}

Our servlet begins with a long list of import statements, indicating dependencies on the servlet API as well as the JAXP package. The servlet itself is a subclass of HttpServlet, as usual, and has three private fields:

public class PersonalDataServlet extends HttpServlet {
    private PersonalDataXML personalDataXML = new PersonalDataXML( );
    private Templates editTemplates;
    private Templates thanksTemplates;

It is important to ensure that each of these fields is thread-safe. Because many clients share the same servlet instance, it is highly probable that these fields will be accessed concurrently. Instances of PersonalDataXML are thread-safe because they are stateless, meaning they contain no data that can be concurrently modified. The Templates instances are compiled representations of the two stylesheets this servlet uses and are also designed to be thread-safe.

As the comments indicate, the init( ) method performs a one-time initialization of the servlet. A servlet container will invoke this method before this servlet is asked to handle any client requests. The init( ) method is further guaranteed to execute to completion before any other threads can access this servlet, so concurrency is not an issue at this point. If anything fails during initialization, an instance of UnavailableException is thrown:

public void init( ) throws UnavailableException {
    TransformerFactory transFact = TransformerFactory.newInstance( );
    String curName = null;
    ...

This exception is provided in the javax.servlet package and indicates that the servlet could not be loaded successfully. In our case, the most common cause of this error is a configuration problem. For example, your XSLT stylesheets may be installed in the wrong directory, or some JAR file was not found.

The next thing the init( ) method does is load the two stylesheets into memory. The XSLT stylesheets are stored on the file system, so StreamSource will be used to read them into JAXP. But you definitely do not want to hardcode the absolute pathname of the stylesheets. If you do this, your code will probably work on your personal machine but will fail once it is deployed onto a production web server. For example, C:/java/tomcat/webapps/chap6/WEB-INF is a Windows-specific absolute pathname. Using something so specific would cause the servlet to fail on all non-Windows platforms, as well as other Windows machines that have Tomcat installed in a different directory. The best approach is to use a relative pathname such as /WEB-INF, so the stylesheets can be located regardless of where your web application is deployed.

A relative pathname has to be relative to some starting location, so we use the ServletContext class. ServletContext has the ability to locate resources relative to the deployed directory of the current web application, so you can avoid absolute pathnames in your code. The details of mapping the relative pathname to the absolute pathname are taken care of by the servlet container, thus making your code more portable.

In this example, chap6.war is deployed to Tomcat's webapps directory. Tomcat will expand it into the webapps/chap6 directory, which contain subdirectories that match the directory structure of the WAR file. We start by assigning the current XSLT filename to the curName variable, using the following pathname:

try {
    curName = "/WEB-INF/xslt/editPersonalData.xslt";

Two options are available at this point. The ServletContext can provide either an InputStream or a URL, both of which represent the XSLT stylesheet. If you use an InputStream, however, the XSLT processor sees your stylesheet as a stream of bytes. It will not know where this datastream originated, so it will not automatically know how to resolve URI references. This becomes a problem if your stylesheet imports or includes another stylesheet because this other stylesheet will not be located. To resolve this problem when using InputStream, the javax.xml.transform.Source interface provides the setSystemId( ) method. This allows the XSLT processor to resolve URI references in the stylesheet (see Chapter 5, "XSLT Processing with Java").

For this servlet, we avoid this issue by using a URL instead of an InputStream. The URL is converted into a system identifier, which makes it possible to create a StreamSource instance. That is, in turn, used to create a Templates instance for this stylesheet:

URL xsltURL = getServletContext( ).getResource(curName);
String xsltSystemID = xsltURL.toExternalForm( );
this.editTemplates = transFact.newTemplates(
        new StreamSource(xsltSystemID));

The same process is repeated for the second stylesheet, followed by basic exception handling:

   curName = "/WEB-INF/xslt/confirmPersonalData.xslt";
   xsltURL = getServletContext( ).getResource(curName);
   xsltSystemID = xsltURL.toExternalForm( );
   this.thanksTemplates = transFact.newTemplates(
          new StreamSource(xsltSystemID));
   } catch (TransformerConfigurationException tce) {
   log("Unable to compile stylesheet", tce);
          throw new UnavailableException("Unable to compile stylesheet");
} catch (MalformedURLException mue) {
    log("Unable to locate XSLT file: " + curName);
    throw new UnavailableException(
            "Unable to locate XSLT file: " + curName);
}
}

The log() method causes messages to be written to one of Tomcat's log files, found in the TOMCAT_HOME/logs directory. The UnavailableException simply indicates that this servlet is unavailable, so it will not be loaded into memory. The user will see an error page in their browser at this point.

If the init( ) method completes successfully, the servlet will be available to handle requests from clients. In this servlet, the doGet( ) and doPost( ) methods have been implemented; therefore, both HTTP GET and POST protocols are supported. When the user first enters the application, they will click on a hyperlink, type a URL into their browser, or visit a saved bookmark. In all of these cases, the browser issues an HTTP GET request that ultimately causes the doGet( ) method to be invoked:

protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws IOException,
        ServletException {
    PersonalData personalData = getPersonalData(request);
    // the third parameter, 'false', indicates that error
    // messages should not be displayed when showing the page.
    showPage(response, personalData, false, this.editTemplates);
}

The first thing the doGet( ) method does is retrieve the instance of PersonalData associated with this particular user. The appropriate code has been factored out into the getPersonalData( ) helper method, since this same functionality is required by the doPost( ) method as well. You can refer back to Example 6-8 to see how getPersonalData( ) is implemented. It basically uses HttpSession to locate the appropriate instance of PersonalData. If the object is not found in the session, a new instance is created and stored.

The doGet( ) method then calls the showPage( ) method, which does the actual work of sending the web page to the browser. The parameters to showPage( ) include:

  • The HttpServletResponse, which provides access to the PrintWriter. The result of the transformation will be sent to this writer.

  • The instance of PersonalData, so the showPage( ) method knows what data to display.

  • A false parameter, indicating that error messages should not be shown. That makes sense because doGet( ) is called when the page is first displayed, and users should not be warned about invalid data before they type something.

  • A reference to the appropriate stylesheet. In this case, the stylesheet will show the HTML form so the user can fill out his or her information.

Once the user fills out the form and submits it to the servlet, the doPost( ) method is invoked. The code for doPost( ) is similar to doGet( ) (see Example 6-8). The only difference here is that all incoming data is validated via the PersonalData class. If the request is valid, the "Thank You" page is displayed. Otherwise, the current page is redisplayed with error messages enabled. As you can see in the code, the only distinction between these two pages is that they use different stylesheets.

The final piece to this puzzle resides in the showPage( ) method. This method begins by creating a DOM Document instance by delegating to the PersonalDataXML helper class. As you can see in the following code, the servlet stays quite small because the DOM generation is factored out into the helper class:

private void showPage(HttpServletResponse response,
        PersonalData personalData, boolean includeErrors,
        Templates stylesheet) throws IOException, ServletException {
    try {
        org.w3c.dom.Document domDoc =
                this.personalDataXML.produceDOMDocument(
                personalData, includeErrors);

This method then proceeds to create a new instance of Transformer. You may recall from Chapter 5, "XSLT Processing with Java" that Transformer instances are very lightweight and merely hold state information for the current transformation. Since Transformer instances are not thread-safe, the instance is a local variable in this method. With local variables, each thread gets its own copy:

Transformer trans = stylesheet.newTransformer( );

Next, the content type is configured for the HttpServletResponse, a PrintWriter is obtained, and the transformation is performed. The result tree is sent directly to the response's PrintWriter:

            response.setContentType("text/html");
            PrintWriter writer = response.getWriter( );

            trans.transform(new DOMSource(domDoc), new StreamResult(writer));
        } catch (Exception ex) {
            showErrorPage(response, ex);
        }
    }

If any exception occurs, the showErrorPage( ) method is invoked. Since an exception can indicate that some XML library is unavailable, the showErrorPage( ) does not attempt to use XML or XSLT for its output. If it does, another similar exception would almost certainly occur. Instead, it uses hardcoded println( ) statements to generate its HTML (see Example 6-8).

6.3.4. Deployment

Figure 6-8 shows the complete contents of the WAR file used in this example. You may notice that SplashScreenServlet.class is still listed in this WAR file. This example is merely an extension of the example created earlier in this chapter. As in the earlier example, placing the .class files under the classes directory made them available to the ClassLoader used by this web application.

Figure 6-8. WAR file for PersonalDataServlet

The XSLT stylesheets are placed under the WEB-INF/xslt directory. Since anything under the WEB-INF directory is hidden from clients, the XSLT stylesheets are not directly visible to anyone visiting your web site. If you want to make these stylesheets publicly visible, move them out of the WEB-INF directory. The index.html file, for example, is the publicly visible "home page" for this web application. It merely contains a link that the user can click on to view the servlet. Although the stylesheets are hidden from clients, they are accessible from your Java code. Referring back to the code in Example 6-8, the init( ) method used the following to locate the stylesheets:

curName = "/WEB-INF/xslt/editPersonalData.xslt";
URL xsltURL = getServletContext( ).getResource(curName);

As this code illustrates, the locations of the stylesheets are entirely relative to their position in the WAR file. Therefore, your servlet will still work as the web application is moved onto a production web server.

The deployment descriptor, listed in Example 6-9, has been expanded to include the new PersonalDataServlet class. The lines that have changed from our first iteration are emphasized.

Example 6-9. Expanded deployment descriptor

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
  <servlet>
    <servlet-name>personalDataServlet</servlet-name>
    <servlet-class>chap6.PersonalDataServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>splashScreen</servlet-name>
    <servlet-class>chap6.SplashScreenServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>personalDataServlet</servlet-name>
    <url-pattern>/personalData/*</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>splashScreen</servlet-name>
    <url-pattern>/splash/*</url-pattern>
  </servlet-mapping>
</web-app>

6.3.5. How to Compile, Deploy, and Run

In Java, it often seems that half of the battle is devoted to figuring out CLASSPATH issues. In order to compile this example, the following JAR files must be listed on the CLASSPATH:

jaxp.jar
Java API for XML Processing (JAXP) 1.1

xalan.jar
Xalan XSLT processor (use the one included with JAXP)

crimson.jar
Crimson XML parser (use the one included with JAXP)

servlet.jar
Included with the Tomcat servlet container

Of course, the directory containing your own source code must also be listed on the CLASSPATH. Once everything is set up, you can compile PersonalData.java, PersonalDataXML.java, and PersonalDataServlet.java by typing javac *.java. Whether you also wish to include SplashScreenServlet.java is entirely up to you.

As mentioned earlier in this chapter, use the jar command to create the WAR file. To create chap6.war, simply arrange your files into the directory structure shown in Figure 6-8 and issue the following command from the directory that contains index.html:

jar -cvfM ../chap6.war .

This command places chap6.war in the parent of your current working directory; the forward slash (/) works on Windows as well as Unix clients. Once the WAR file has been created, you can view its contents by changing to its directory and issuing the following command:

jar -tvf chap6.war

This shows the table of contents for the WAR file, which must match the structure shown in Figure 6-8.

Deployment to Tomcat is easy: just copy chap6.war to the TOMCAT_HOME/webapps directory while Tomcat is not running. You can attempt to execute the servlet now, but it will probably not work because jaxp.jar, xalan.jar, and crimson.jar must be installed in the TOMCAT_HOME/lib directory before they can be available for your web application.

The most difficult aspect of this step is installing the correct versions of these JAR files. Depending on which version of Tomcat you are running, older versions of jaxp.jar and crimson.jar may already be found in the TOMCAT_HOME/lib directory. The safest approach is to download JAXP 1.1, which includes all three of these JAR files, and copy them from the JAXP distribution to the TOMCAT_HOME/lib directory.

Once these steps are complete, start Tomcat and access the following URL:

http://localhost:8080/chap6/personalData

This should bring up the personal information page with a blank form, ready for input.

6.3.6. Locating Stylesheets with Initialization Parameters

As you just saw, an easy way to locate stylesheets is simply to place them somewhere underneath the WEB-INF directory of a WAR file. While this is an ideal solution for solitary web applications, there are situations where the same stylesheets are shared across a whole group of web apps. In this case, embedding the stylesheets into various WAR files is not viable.

Ideally, the stylesheets will be located in a shared directory somewhere, but that directory location will not be hardcoded into any servlets. The simple way to accomplish this is via initialization parameters. These are name/value pairs of strings specified in the deployment descriptor and retrieved via the Servlet or ServletContext.

Servlet initialization parameters are tied to specific servlets, and context initialization parameters are tied to an entire web application. For the purposes of specifying the XSLT stylesheet location, it makes sense to use context parameters. These can be specified in the deployment descriptor as follows:

<web-app>
  <context-param>
    <param-name>xslt_directory</param-name>
    <param-value>C:/dev/xslt</param-value>
  </context-param>
  <servlet>
    ...
  </servlet>
</web-app>

And the values of these parameters can be retrieved using the following methods on the javax.servlet.ServletContext interface:

public interface ServletContext {
    // if the parameter name does not exist, return null
    String getInitParameter(String name);
    Enumeration getInitParameterNames( );
    ...remaining methods omitted
}

So in order to locate the stylesheet, one might write the following code in a servlet's init( ) method:

public class MyServlet extends HttpServlet {
    private String xsltDirectory;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        this.xsltDirectory = config.getServletContext( ).getInitParameter(
                "xslt_directory");
        if (this.xsltDirectory == null) {
            throw new UnavailableException(
                    "xslt_directory is a required context-param");
        }
    }

    ...remainder of code omitted
}

Now that the actual location of the stylesheets has been moved into the deployment descriptor, changes can be made without any edits to the servlet.

Previous | Home | Next
6.2. WAR Files and Deployment | Book Index | 6.4. Stylesheet Caching Revisited
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Stylesheet Caching Revisited (Java and XSLT)

 Java and XSLTSearch this book

6.4. Stylesheet Caching Revisited

We have seen two approaches that eliminate the need to hardcode the absolute pathname of XSLT stylesheets in your servlet code. In the first approach, the ServletContext was used to load resources from the web application using a relative pathname. In the second approach, the location was specified as a context initialization parameter.

This takes care of compilation changes, but now we have the issue of dynamic loading. In the PersonalDataServlet class, the two XSLT stylesheets are located and "compiled" into instances of the javax.xml.transform.Templates interface. Although this offers high performance for transformations, the two stylesheets are never flushed from memory. If changes are made to the XSLT stylesheets on disk, the servlet must be stopped and started again.

6.4.1. Integration with the Stylesheet Cache

In Chapter 5, "XSLT Processing with Java", a stylesheet cache was implemented. In this next example, PersonalDataServlet is modified to use the cache instead of Templates directly. This will offer virtually the same runtime performance. However, you will be able to modify the stylesheets and immediately see those changes in your web browser. Each time a stylesheet is requested, the cache will check its timestamp on the file system. If the file has been modified, a new Templates instance is instantiated without bringing down the servlet.

Fortunately, integration with the cache actually makes the PersonalDataServlet simpler to implement. Example 6-10 contains the modified listing, and all modified lines are emphasized.

Example 6-10. Modified PersonalDataServlet.java with stylesheet cache

package chap6;

import com.oreilly.javaxslt.util.StylesheetCache;
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

/**
 * A modification of PersonalDataServlet that uses the
 * com.oreilly.javaxslt.util.StylesheetCache class.
 */
public class PersonalDataServlet extends HttpServlet {
    private PersonalDataXML personalDataXML = new PersonalDataXML( );
    private String editXSLTFileName;
    private String thanksXSLTFileName;

    /**
     * One-time initialization of this Servlet.
     */
    public void init( ) throws UnavailableException {
        this.editXSLTFileName = getServletContext( ).getRealPath(
                "/WEB-INF/xslt/editPersonalData.xslt");
        this.thanksXSLTFileName = getServletContext( ).getRealPath(
                "/WEB-INF/xslt/confirmPersonalData.xslt");
    }

    /**
     * Handles HTTP GET requests, such as when the user types in
     * a URL into his or her browser or clicks on a hyperlink.
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {
        PersonalData personalData = getPersonalData(request);
        // the third parameter, 'false', indicates that error
        // messages should not be displayed when showing the page.
        showPage(response, personalData, false, this.editXSLTFileName);
    }

    /**
     * Handles HTTP POST requests, such as when the user clicks on
     * a Submit button to update his or her personal data.
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {

        // locate the personal data object and update it with
        // the information the user just submitted.
        PersonalData pd = getPersonalData(request);
        pd.setFirstName(request.getParameter("firstName"));
        pd.setLastName(request.getParameter("lastName"));
        pd.setDaytimePhone(request.getParameter("daytimePhone"));
        pd.setEveningPhone(request.getParameter("eveningPhone"));
        pd.setEmail(request.getParameter("email"));

        if (!pd.isValid( )) {
            // show the 'Edit' page with an error message
            showPage(response, pd, true, this.editXSLTFileName);
        } else {
            // show a confirmation page
            showPage(response, pd, false, this.thanksXSLTFileName);
        }
    }

    /**
     * A helper method that sends the personal data to the client
     * browser as HTML. It does this by applying an XSLT stylesheet
     * to the DOM tree.
     */
    private void showPage(HttpServletResponse response,
            PersonalData personalData, boolean includeErrors,
            String xsltFileName) throws IOException, ServletException {
        try {
            org.w3c.dom.Document domDoc =
                    this.personalDataXML.produceDOMDocument(
                    personalData, includeErrors);

            Transformer trans =
                    StylesheetCache.newTransformer(xsltFileName);

            response.setContentType("text/html");
            PrintWriter writer = response.getWriter( );

            trans.transform(new DOMSource(domDoc), new StreamResult(writer));
        } catch (Exception ex) {
            showErrorPage(response, ex);
        }
    }

    /**
     * If any exceptions occur, this method can be showed to display
     * the stack trace in the browser window.
     */
    private void showErrorPage(HttpServletResponse response,
            Throwable throwable) throws IOException {
        PrintWriter pw = response.getWriter( );
        pw.println("<html><body><h1>An Error Has Occurred</h1><pre>");
        throwable.printStackTrace(pw);
        pw.println("</pre></body></html>");
    }

    /**
     * A helper method that retrieves the PersonalData object from
     * the HttpSession.
     */
    private PersonalData getPersonalData(HttpServletRequest request) {
        HttpSession session = request.getSession(true);
        PersonalData pd = (PersonalData) session.getAttribute(
                "chap6.PersonalData");
        if (pd == null) {
            pd = new PersonalData( );
            session.setAttribute("chap6.PersonalData", pd);
        }
        return pd;
    }
}

One key difference in this example is its reliance on the com.oreilly.javaxslt.util.StylesheetCache class. This will, of course, require that you add StylesheetCache.class to your WAR file in the appropriate directory. Another option is to place the stylesheet cache into a JAR file, and place that JAR file into the TOMCAT_HOME/lib directory. This approach is taken when you download the example code for this book.

The biggest code savings occur in the init( ) method because the filenames for the stylesheets are stored instead of Templates instances. This is because the stylesheet cache requires filenames as inputs and will create its own instances of Templates, which accounts for a majority of the simple changes throughout the servlet.

Once you get this example up and running, testing the stylesheet reloading capability is a snap. As before, chap6.war is copied to the TOMCAT_HOME/webapps directory. After you run the servlet the first time, you will notice that the WAR file is expanded into the TOMCAT_HOME/webapps/chap6 directory. Simply go into the TOMCAT_HOME/webapps/chap6/WEB-INF/xslt directory and edit one of the stylesheets. Then click on the Refresh button on your web browser, and you should see the results of the edits that were just made.

If you don't see the changes, there might be some leftover files from earlier examples in this chapter. Be sure to shut down Tomcat and remove both chap6.war and the chap6 directory from Tomcat's webapps directory. Then re-deploy and try again.

Previous | Home | Next
6.3. Another Servlet Example | Book Index | 6.5. Servlet Threading Issues
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Servlet Threading Issues (Java and XSLT)

 Java and XSLTSearch this book

6.5. Servlet Threading Issues

Like it or not, a servlet must be capable of serving more than one client at a time. Built-in threading capability is one of the key reasons why Java is so well-suited to server applications, particularly when compared to a traditional CGI model. As usual, however, tradeoffs are involved. In particular, writing code that can handle many concurrent tasks without corrupting data can be quite challenging at times. Ideally, this material can alert you to the most common causes of threading problems found in a servlet environment.

6.5.1. Servlet Threading Model

In the standard servlet model, a client makes a request via the servlet's service( ) method. In the HttpServlet class, the service( ) method determines the type of HTTP request and delegates to methods such as doGet( ) or doPost( ). If several clients issue requests at the same time, these methods will serve each client in a different thread. Since most servlets are subclasses of HttpServlet, your main concern is insuring that service( ), doGet( ), and doPost( ) can handle many concurrent clients.

Before handling any requests, a servlet's init( ) method is invoked. According to the servlet API specification, this method must be invoked by only a single thread and must complete successfully before subsequent threads are allowed to enter the service( ) method. For this reason, you do not have to worry about threading problems inside of the init( ) method. From there, however, all bets are off.

One simplistic approach to thread safety is to declare a method as synchronized. In this approach, your doGet( ) method would be declared as follows:

protected synchronized void doGet(HttpServletRequest request,
        HttpServletResponse response) throws IOException, ServletException {
    ...
}

The synchronized keyword will require that any thread wishing to invoke this method first obtain a lock on the servlet object. Once the first client obtains the lock and begins to execute the method, all others must wait their turn. If the doGet( ) method takes 0.5 seconds to execute, then a load of a mere 100 users will result in nearly a minute-long wait for many visitors to your site, since each waits in a queue for access to the lock.

This is almost never a viable option, so another choice is to declare that your servlet implements the javax.servlet.SingleThreadModel interface as follows:

public class MyServlet extends HttpServlet implements SingleThreadModel {
...
}

The SingleThreadModel interface is a marker interface, meaning that it does not declare any methods. It merely indicates to the servlet container that your servlet is not thread-safe, and can handle only one request at a time in its service( ) method. A typical servlet container will maintain a pool of servlet instances in this case, allowing each instance to handle a single request at a time.

This is somewhat better than merely synchronizing the doGet( ) or doPost( ) method. However, it does mean that multiple copies of the servlet will be instantiated. This results in higher memory overhead and still does not ensure that all threading issues will be resolved. For example, concurrent modifications to a shared resource such as a file or a static field are not prevented in any way.

6.5.2. Thread Safety Tips

Most servlet threading problems occur when two or more threads make changes to the same resource. This might mean that two threads try to modify a file, or perhaps several threads all update the value of a shared variable at the same instant. This causes unpredictable behavior and can be very hard to diagnose. Another type of thread problem is deadlock, where two threads are in contention for the same resource, each holding a lock that the other thread needs. Yet another problem is performance. As mentioned earlier, synchronizing access to a method can introduce significant performance penalties.

The best overall approach to servlet thread safety is to avoid the SingleThreadModel interface and synchronizing access to the service( ) method. This way, your servlet can handle multiple client requests at the same time. This also means that you must avoid situations where more than one thread can modify a shared resource concurrently. The following tips should offer some guidance.

6.5.2.1. Tip 1: Local variables are thread-safe

Object fields in a servlet are often bad news. Consider the following code:

public class HomeServlet extends HttpServlet {
    private Customer currentCust;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {
        HttpSession session = request.getSession(true);
        currentCust = (Customer) session.getAttribute("cust");
        currentCust.setLastAccessedTime(new Date( ));
        ...
    }
}

In this code, the currentCust field is obtained from the HttpSession whenever a client enters the doGet( ) method. Unfortunately, if another thread invokes this method an instant later, the currentCust field will be overwritten before the first thread is complete. In fact, dozens of threads could enter the doGet( ) method at roughly the same time, repeatedly replacing the currentCust reference. This would lead to complete failure of this servlet.

The easy fix is to make currentCust a local variable as follows:

public class HomeServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {
        HttpSession session = request.getSession(true);
        Customer currentCust = (Customer) session.getAttribute("cust");
        currentCust.setLastAccessedTime(new Date( ));
        ...
    }
}

This fixes our problem because each thread gets its own copy of local variables in Java. By simply removing the object field and replacing it with a local variable, this particular threading problem is resolved.

6.5.2.2. Tip 2: Immutable objects are thread-safe

Whenever two or more threads make changes to the same object at the same time, a race condition can occur. Consider the following code:

public class Person {
    private String firstName;
    private String lastName;

    public void setName(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    ...getter methods omitted
}

If two threads invoke the setName( ) method at roughly the same time, the following scenario can occur:

  1. Thread "A" sets the first name to "Bill," but is interrupted by thread "B".

  2. Thread "B" sets the first and last names to "George" and "Bush."

  3. Thread "A" regains control, and sets the last name to "Clinton."

At this point, the person's name is George Clinton, which is clearly not what was intended. Although you could make the setName( ) method synchronized, you would also have to make any get methods synchronized as well.

Another option is to make this an immutable object. An immutable object cannot be modified, so multiple threads cannot concurrently alter it. The Person class can be modified as follows:

public class Person {
    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName( ) { return this.firstName; }
    public String getLastName( ) { return this.lastName; }
}

Since instances of the Person class cannot be modified, its methods do not have to be synchronized. This makes the objects fast and allows them to be read by many threads concurrently. The only drawback is that you cannot make changes to these objects once they are constructed. The simple fix is to create a brand new Person object whenever a change needs to be made. This is essentially the approach that java.lang.String takes.

Immutable objects are not always an option but can be a useful technique for many smaller "data helper" classes that seem to pop up in every application.

6.5.2.3. Tip 3: Provide a single point of entry

When dealing with a single instance of a shared resource, such as a file that needs to be modified, you should consider creating a facade around that resource. This is a single class that provides controlled access to that resource, thus providing a single point in your code for proper synchronization. The following code snippet illustrates how you can essentially create a facade around a data source that holds Customer objects. It is assumed that the Customer class is immutable, making it impossible to change a Customer instance without going through this well-defined API:

public class CustomerSource {
    public static synchronized Customer getCustomer(String id) {
        // read the customer from a file, or perhaps
        // from a database...
    }

    public static synchronized Customer createCustomer( ) {
        // create a new customer in the file or database
        // and return it...
    }

    public static synchronized void deleteCustomer(String id) {
        // ...
    }
}

This is just one simple approach that works best on smaller applications. A servlet's doGet( ) or doPost( ) method should utilize the CustomerSource class without any data corruption. If the methods in CustomerSource are slow, however, they will hinder scalability as more and more clients wait for their turn to access the underlying data source.

6.5.2.4. Tip 4: Understand the Templates interface

Multiple threads can share implementations of javax.xml.transform.Templates. Therefore, instances can be stored as object fields on a servlet:

public class MyServlet extends HttpServlet {
    private Templates homePageStylesheet;

    ...
}

But instances of javax.xml.transform.Transformer are not thread-safe; they should be declared as local variables within the doGet( ) or doPost( ) method:

public class MyServlet extends HttpServlet {
    private Templates homePageStylesheet;

    public void init( ) throws UnavailableException {
        ... create the Templates instance
    }

    protected void doGet( ) {
        Transformer trans = homePageStylesheet.newTransformer( );
        ... use this Transformer instance, a local variable
    }
}

Previous | Home | Next
6.4. Stylesheet Caching Revisited | Book Index | 7. Discussion Forum
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Discussion Forum (Java and XSLT)

 Java and XSLTSearch this book

Chapter 7. Discussion Forum

Contents:

Overall Process
Prototyping the XML
Making the XML Dynamic
Servlet Implementation
Finishing Touches

Up until now, the examples in this book have been short and to the point. The goal of this chapter is to show how a much more sophisticated web application is designed and implemented from the ground up. This is the culmination of everything covered so far, combining XML, servlets, XSLT, JDBC, JAXP, and JDOM into a fully functioning web-based discussion forum. As with the other examples in this book, the full source code is available from the companion web site.

Walking the line between "textbook quality" and "real-world" examples is difficult. First and foremost, the goal of this chapter is to demonstrate how to design and implement a nontrivial web application using XSLT and Java. The second goal is to produce a decent application that can actually be used in the real world. Hopefully this has been achieved. Although making an example of this size fit into a single chapter involves a few tradeoffs, the design is flexible enough to allow new features, such as user authentication, to be implemented without too much additional effort.

The discussion forum requires the following Java packages:

  • Java 2 Standard Edition v1.2 or later

  • JDOM beta 6

  • Any JAXP 1.1 compatible XML parser and XSLT processor

  • Any servlet container that supports Version 2.2 or later of the servlet specification

  • Either MySQL or Microsoft Access

7.1. Overall Process

Developing a web application using Java and XSLT can be broken down into several key steps. As in any software development project, a modular approach that can be dispatched to several developers simultaneously is highly desirable. This speeds the overall process and allows developers of different skill levels to participate.

Our process consists of the following high-level steps:

  • Define the requirements.

  • Create prototype XHTML web pages.

  • Create prototype XML datafiles.

  • Create XSLT stylesheets.

  • Design and implement the Java domain classes.

  • Design and implement the database and related code.

  • Create "XML producer" objects.

  • Implement the servlets and related code.

Although the list shown here approximates the order in which these steps will be performed, in larger applications it is typical to implement a vertical slice of the system first. This slice will implement one or two key screens and will require the development team to follow all of the previous steps. As more screens are added to the system, the process is followed again for each piece of functionality that is added. This is very typical of most lightweight software development processes in which the system is developed in iterative steps rather than trying to implement the entire system in one pass.

The remainder of this chapter will present the implementation of the discussion forum. The requirements, design, and code will be presented in roughly the same order as the list shown in this section.

7.1.1. Requirements

An online discussion forum will be developed using Java and XSLT. For the reference implementation, all features will be accessible via a web browser using XHTML, and no client-side Java or JavaScript will be required. The target for deployment is a web-hosting provider that supports Java 2, servlet 2.2, and access to a relational database such as MySQL. It is assumed that any additional Java JAR files, such as those required for JAXP and JDOM, can be installed along with the web application.

The discussion forum will be divided into message boards, each of which covers a different topic such as "Dog Lovers" or "Cat Lovers." Each message belongs to one of these boards and may be a response to a previous message. This is known as a threaded discussion forum. Each message will also contain a subject, create date, author email, and the actual message text.

When visiting the web site, users can read existing messages, post new messages, or reply to existing messages. Only administrators can create new message boards. Although XHTML is specified for the reference implementation, every effort will be made to facilitate alternatives, such as XHTML Basic or WML. Other than practical limitations such as bandwidth and database capacity, no artificial constraints shall be placed on the number of boards or messages.

A few features will be omitted to keep this example reasonably sized. These include a web-based administrative interface, user authentication and security, and the ability to search the archive. Suggestions for implementing these features are mentioned at the end of this chapter.

7.1.2. Screen Flow

The forum user interface consists of four primary screens, as shown in Figure 7-1. Each box represents a different web page that visitors encounter, and lines indicate screen-to-screen flow as the user clicks on links.

Figure 7-1. Discussion forum screens

Creating a graphical layout of the web pages as shown here is sometimes called storyboarding, a common user interface design technique that has its roots in the animation, television, and motion picture industries. Such high-level diagrams typically start as hand-drawn sketches on paper, with the intent of capturing the overall application flow. This is a good place to start because it shows how everything fits together without delving too deeply into technical design details.

The "Discussion Forum Home" page is the starting point and displays the list of all message boards. For each message board, a list of months with messages is displayed. From this screen, the user can either click on a month to view a list of message subjects, or click on a link to post a new message. The user can always return to the home page from any other page in the application.

The "View Month" page shows message subjects for a particular month in a given board. These messages are displayed in a tree that shows the message subject, author, and create date. The structure of the tree represents threads of discussion, with replies indented underneath the original messages. From this page, the user can either select a message to view or click on a link to visit the "Post New Message" page.

The "View Message" screen shows details for an individual message. From this page, visitors can either return to the month view or click on a link to reply to this message.

The final page allows users to either post a new message or reply to an existing message. Since posting and replying are quite similar, much of the Java and XSLT stylesheet code is reused. Although using the same code for multiple web pages reduces the size of the application, it can add complexity because the code must be capable of two modes of operation.

Previous | Home | Next
6.5. Servlet Threading Issues | Book Index | 7.2. Prototyping the XML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Prototyping the XML (Java and XSLT)

 Java and XSLTSearch this book

7.2. Prototyping the XML

Once the requirements and screen flow are well understood, it is possible to move on to a more rigorous design process. Web sites based on XSLT are highly modular, facilitating a design and implementation process that can be farmed out to several members of a development team. Each piece can be developed and tested independently before finally bringing everything together into the completed web application.

7.2.1. XHTML Prototypes

Creating user interface prototypes is an early task that can be handed off to less experienced programmers or perhaps to a dedicated web page designer. At this stage in the game, an overly complex and graphical web interface is not required. The bells and whistles can be added later by merely updating XSLT stylesheets. In fact, too much effort at this early stage can make it more difficult to figure out what the XML and XSLT should look like.

Since the front end will be created using XHTML Strict, a separate cascading style sheet (CSS) will be required to make the pages look presentable.[25] The strict variant of XHTML does not allow most of the HTML 4.0 formatting tags, but instead encourages the use of CSS. Example 7-1 contains the complete CSS file used by the discussion forum.

[25] See http://www.w3.org/TR/xhtml1 for more information on XHTML Strict.

Example 7-1. forum.css

body {
  font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
}

.box1 {
  border: 3px solid Navy;
  text-align: center;
  padding: 4px;
  margin : 2px;
  background-color: #c0c0c0;
}

.box2 {
  border: 1px solid Navy;
  padding: 4px;
  margin: 2px;
  background-color: #FFFFCC;
}

h1 {
  font-size: 22pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

h2 {
  font-size: 18pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

h3 {
  font-size: 14pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

ul {
  margin-top: 0px;
}

.msgSummaryLine {
  font-size: smaller;
  font-weight: normal;
}

a:hover {
  background-color:yellow;
}

.error {
  font-weight: bold;
  color: red;
}

Each of the XHTML web pages refers to this CSS file using the following syntax:

<link type="text/css" rel="stylesheet" href="/forum/forum.css" />

This is a great technique because it keeps the size of the XSLT stylesheets and each XHTML page much smaller. Changes to fonts and colors can be made in the single CSS file and are immediately reflected throughout the web application. The primary obstacle at this time is noncompliant web browsers. Although support for CSS is gradually improving, web pages must be tested on a variety of browsers to identify formatting problems.

NOTE: A common theme presented throughout this book is the separation of data and presentation that XSLT supports. CSS expands upon this theme by separating XHTML content from many aspects of its visual presentation. CSS and XSLT are very different technologies that complement one another nicely.

Most of the code in a CSS file is fairly self-explanatory. For example, the h2 style applies to <h2> elements in the XHTML. One style element that many programmers may not be familiar with is:

.box2 {
  border: 1px solid Navy;
  padding: 4px;
  margin: 2px;
  background-color: #FFFFCC;
}

The dot in .box2 indicates a style class definition. Here is how the box2 style class is used in the XHTML:

<div class="box2">Messages for March, 2001</div>

The advantage of a style class is that it can be applied to any element in the XHTML. In this case, a thin border and yellow background are applied to any element that has the box2 class.

The web page designers should create basic representations of every page in the application at this point. The home page is shown in Figure 7-2.

Figure 7-2. Home page prototype

The complete XHTML source code for the home page is shown in Example 7-2. As shown, the actual hyperlinks are not valid because the design for the servlets has not been completed, and the final URLs are probably unknown at this point. At any rate, this is only prototype code, because the actual XHTML web pages are dynamic and will be produced as the result of an XSLT transformation from XML data.

Example 7-2. Home page XHTML source

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Discussion Forum Home</title>
    <link href="../docroot/forum.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div class="box1">
      <h1>Discussion Forum Home</h1>
    </div>
    <div class="box2">
      <h2>Java Programming</h2>
      <div>General programming questions about Java.</div>
      <div style="margin-left: 2em;">
        <p>
          <a href="link_to_post_message">Post Message</a>
        to Java Programming</p>
        <a href="link_to_feb_messages">Feb, 2001</a> |
        <a href="link_to_mar_messages">Mar, 2001</a> |
        <a href="link_to_apr_messages">Apr, 2001</a>
      </div>
    </div>
    <div class="box2">
      <h2>XSLT Basics</h2>
      <div>Writing effective XSLT stylesheets.</div>
      <div style="margin-left: 2em;">
        <p>
        <a href="link_to_post_message">Post Message</a> to XSLT Basics</p>
        <a href="link_to_feb_messages">Feb, 2001</a> |
        <a href="link_to_mar_messages">Mar, 2001</a> |
        <a href="link_to_apr_messages">Apr, 2001</a>
      </div>
    </div>
    <div class="box2">
      <h2>Sample empty board</h2>
      <div>This board has no messages.</div>
      <div style="margin-left: 2em;">
        <p>
          <a href="link_to_post_msg">Post Message</a>
        to Sample empty board</p>
      </div>
    </div>
  </body>
</html>

<div> and <span> tags may be unfamiliar because they were ignored by many HTML authors until CSS became more prevalent. Basically, a <div> tag is wrapped around any number of other elements, turning them into a block-level element group. The <span> tag is similar, but it is an inline element. This means that <span> tags will be embedded into the current line, while <div> tags will wrap to a new line much like <p> or <h1> tags do. The ability to define style classes make <div> and <span> particularly useful for XHTML Strict, which disallows deprecated HTML 4.0 elements such as <font>. Although <span> is not used in this particular example, <div> is used frequently to introduce line breaks and to apply styles using CSS.

The next prototype, shown in Figure 7-3, shows what a message board looks like. XHTML source code for the remaining screens is not listed here.

Figure 7-3. View Month prototype

Messages that are replies to other messages are indented a few spaces. Later, a simple change to the XSLT stylesheet can be employed to show graphical folders or other icons in front of each message. The next screen, shown in Figure 7-4, shows how users can post new messages to the discussion forum.

Figure 7-4. Post Message prototype

This page is also used to reply to an existing message. Although not shown here, the title changes to "Reply to Message," and the subject and message text are pre-filled with text from the original message. If the user submits this form without filling in all values, the web page is redisplayed with an error message.

The final screen prototype is shown in Figure 7-5. This screen allows users to view existing messages.

Figure 7-5. View Message prototype

7.2.2. XML Samples

While page designers are hard at work on the XHTML prototype screens, someone else can be working on sample XML data for each web page. Although different people may work on these tasks, a certain degree of coordination is critical at this point. The prototype XHTML pages may look great, but the XML must provide the data to enable those pages to be created. The XML designer will also have to work with the people who are designing the back-end data sources to determine if the desired data is even available.

When designing XML, the focus should be on data rather than presentation. All of the fonts and colors that are part of the CSS should have absolutely zero impact on the design of the XML. The XML will contain additional data that is not displayed, however. For example, creating hyperlinks requires some sort of identifier for each object. This allows the servlet to figure out which message the user clicked on. The XML data contains the identifier for the message, but the actual XHTML markup for the hyperlink comes from an XSLT stylesheet.

The XML data for the home page is shown in Example 7-3. Because the XML does not contain presentation information, it is smaller than the XHTML markup.

Example 7-3. home.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/home.xslt"?>
<home>
  <board id="0">
    <name>Java Programming</name>
    <description>General programming questions about Java.</description>
    <messages month="1" year="2001"/>
    <messages month="2" year="2001"/>
    <messages month="3" year="2001"/>
  </board>
  <board id="1">
    <name>XSLT Basics</name>
    <description>Writing effective XSLT stylesheets</description>
    <messages month="1" year="2001"/>
    <messages month="2" year="2001"/>
    <messages month="3" year="2001"/>
  </board>
  <board id="3">
    <name>Sample empty board</name>
    <description>This board has no messages.</description>
  </board>
</home>

Do not forget that this is still just a prototype XML file. The actual XML data will be dynamically generated by JDOM once the application is finished; this XML prototype code is used only for testing and development purposes.

In this XML data, each message board is represented by a <board> element that has an id attribute. When the user clicks on the "Post Message" web page hyperlink, this id is used to figure out which message board he or she wants to post to. The list of <messages> elements indicates months that have messages in them. These do not need id attributes because the month and year are used in the hyperlink.

The second line of the XML links to the XSLT stylesheet:

<?xml-stylesheet type="text/xsl" href="../xslt/home.xslt"?>

This is not used in the final application but is very useful during the prototyping and development process. By linking to the stylesheet, the transformation can be quickly viewed in an XSLT-compatible web browser by simply loading the XML page.

Iterative Design

The examples shown in this chapter are the result of several attempts to get the design "right." As in other areas of software design, figuring out what to place in the XHTML, XML, and XSLT is an iterative process that requires several attempts before the design can be finalized.

In a nutshell, the process works something like this:

  • Prototype the web pages using HTML or XHTML.

  • Create the XML datafile prototypes and optionally create DTDs.

  • Create XSLT stylesheets that transform the XML into XHTML.

  • Design and create back-end data sources and classes that know how to produce the required XML data.

  • Create servlets that tie everything together.

As each piece of the application is implemented, missing or redundant features will manifest themselves in other areas. This is where the iterative process comes into effect. If some features are not right the first time, simply refine the prototypes and repeat various steps in the process until all the pieces fit together.

The next XML file, shown in Example 7-4, contains data for the "View Month" page.

Example 7-4. viewMonth.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/viewMonth.xslt"?>
<viewMonth month="1" year="2001">
  <board id="1">
    <name>Java Programming</name>
    <description>General programming questions about Java.</description>
  </board>
  <message id="1" day="1">
    <subject>First test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
    <message id="2" day="2">
      <subject>Re: First test message</subject>
      <authorEmail>aidan@nowhere.com</authorEmail>
    </message>
  </message>
  <message id="3" day="4">
    <subject>Another test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
  </message>
</viewMonth>

Moving on to Example 7-5, we have the XML for the "Post/Reply Message" page.

Example 7-5. postMsg.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/postMsg.xslt"?>
<postMsg>
  <board id="1">
    <name>Java Programming</name>
    <description>The board description...</description>
  </board>
  <inResponseTo id="4">
    <subject>Test Subject</subject>
  </inResponseTo>
  <error code="ALL_FIELDS_REQUIRED"/>
  <prefill>
    <subject>Test Subject</subject>
    <authorEmail></authorEmail>
    <message>My Message</message>
  </prefill>
</postMsg>

This XML is used for both posting new messages and replying to existing messages because the web pages are virtually identical, and the data is the same in both cases. The <error> and <prefill> elements were not part of the original prototype, but it was quickly determined that these were needed if the user did not provide information for all required fields. When the "Post New Message" page is first displayed, these XML elements are not present. After the user clicks on the Submit button, however, these elements are inserted into the XML if a field is missing and the page needs to be redisplayed.

And finally, the XML for the "View Message" page is shown in Example 7-6.

Example 7-6. viewMsg.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/viewMsg.xslt"?>
<message id="5" month="1" day="4" year="2001">
  <board id="1">
    <name>Java Programming</name>
  </board>
  <inResponseTo id="4">
    <subject>Test Subject</subject>
  </inResponseTo>
  <subject>Re: Test Subject</subject>
  <authorEmail>burke_e@yahoo.com</authorEmail>
  <text>This is a test of the message
   text.</text>
</message>

A quick study of this data reveals that postMsg.xml and viewMsg.xml have many similarities. A few modifications to either XML file will enable us to reuse the same JDOM code later when producing these pages. The alternative is to keep these pages separate, which results in at least one additional Java class later on. The advantage of keeping these files separate is so that the XML generation code does not have to be cluttered up with a lot of if/else statements to figure out the mode of operation it is in.

7.2.3. XSLT Stylesheets

Yet another member of the development team can be assigned to the task of creating XSLT stylesheets, although he or she will have to wait until the XML and XHTML prototypes are complete. More often than not, the person designing the XML will be the one creating the initial XSLT stylesheets.

At this point in the process, a tool such as XML Spy can be invaluable.[26] The ability to edit the XSLT stylesheet and click on the Refresh button in an IDE makes development a snap. Alternately, an XSLT-compatible web browser can quickly display changes as stylesheets are edited. As explained in Chapter 1, "Introduction ", Microsoft's Internet Explorer 5.x supports XSLT, provided that the updated msxml parser is installed using the xmlinst utility.[27]

[26] XML Spy is a commercial XML editor that works nicely for XSLT development. It is available at http://www.xmlspy.com.

[27] As this is written, IE 6.0 is in beta testing. It supports the latest XSLT specification. The Mozilla browser will also support XSLT at some point.

Example 7-7 shows the XSLT for the discussion forum home page.

Example 7-7. XSLT for the home page

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** home.xslt
**
** Transforms the home page into XHTML
***********************************************************
-->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="utils.xslt"/>
  <xsl:param name="rootDir" select="'../docroot/'"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8"
    indent="yes"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>

  <!--
  **********************************************************
  ** Create the XHTML web page
  *******************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Discussion Forum Home</title>
        <link href="{$rootDir}forum.css"
            rel="stylesheet" type="text/css"/>
      </head>
      <body>
        <div class="box1">
          <h1>Discussion Forum Home</h1>
        </div>
        <xsl:apply-templates select="home/board"/>
      </body>
    </html>
  </xsl:template>

  <!--
  **********************************************************
  ** Output a box for each board in the discussion forum
  **********************************************************
  -->
  <xsl:template match="board">
    <xsl:variable name="boardID" select="@id"/>

    <div class="box2">
      <h2><xsl:value-of select="name"/></h2>
      <div><xsl:value-of select="description"/></div>
      <div style="margin-left: 2em;">
        <!-- create a link so the user can post a new message
             to this board -->
        <p>
        <a href="/forum/main/postMsg?mode=postNewMsg&amp;boardID={@id}">Post Message</a>
        to <xsl:value-of select="name"/>
        </p>

        <!-- For each month that has messages, show the
             month name and year number as a link -->
        <xsl:for-each select="messages">
         <ahref="forum/main/viewMonth?boardID={$boardID}&amp;month={@month}&amp;year=
         {@year}">
            <xsl:call-template name="utils.printShortMonthName">
              <xsl:with-param name="monthNumber" select="@month"/>
            </xsl:call-template>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="@year"/>
          </a>
          <!-- put a pipe character after
               all but the last month -->
          <xsl:if test="position() != last( )">
            <xsl:text> | </xsl:text>
          </xsl:if>
        </xsl:for-each>
      </div>
    </div>
  </xsl:template>
</xsl:stylesheet>

This stylesheet opens with the usual <xsl:stylesheet> tag and then proceeds to import utils.xslt. This is a stylesheet that contains common templates for formatting dates. Since these utilities are needed on just about every page, they are defined a common file that is imported, as shown here. This stylesheet also takes a parameter named rootDir, allowing the web application to specify the location of the document root directory:

<xsl:param name="rootDir" select="'../docroot/'"/>

The select attribute defines a default value for this parameter if none was specified. During the stylesheet development process, the XSLT is tested using a static XML file. This is done outside of a web application, so the parameter is not specified and the root directory defaults to ../docroot/. This makes it possible to locate the CSS file during development, when developers are working from a static directory structure on their file systems. Later, when the XSLT stylesheet is deployed to a web application and the servlet is running, the servlet can specify a different value for this parameter that indicates a directory relative to the web application context. This is a useful technique whenever a stylesheet has to reference external resources such as CSS files, JavaScript files, or images.

Next, the <xsl:output> element is used to set up XHTML output. The XHTML 1.0 Strict DTD is used, which eliminates many deprecated HTML 4.0 features. Because the strict DTD does away with many formatting tags, a CSS file is required to make the pages look presentable. All the XSLT needs to do is produce HTML code that references the external stylesheet, as shown here:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Discussion Forum Home</title>
    <link href="{$rootDir}forum.css"
        rel="stylesheet" type="text/css"/>
  </head>

The XSLT processor does not actually deal with the CSS file. From the perspective of XSLT, the <link> tag is just text that is copied to the result tree during the transformation process. Later, when the web browser displays the XHTML page, the actual CSS file is loaded. This technique is great because styles can be shared across all web pages without complicating the XSLT stylesheets.

The remainder of the stylesheet is pretty basic -- just matching patterns in the XML and producing XHTML content to the result tree. One important thing to point out here is the way that hyperlinks are created:

<a href="/forum/main/postMsg?mode=postNewMsg&amp;boardID={@id}">Post Message</a>

Since the ampersand character (&) is not allowed in an XML attribute value, it must be written using the &amp; built-in entity. As it turns out, browsers deal with this just fine, and the hyperlink works anyway.[28]

[28] We will see this again when dealing with WML in Chapter 10, "Wireless Applications".

What Is the URL?

You may be wondering how you are supposed to know what each hyperlink is actually supposed to be. At this stage of the game, you probably will not know, and your links will actually look something like this:

<a href="TODO: link to post a new message">Post Message</a>

This is fine for now, because you really won't know what to put there until the servlets are fully designed. Part of the servlet design process involves figuring out what parameters are required and what the legal values are. Until this work has been completed, however, an educated guess or "TODO" comment is fine.

Another key piece of this stylesheet shows how to call a utility template:

<xsl:call-template name="utils.printShortMonthName">
  <xsl:with-param name="monthNumber" select="@month"/>
</xsl:call-template>

The utils.printShortMonthName template is part of utils.xslt and is invoked just like a local template. The only difference is that the current stylesheet must import utils.xslt or the code will fail. Prefixing the template name with utils. has nothing to do with the actual filename; it is a convention adopted only for this application that makes the code a little easier to read, reducing the chances for naming conflicts.

The reusable XSLT stylesheet, utils.xslt, is shown next in Example 7-8.

Example 7-8. Reusable XSLT code

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template name="utils.printShortMonthName">
    <xsl:param name="monthNumber"/>
    <xsl:choose>
      <xsl:when test="$monthNumber='0'">Jan</xsl:when>
      <xsl:when test="$monthNumber='1'">Feb</xsl:when>
      <xsl:when test="$monthNumber='2'">Mar</xsl:when>
      <xsl:when test="$monthNumber='3'">Apr</xsl:when>
      <xsl:when test="$monthNumber='4'">May</xsl:when>
      <xsl:when test="$monthNumber='5'">Jun</xsl:when>
      <xsl:when test="$monthNumber='6'">Jul</xsl:when>
      <xsl:when test="$monthNumber='7'">Aug</xsl:when>
      <xsl:when test="$monthNumber='8'">Sep</xsl:when>
      <xsl:when test="$monthNumber='9'">Oct</xsl:when>
      <xsl:when test="$monthNumber='10'">Nov</xsl:when>
      <xsl:when test="$monthNumber='11'">Dec</xsl:when>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="utils.printLongMonthName">
    <xsl:param name="monthNumber"/>
    <xsl:choose>
      <xsl:when test="$monthNumber='0'">January</xsl:when>
      <xsl:when test="$monthNumber='1'">February</xsl:when>
      <xsl:when test="$monthNumber='2'">March</xsl:when>
      <xsl:when test="$monthNumber='3'">April</xsl:when>
      <xsl:when test="$monthNumber='4'">May</xsl:when>
      <xsl:when test="$monthNumber='5'">June</xsl:when>
      <xsl:when test="$monthNumber='6'">July</xsl:when>
      <xsl:when test="$monthNumber='7'">August</xsl:when>
      <xsl:when test="$monthNumber='8'">September</xsl:when>
      <xsl:when test="$monthNumber='9'">October</xsl:when>
      <xsl:when test="$monthNumber='10'">November</xsl:when>
      <xsl:when test="$monthNumber='11'">December</xsl:when>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Month numbers are indexed from position 0 to be consistent with the java.util.Calendar class, which also uses 0 to represent January. The templates convert the month number into an English month name.

viewMonth.xslt is shown in Example 7-9. It generates an XHTML page that shows all messages in a month for a particular board.

Example 7-9. XSLT for the View Month page

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** viewMonth.xslt
**
** Shows a month-view of messages in a given board.
***********************************************************
-->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="utils.xslt"/>
  <xsl:param name="rootDir" select="'../docroot/'"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8"
    indent="yes"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  <!-- ================= Global Variables ================= -->
  <xsl:variable name="global.boardName" select="/viewMonth/board/name"/>
  <xsl:variable name="global.boardID" select="/viewMonth/board/@id"/>
  <xsl:variable name="global.monthNum" select="/viewMonth/@month"/>
  <xsl:variable name="global.yearNum" select="/viewMonth/@year"/>

  <!--
  **********************************************************
  ** Create the XHTML web page
  *******************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>
          <xsl:value-of select="$global.boardName"/>
        </title>
        <!-- reference an external CSS file to keep this
             XSLT stylesheet smaller -->
        <link href="{$rootDir}forum.css"
            rel="stylesheet" type="text/css"/>
      </head>
      <body>
        <div class="box1">
          <h1>
            <xsl:value-of select="$global.boardName"/>
          </h1>
        </div>
        <div class="box2">
          <xsl:text>Messages for </xsl:text>
          <xsl:call-template name="utils.printLongMonthName">
            <xsl:with-param name="monthNumber" select="$global.monthNum"/>
          </xsl:call-template>
          <xsl:text>, </xsl:text>
          <xsl:value-of select="$global.yearNum"/>
        </div>
        <!-- ===== Quick Actions ====== -->
        <h3>Quick Actions</h3>
        <ul>
          <li>
            <a href="postMsg?mode=postNewMsg&amp;boardID={$global.boardID}">
          Post</a> a new message...</li>
          <li>Return to the <a href="home">home page</a>...</li>
        </ul>
        <!-- ===== Recursively show the message tree ===== -->
        <xsl:apply-templates select="viewMonth/message"/>
      </body>
    </html>
  </xsl:template>

  <!--
  **********************************************************
  ** Display a one-line summary for each message.
  *******************************************************-->
  <xsl:template match="message">
    <xsl:param name="indent" select="0"/>

    <!-- indent according to the 'indent' parameter -->
    <div style="margin-left: {$indent}em;">
      <a href="viewMsg?msgID={@id}">
        <xsl:value-of select="subject"/>
      </a>
      <xsl:text> posted by </xsl:text>
      <xsl:apply-templates select="authorEmail"/>
      <xsl:text> on </xsl:text>
      <xsl:value-of select="@day"/>
      <xsl:text disable-output-escaping="yes">&amp;nbsp;
      <xsl:call-template name="utils.printShortMonthName">
        <xsl:with-param name="monthNumber" select="$global.monthNum"/>
      </xsl:call-template>
            <xsl:text disable-output-escaping="yes">&amp;nbsp;
      <xsl:value-of select="$global.yearNum"/>

      <!-- recursively select all messages that are
          responses to this one. Increment the indentation
          with each call -->
      <xsl:apply-templates select="message">
        <xsl:with-param name="indent" select="$indent + 1"/>
      </xsl:apply-templates>
    </div>
  </xsl:template>

  <!--
  **********************************************************
  ** Show the author's email address.
  *******************************************************-->
  <xsl:template match="authorEmail">
    <a href="mailto:{.}">
      <xsl:value-of select="."/>
    </a>
  </xsl:template>
</xsl:stylesheet>

Because viewMonth.xslt shows a summary view of a large number of messages, the actual text content for each message is not included in the output. Instead, the message subject, author, and create date are displayed. These lines are grouped and indented according to replies, making threads of discussion immediately visible.

This stylesheet declares a series of global variables. These can be referenced throughout the stylesheet and are designed to make the code more maintainable. Since each variable is prefixed with global., the code is easy to understand when using the variables:

<xsl:value-of select="$global.boardName"/>
NOTE: The global. naming convention is not a standard part of XSLT. It is just a convention used here to make the XSLT more self-documenting.

The interesting part of this stylesheet involves construction of the tree of messages. Since messages in the XML are hierarchical, the XSLT must recursively process the data to properly show threads of discussion. Here is another look at a portion of the viewMonth.xml file presented earlier in this chapter:

<viewMonth month="1" year="2001">
  <board id="1">
    <name>Java Programming</name>
    <description>General programming questions about Java.</description>
  </board>
  <message id="1" day="1">
    <subject>First test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
    <message id="2" day="2">
      <subject>Re: First test message</subject>
      <authorEmail>aidan@nowhere.com</authorEmail>
    </message>
  </message>
  <message id="3" day="4">
    <subject>Another test message</subject>
    <authorEmail>burke_e@yahoo.com</authorEmail>
  </message>
</viewMonth>

In the XSLT stylesheet, the first part of the recursive process selects all <message> elements occurring immediately below the <viewMonth> element:

<xsl:apply-templates select="viewMonth/message"/>

This selects messages with ids 1 and 3, causing the following template to be instantiated:

<xsl:template match="message">
  <xsl:param name="indent" select="0"/>

This template takes a parameter for the level of indentation. If the parameter is not specified, as in this first usage, it defaults to 0. This code is followed by very basic XSLT code to produce a one-line summary of the current message, and then the template recursively instantiates itself:

<xsl:apply-templates select="message">
  <xsl:with-param name="indent" select="$indent + 1"/>
</xsl:apply-templates>

This efficiently selects all <message> elements that occur immediately within the current message and increments the indentation by 1. This allows the stylesheet to indent replies appropriately. The recursive process continues until no messages remain.

Another stylesheet, viewMsg.xslt, is responsible for displaying a single message. This is a simple XSLT stylesheet and can be found in Appendix A, "Discussion Forum Code". The only remaining stylesheet, postMsg.xslt, is shown in Example 7-10. This stylesheet supports two modes of operation. Therefore, it is more complicated than the previous examples.

Example 7-10. XSLT for the Post/Reply message page

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** postMsg.xslt
**
** Creates the "Post New Message" XHTML page and the
** "Reply to Message" XHTML page.
***********************************************************
-->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="utils.xslt"/>

  <!-- pass the root directory as a parameter, thus
       allowing this stylesheet to refer to the CSS file -->
  <xsl:param name="rootDir" select="'../docroot/'"/>

  <xsl:output method="xml" version="1.0" encoding="UTF-8"
    indent="yes"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>

  <!-- ===== Global Variables ===== -->
  <xsl:variable name="global.subject" select="/postMsg/prefill/subject"/>
  <xsl:variable name="global.email" select="/postMsg/prefill/authorEmail"/>
  <xsl:variable name="global.message" select="/postMsg/prefill/message"/>

  <xsl:variable name="global.title">
    <xsl:choose>
      <xsl:when test="/postMsg/inResponseTo">
        <xsl:text>Reply to Message</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>Post New Message</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!--
  **********************************************************
  ** Create the XHTML web page
  *******************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title><xsl:value-of select="$global.title"/></title>
        <link href="{$rootDir}forum.css"
            rel="stylesheet" type="text/css"/>
      </head>
      <body>
        <!-- show the page title and board name -->
        <div class="box1">
          <h1><xsl:value-of select="$global.title"/></h1>
          <div>
            <xsl:value-of select="postMsg/board/name"/>
          </div>
        </div>
        <xsl:apply-templates select="postMsg/inResponseTo"/>

        <div class="box2">
          <!-- optionally display error message -->
          <xsl:if test="postMsg/error/@code='ALL_FIELDS_REQUIRED'">
            <p class="error">All fields are required...</p>
          </xsl:if>

          <!-- Create an XHTML form. The user will provide
               the subject, and Email address, and
               the message text -->
          <form method="post" action="postMsg">
            <div>
            <input type="hidden" name="boardID"
                value="{postMsg/board/@id}"/>

            <!-- Determine the mode of operation -->
            <xsl:choose>
              <xsl:when test="/postMsg/inResponseTo">
                <input type="hidden" name="origMsgID"
                         value="{postMsg/inResponseTo/@id}"/>
                <input type="hidden" name="mode" value="replyToMsg"/>
              </xsl:when>
              <xsl:otherwise>
                <input type="hidden" name="mode" value="postNewMsg"/>
              </xsl:otherwise>
            </xsl:choose>
            </div>

            <!-- Show the input fields in a table to
                 keep things aligned properly -->
            <table>
              <tr>
                <td>Subject:</td>
                <td>
                  <input type="text" name="msgSubject"
                      value="{$global.subject}" size="60" maxlength="70"/>
                </td>
              </tr>
              <tr>
                <td nowrap="nowrap">Your Email:</td>
                <td>
                  <input type="text" name="authorEmail"
                      value="{$global.email}" size="60" maxlength="70"/>
                </td>
              </tr>
              <tr valign="top">
                <td>Message:</td>
                <td>
                  <!-- xsl:text prevents the XSLT processor from collapsing to
                       <textarea/>, which caused problems with many browsers. -->
                  <textarea name="msgText" wrap="hard" rows="12"
                      cols="60"><xsl:value-of
                      select="$global.message"/><xsl:text> </xsl:text></textarea>
                </td>
              </tr>
              <!-- The last table row contains a submit
                   and cancel button -->
              <tr>
                <td> </td>
                <td>
                  <input type="submit" name="submitBtn" value="Submit"/>
                  <input type="submit" name="cancelBtn" value="Cancel"/>
                </td>
              </tr>
            </table>
          </form>
        </div>
      </body>
    </html>
  </xsl:template>

  <!--
  **********************************************************
  ** Show the text: 'In Response to: Msg Subject'
  *******************************************************-->
  <xsl:template match="inResponseTo">
    <div>
      In Response to:
      <span style="font-weight: bold;">
      <xsl:value-of select="subject"/>
      </span>
    </div>
  </xsl:template>
</xsl:stylesheet>

Since this stylesheet must work for posting new messages as well as for replying to messages, it must determine the appropriate mode of operation. This can be accomplished by checking for the existence of elements that occur only in one mode or the other. For example, the <inResponseTo> XML element occurs only when the user replies to an existing message. Therefore, the XSLT stylesheet can define a variable for the page title as follows:

<xsl:variable name="global.title">
  <xsl:choose>
    <xsl:when test="/postMsg/inResponseTo">
      <xsl:text>Reply to Message</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>Post New Message</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:when test="/postMsg/inResponseTo"> returns true when the <inResponseTo> element exists in the original XML data. In this case, the global.title variable is set to "Reply to Message." Otherwise, the title defaults to "Post New Message."

This stylesheet optionally displays an error message when the user partially fills out the XHTML form and submits the data. The servlet redisplays the page with an error message, allowing the user to fix the problem. It does this by inserting the following XML element into the data:

<error code="ALL_FIELDS_REQUIRED"/>

The XSLT stylesheet tests for the existence of this element as follows:

<xsl:if test="postMsg/error/@code='ALL_FIELDS_REQUIRED'">
   <p class="error">All fields are required...</p>
</xsl:if>

An additional trick used in this stylesheet involves its interaction with a servlet. When the user submits the XHTML form data, the servlet must determine which mode of operation the user was in. For this task, the servlet looks for a request parameter called mode. Legal values for this parameter are replyToMsg and postNewMsg. Since the user is submitting an XHTML form, the easiest way to pass this data is via a hidden form field named mode. Here is the code that does the work:

<xsl:choose>
  <xsl:when test="/postMsg/inResponseTo">
    <input type="hidden" name="origMsgID"
         value="{postMsg/inResponseTo/@id}"/>
    <input type="hidden" name="mode" value="replyToMsg"/>
  </xsl:when>
  <xsl:otherwise>
    <input type="hidden" name="mode" value="postNewMsg"/>
  </xsl:otherwise>
</xsl:choose>

The stylesheet also inserts a hidden form field that contains the original message ID whenever the mode is replyToMsg. On the servlet side, the code looks something like this:

public void doGet(HttpServletRequest request, HttpServletResponse response) ... {
    String mode = request.getParameter("mode");
    if ("replyToMsg".equals(mode)) {
        String origMsgID = request.getParameter("origMsgID");
        ....

Previous | Home | Next
7. Discussion Forum | Book Index | 7.3. Making the XML Dynamic
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Making the XML Dynamic (Java and XSLT)

 Java and XSLTSearch this book

7.3. Making the XML Dynamic

At this point in the process, we have specified what each web page looks like, the XML data for each page, and the XSLT stylesheets to perform the necessary transformations. The next step is to figure out where the XML actually comes from. During the design and prototyping process, all XML data is created as a collection of static text files. This makes development of the XSLT stylesheets much easier, because the stylesheet authors can see results immediately without waiting for the back-end business logic and database access code to be created.

In the real system, static XML will not meet our requirements. We need the ability to extract data from a relational database and convert it into XML on the fly, as each page is requested. This makes the application "live," making updates to the database immediately visible to users. To the XSLT stylesheet developer, this is a moot point. The XSLT transformations work the same, regardless of whether the XML data came from a flat file, a relational database, or any other source.

7.3.1. Domain Classes

A domain class is a Java class that represents something in the problem domain. That's a fancy way to describe a class that represents the underlying problem you are trying to solve. In this example, we need to model the discussion forum as a series of Java classes to provide a buffer between the XML and the underlying relational database. In addition to representing data about the discussion forum, these Java classes can contain business logic.

Figure 7-6 contains a UML diagram of the classes found in the com.oreilly.forum.domain package. These classes do not contain any database access code, nor do they have any XML capability. Instead, they are simply data structures with a few key pieces of functionality. This makes it possible, for example, to rip out the relational database and replace it with some other back-end data source without changing to the XML generation logic.

Figure 7-6. Key domain classes

BoardSummary, MessageSummary, and Message are the key interfaces that describe the basic discussion forum capabilities. For each interface, an associated Impl class provides a basic implementation that contains get and set methods, which are not shown here. The MonthYear, DayMonthYear, and DateUtil classes are designed to represent and manipulate dates in an easy way and are listed in Appendix B, "JAXP API Reference". Finally, the MessageTree class encapsulates some business logic to sort a collection of messages into a hierarchical tree based on message replies and creation dates.

The BoardSummary interface, shown in Example 7-11, contains data that will eventually be used to build the discussion forum home page.

Example 7-11. BoardSummary.java

package com.oreilly.forum.domain;

import java.util.Iterator;

/**
 * Information about a message board.
 */
public interface BoardSummary {
    /**
     * @return a unique ID for this board.
     */
    long getID( );

    /**
     * @return a name for this board.
     */
    String getName( );

    /**
     * @return a description for this board.
     */
    String getDescription( );

    /**
     * @return an iterator of <code>MonthYear</code> objects.
     */
    Iterator getMonthsWithMessages( );
}

By design, the BoardSummary interface is read-only. This is an important feature because it means that once an instance of this class is extracted from the back-end data source, a programmer cannot accidentally call a set method only to discover later that the updates were not saved in the database. Technically, the client of this class could retrieve an Iterator of months with messages and then call the remove( ) method on the Iterator instance. Although we could take steps to make instances of this interface truly immutable, such efforts are probably overkill.

An early decision made in the design of the discussion forum was to assign a unique long identifier to each domain object. These identifiers have absolutely no meaning other than to identify objects uniquely, which will make the SQL queries much simpler later on.[29] This technique also makes it easy to reference objects from hyperlinks in the XHTML, because a simple identifier can be easily converted to and from a string representation.

[29] The code to actually generate these unique IDs is found in the DBUtil class, shown in Example 7-18.

The next interface, shown in Example 7-12, provides a summary for an individual message.

Example 7-12. MessageSummary.java

package com.oreilly.forum.domain;

import java.util.*;

/**
 * Basic information about a message, not including the message text.
 */
public interface MessageSummary extends Comparable {

    /**
     * @return the ID of the message that this one is a reply to, or
     *         -1 if none.
     */
    long getInReplyTo( );

    /**
     * @return the unique ID of this message.
     */
    long getID( );

    /**
     * @return when this message was created.
     */
    DayMonthYear getCreateDate( );

    /**
     * @return the board that this message belongs to.
     */
    BoardSummary getBoard( );

    /**
     * @return the subject of this message.
     */
    String getSubject( );

    /**
     * The author Email can be 80 characters.
     */
    String getAuthorEmail( );
}

The only thing missing from the MessageSummary interface is the actual message text. The Message interface, which extends from MessageSummary, adds the getText( ) method. This interface is shown in Example 7-13.

Example 7-13. Message.java

package com.oreilly.forum.domain;

/**
 * Represent a message, including the text.
 */
public interface Message extends MessageSummary {
    /**
     * @return the text of this message.
     */
    String getText( );
}

The decision to keep the message text in a separate interface was driven by a prediction that performance could be dramatically improved. Consider a web page that shows a hierarchical view of all messages for a given month. This page may contain hundreds of messages, displaying key information found in the MessageSummary interface. But the text of each message could contain thousands of words, so it was decided that the text should be retrieved later when a message is displayed in its entirety. For this page, an instance of a class that implements Message can be created.

These are the sorts of design decisions that cannot be made in complete isolation. Regardless of how cleanly XSLT and XML separate the presentation from the underlying data model, heavily used web pages should have some influence on design decisions made on the back end. The trick is to avoid falling into the trap of focusing too hard on early optimization at the expense of a clean design. In this case, the potential for large numbers of very long messages was significant enough to warrant a separate interface for Message.

The three reference implementation classes are MessageImpl, Message-SummaryImpl, and BoardSummaryImpl. These are basic Java classes that hold data and are listed in Appendix B, "JAXP API Reference". The JDBC data adapter layer (see Section 7.3.2, "Data Adapter Layer") will create and return new instances of these classes, which implement the interfaces in this package. If creating a new back-end data source in the future, it is possible to reuse these classes or write brand new classes that implement the appropriate interfaces.

The final class in this package, MessageTree , is listed in Example 7-14.

Example 7-14. MessageTree.java

package com.oreilly.forum.domain;

import java.util.*;

/**
 * Arranges a collection of MessageSummary objects into a tree.
 */
public class MessageTree {
    private List topLevelMsgs = new ArrayList( );

    // map ids to MessageSummary objects
    private Map idToMsgMap = new HashMap( );

    // map reply-to ids to lists of MessageSummary objects
    private Map replyIDToMsgListMap = new HashMap( );

    /**
     * Construct a new message tree from an iterator of MessageSummary
     * objects.
     */
    public MessageTree(Iterator messages) {
        while (messages.hasNext( )) {
            // store each message in a map for fast retrieval by ID
            MessageSummary curMsg = (MessageSummary) messages.next( );
            this.idToMsgMap.put(new Long(curMsg.getID( )), curMsg);

            // build the inverted map that maps reply-to IDs to
            // lists of messages
            Long curReplyID = new Long(curMsg.getInReplyTo( ));
            List replyToList =
                    (List) this.replyIDToMsgListMap.get(curReplyID);
            if (replyToList == null) {
                replyToList = new ArrayList( );
                this.replyIDToMsgListMap.put(curReplyID, replyToList);
            }
            replyToList.add(curMsg);
        }

        // build the list of top-level messages. A top-level message
        // fits one of the following two criteria:
        //  - its reply-to ID is -1
        //  - its reply-to ID was not found in the list of messages. This
        //    occurs when a message is a reply to a previous month's message
        Iterator iter = this.replyIDToMsgListMap.keySet().iterator( );
        while (iter.hasNext( )) {
            Long curReplyToID = (Long) iter.next( );
            if (curReplyToID.longValue( ) == -1
                    || !this.idToMsgMap.containsKey(curReplyToID)) {
                List msgsToAdd =
                        (List) this.replyIDToMsgListMap.get(curReplyToID);
                this.topLevelMsgs.addAll(msgsToAdd);
            }
        }
        Collections.sort(this.topLevelMsgs);
    }

    public Iterator getTopLevelMessages( ) {
        return this.topLevelMsgs.iterator( );
    }

    /**
     * @return an iterator of MessageSummary objects that are replies
     *         to the specified message.
     */
    public Iterator getReplies(MessageSummary msg) {
        List replies = (List) this.replyIDToMsgListMap.get(
                new Long(msg.getID( )));
        if (replies != null) {
            Collections.sort(replies);
            return replies.iterator( );
        } else {
            return Collections.EMPTY_LIST.iterator( );
        }
    }
}

The MessageTree class helps organize a list of messages according to threads of discussion. If you look back at the code for MessageSummary, you will see that each message keeps track of the message that it is in reply to:

public interface MessageSummary extends Comparable {
    ...
    long getInReplyTo( );
    ...
}

If the message is a top-level message, then the reply-to id is -1. Otherwise, it always refers to some other message. Since a message does not have a corresponding method to retrieve a list of replies, the MessageTree class must build this list for each message. This leads to the three data structures found in the MessageTree class:

private List topLevelMsgs = new ArrayList( );
private Map idToMsgMap = new HashMap( );
private Map replyIDToMsgListMap = new HashMap( );

When the MessageTree is constructed, it is given an Iterator of all messages in a month. From this Iterator, the idToMsgMap data structure is built. All messages are stored in idToMsgMap, which is used for rapid retrieval based on message ids. While building the idToMsgMap, the constructor also builds the replyIDToMsgListMap. The keys in this map are reply-to ids, and the values are lists of message ids. In other words, each key maps to a list of replies.

After the first two data structures are built, the list of top-level messages is built. This is accomplished by looping over all keys in the idToMsgMap and then looking for messages that have a reply-to id of -1. In addition, messages whose reply-to id could not be located are also considered to be top-level messages. This occurs when a message is in reply to a previous month's message. All of this code can be seen in the MessageTree constructor.

7.3.2. Data Adapter Layer

Bridging the gap between an object-oriented class library and a physical database is often quite difficult. Enterprise JavaBeans (EJB) can be used for this purpose. However, this makes it extremely hard to deploy the discussion forum at a typical web hosting service. By limiting the application to servlets and a relational database, it is possible to choose from several ISPs that support both servlets and JDBC access to databases such as MySQL.

In addition to the software constraints found at most web hosting providers, design flexibility is another consideration. Today, direct access to a MySQL database may be the preferred approach. In the future, a full EJB solution with some other database may be desired. Or, we may choose to store messages in flat files instead of any database at all. All of these capabilities are achieved by using an abstract class called DataAdapter. This class is shown in Figure 7-7 along with several related classes.

Figure 7-7. Data adapter design

The DataAdapter class defines an interface to some back-end data source. As shown in the class diagram, FakeDataAdapter and JdbcDataAdapter are subclasses. These implement the data tier using flat files and relational databases, respectively. It is easy to imagine someone creating an EJBDataAdapter at some point in the future. ForumConfig is used to determine which subclass of DataAdapter to instantiate, and the DBUtil class encapsulates a few commonly used JDBC functions.

The source code for ForumConfig is shown in Example 7-15. This is a simple class that places configurable application settings in a single place. As shown later in this chapter, all configurable settings are stored in the servlet's deployment descriptor, so they do not have to be hardcoded. The first thing the servlet does is read the values and store them in ForumConfig.[30]

[30] JNDI could also be used for this purpose. However, JNDI requires more configuration and may make it harder to deploy to some ISPs.

Example 7-15. ForumConfig.java

package com.oreilly.forum;

/**
 * Define application-wide configuration information. The Servlet
 * must call the setValues( ) method before any of the get
 * methods in this class can be used.
 */
public class ForumConfig {
    // maximum sizes of various fields in the database
    public static final int MAX_BOARD_NAME_LEN = 80;
    public static final int MAX_BOARD_DESC_LEN = 255;
    public static final int MAX_MSG_SUBJECT_LEN = 80;
    public static final int MAX_EMAIL_LEN = 80;

    private static String jdbcDriverClassName;
    private static String databaseURL;
    private static String adapterClassName;

    public static void setValues(
            String jdbcDriverClassName,
            String databaseURL,
            String adapterClassName) {
        ForumConfig.jdbcDriverClassName = jdbcDriverClassName;
        ForumConfig.databaseURL = databaseURL;
        ForumConfig.adapterClassName = adapterClassName;
    }

    /**
     * @return the JDBC driver class name.
     */
    public static String getJDBCDriverClassName( ) {
        return ForumConfig.jdbcDriverClassName;
    }

    /**
     * @return the JDBC database URL.
     */
    public static String getDatabaseURL( ) {
        return ForumConfig.databaseURL;
    }

    /**
     * @return the data adapter implementation class name.
     */
    public static String getAdapterClassName( ) {
        return ForumConfig.adapterClassName;
    }

    private ForumConfig( ) {
    }
}

The DataException class is a very basic exception that indicates a problem with the back-end data source. It hides database-specific exceptions from the client, leaving the door open for nondatabase implementations in the future. For example, an EJB tier could be added, but the EJBs would throw RemoteException and EJBException instead of SQLException. Therefore, whenever a specific exception is thrown, it is wrapped in an instance of DataException before being propogated to the caller. The source code for DataException is found in Appendix B, "JAXP API Reference".

The code for DataAdapter, shown in Example 7-16, demonstrates how each method throws DataException. This class is the centerpiece of the "data abstraction" layer, insulating the domain classes from the underlying database implementation.

Example 7-16. DataAdapter.java

package com.oreilly.forum.adapter;

import com.oreilly.forum.*;
import com.oreilly.forum.domain.*;
import java.util.*;

/**
 * Defines an interface to a data source.
 */
public abstract class DataAdapter {
    private static DataAdapter instance;

    /**
     * @return the singleton instance of this class.
     */
    public static synchronized DataAdapter getInstance( )
            throws DataException {
        if (instance == null) {
            String adapterClassName = ForumConfig.getAdapterClassName( );
            try {
                Class adapterClass = Class.forName(adapterClassName);
                instance = (DataAdapter) adapterClass.newInstance( );
            } catch (Exception ex) {
                throw new DataException("Unable to instantiate "
                        + adapterClassName);
            }
        }
        return instance;
    }

    /**
     * @param msgID must be a valid message identifier.
     * @return the message with the specified id.
     * @throws DataException if msgID does not exist or a database
     * error occurs.
     */
    public abstract Message getMessage(long msgID) throws DataException;

    /**
     * Add a reply to an existing message.
     *
     * @throws DataException if a database error occurs, or if any
     * parameter is illegal.
     */
    public abstract Message replyToMessage(long origMsgID, String msgSubject,
            String authorEmail, String msgText) throws DataException;

    /**
     * Post a new message.
     *
     * @return the newly created message.
     * @throws DataException if a database error occurs, or if any
     * parameter is illegal.
     */
    public abstract Message postNewMessage(long boardID, String msgSubject,
            String authorEmail, String msgText) throws DataException;

    /**
     * If no messages exist for the specified board and month, return
     * an empty iterator.
     * @return an iterator of <code>MessageSummary</code> objects.
     * @throws DataException if the boardID is illegal or a database
     * error occurs.
     */
    public abstract Iterator getAllMessages(long boardID, MonthYear month)
            throws DataException;

    /**
     * @return an iterator of all <code>BoardSummary</code> objects.
     */
    public abstract Iterator getAllBoards( ) throws DataException;

    /**
     * @return a board summary for the given id.
     * @throws DataException if boardID is illegal or a database
     *                       error occurs.
     */
    public abstract BoardSummary getBoardSummary(long boardID)
            throws DataException;
}

DataAdapter consists of abstract methods and one static method called getInstance( ). This implements a singleton design pattern, returning an instance of a DataAdapter subclass.[31] The actual return type is specified in the ForumConfig class, and Java reflection APIs are used to instantiate the object:

[31] See Gamma et al., Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994).

String adapterClassName = ForumConfig.getAdapterClassName( );
try {
    Class adapterClass = Class.forName(adapterClassName);
    instance = (DataAdapter) adapterClass.newInstance( );
} catch (Exception ex) {
    throw new DataException("Unable to instantiate "
            + adapterClassName);
}

All remaining methods are abstract and are written in terms of interfaces defined in the com.oreilly.forum.domain package. For example, a message can be retrieved by its ID:

public abstract Message getMessage(long msgID) throws DataException;

By writing this code in terms of the Message interface, a future programmer could easily write a new class that implements Message in a different way. Throughout the DataAdapter class, a DataException occurs when an id is invalid, or when the underlying database fails.

The downloadable discussion forum implementation comes with a "fake" implementation of DataAdapter as well as a JDBC-based implementation. The fake implementation is listed in Appendix B, "JAXP API Reference". The database implementation has been tested on Microsoft Access as well as MySQL and should work on just about any relational database that includes a JDBC driver. Figure 7-8 shows the physical database design that the JdbcDataAdapter class uses.

Figure 7-8. Database design

The database is quite simple. Each table has an id column that defines a unique identifier and primary key for each row of data. Message.inReplyToID contains a reference to another message that this one is in reply to, or -1 if this is a top-level message. The create date for each message is broken down into month, day, and year. Although the application could store the date and time in some other format, this approach makes it really easy to issue queries such as:

SELECT subject
FROM Message
WHERE createMonth=3 AND createYear=2001

The Message.boardID column is a foreign key that identifies which board a message belongs to. The Message.msgText column can contain an unlimited amount of text, while the remaining fields all contain fixed-length text.

If you are using MySQL, Example 7-17 shows a "dump" file that can be used to easily recreate the database using the import utility that comes with the database.

Example 7-17. MySQL dump

# MySQL dump 8.8
#
# Host: localhost    Database: forum
#--------------------------------------------------------
# Server version	3.23.23-beta

#
# Table structure for table 'board'
#

CREATE TABLE board (
  id bigint(20) DEFAULT '0' NOT NULL,
  name char(80) DEFAULT '' NOT NULL,
  description char(255) DEFAULT '' NOT NULL,
  PRIMARY KEY (id)
);

#
# Dumping data for table 'board'
#

INSERT INTO board VALUES (0,'XSLT Basics',
    'How to create and use XSLT stylesheets and processors');
INSERT INTO board VALUES (1,'JAXP Programming Techniques','How to use JAXP 1.1');

#
# Table structure for table 'message'
#

CREATE TABLE message (
  id bigint(20) DEFAULT '0' NOT NULL,
  inReplyToID bigint(20) DEFAULT '0' NOT NULL,
  createMonth int(11) DEFAULT '0' NOT NULL,
  createDay int(11) DEFAULT '0' NOT NULL,
  createYear int(11) DEFAULT '0' NOT NULL,
  boardID bigint(20) DEFAULT '0' NOT NULL,
  subject varchar(80) DEFAULT '' NOT NULL,
  authorEmail varchar(80) DEFAULT '' NOT NULL,
  msgText text DEFAULT '' NOT NULL,
  PRIMARY KEY (id),
  KEY inReplyToID (inReplyToID),
  KEY createMonth (createMonth),
  KEY createDay (createDay),
  KEY boardID (boardID)
);

The DBUtil class, shown in Example 7-18, consists of utility functions that make it a little easier to work with relational databases from Java code.

Example 7-18. DBUtil.java

package com.oreilly.forum.jdbcimpl;

import java.io.*;
import java.sql.*;
import java.util.*;

/**
 * Helper methods for relational database access using JDBC.
 */
public class DBUtil {

    // a map of table names to maximum ID numbers
    private static Map tableToMaxIDMap = new HashMap( );

    /**
     * Close a statement and connection.
     */
    public static void close(Statement stmt, Connection con) {
        if (stmt != null) {
            try {
                stmt.close( );
            } catch (Exception ignored1) {
            }
        }
        if (con != null) {
            try {
                con.close( );
            } catch (Exception ignored2) {
            }
        }
    }

    /**
     * @return a new Connection to the database.
     */
    public static Connection getConnection(String dbURL)
            throws SQLException {
        // NOTE: implementing a connection pool would be a worthy
        //       enhancement
        return DriverManager.getConnection(dbURL);
    }

    /**
     * Close any connections that are still open. The Servlet will
     * call this method from its destroy( ) method.
     */
    public static void closeAllConnections( ) {
        // NOTE: if connection pooling is ever implemented, close
        //       the connections here.
    }

    /**
     * Store a long text field in the database. For example, a message's
     * text will be quite long and cannot be stored using JDBC's
     * setString( ) method.
     */
    public static void setLongString(PreparedStatement stmt,
            int columnIndex, String data) throws SQLException {
        if (data.length( ) > 0) {
            stmt.setAsciiStream(columnIndex,
                    new ByteArrayInputStream(data.getBytes( )),
                    data.length( ));
        } else {
            // this 'else' condition was introduced as a bug fix.  It was
            // discovered that the 'setAsciiStream' code shown above
            // caused MS Access throws a "function sequence error"
            // when the string was zero length.  This code now works.
            stmt.setString(columnIndex, "");
        }
    }

    /**
     * @return a long text field from the database.
     */
    public static String getLongString(ResultSet rs, int columnIndex)
            throws SQLException {
        try {
            InputStream in = rs.getAsciiStream(columnIndex);
            if (in == null) {
                return "";
            }

            byte[] arr = new byte[250];
            StringBuffer buf = new StringBuffer( );
            int numRead = in.read(arr);
            while (numRead != -1) {
                buf.append(new String(arr, 0, numRead));
                numRead = in.read(arr);
            }
            return buf.toString( );
        } catch (IOException ioe) {
            ioe.printStackTrace( );
            throw new SQLException(ioe.getMessage( ));
        }
    }

    /**
     * Compute a new unique ID. It is assumed that the specified table
     * has a column named 'id' of type 'long'. It is assumed that
     * that all parts of the program will use this method to compute
     * new IDs.
     * @return the next available unique ID for a table.
     */
    public static synchronized long getNextID(String tableName,
            Connection con) throws SQLException {
        Statement stmt = null;

        try {
            // if a max has already been retrieved from this table,
            // compute the next id without hitting the database
            if (tableToMaxIDMap.containsKey(tableName)) {
                Long curMax = (Long) tableToMaxIDMap.get(tableName);
                Long newMax = new Long(curMax.longValue( ) + 1L);
                tableToMaxIDMap.put(tableName, newMax);
                return newMax.longValue( );
            }

            stmt = con.createStatement( );
            ResultSet rs = stmt.executeQuery(
                    "SELECT MAX(id) FROM " + tableName);
            long max = 0;
            if (rs.next( )) {
                max = rs.getLong(1);
            }
            max++;
            tableToMaxIDMap.put(tableName, new Long(max));
            return max;
        } finally {
            // just close the statement
            close(stmt, null);
        }
    }
}

DBUtil has a private class field called tableToMaxIDMap that keeps track of the largest unique id found in each table. This works in conjunction with the getNextID( ) method, which returns the next available unique id for a given table name. By keeping the unique ids cached in the Map, the application reduces the required database hits. It should be noted that this approach is likely to fail if anyone manually adds a new id to the database without consulting this method.

The close( ) method is useful because nearly everything done with JDBC requires the programmer to close a Statement and Connection. This method should always be called from a finally block, which is guaranteed to be called regardless of whether or not an exception was thrown. For example:

Connection con = null;
Statement stmt = null;
try {
    // code to create the Connection and Statement
    ...
    // code to access the database
    ...
} finally {
    DBUtil.close(stmt, con);
}

If JDBC resources are not released inside of a finally block, it is possible to accidentally leave Connections open for long periods of time. This is problematic because database performance can suffer, and some databases limit the number of concurrent connections.

Although connection pooling is not supported in this version of the application, DBUtil does include the following method:

public static Connection getConnection(String dbURL)

In a future version of the class, it will be very easy to have this method return a Connection instance from pool, rather than creating a new instance with each call. Additionally, the DBUtil.close( ) method could return the Connection back to the pool instead of actually closing it. These are left as future considerations to keep things reasonably sized for the book.

The setLongString( ) and getLongString( ) methods are used for setting and retrieving text for messages. Since this text may be extremely long, it cannot be stored in the same way that shorter strings are stored. In some databases, these are referred to as CLOB columns. MS Access uses the MEMO type, while MySQL uses the TEXT data type. Since this is an area where databases can be implemented differently, the code is placed into the DBUtil class for consistency. If a special concession has to be made for a particular database, it can be made in one place rather than in every SQL statement throughout the application.

Finally, the JdbcDataAdapter class is presented in Example 7-19. This is the relational database implementation of the DataAdapter class and should work with just about any relational database.

Example 7-19. JdbcDataAdapter.java

package com.oreilly.forum.jdbcimpl;

import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import java.sql.*;
import java.util.*;

/**
 * An implementation of the DataAdapter that uses JDBC.
 */
public class JdbcDataAdapter extends DataAdapter {

    private static String dbURL = ForumConfig.getDatabaseURL( );

    /**
     * Construct the data adapter and load the JDBC driver.
     */
    public JdbcDataAdapter( ) throws DataException {
        try {
            Class.forName(ForumConfig.getJDBCDriverClassName( ));
        } catch (Exception ex) {
            ex.printStackTrace( );
            throw new DataException("Unable to load JDBC driver: "
                    + ForumConfig.getJDBCDriverClassName( ));
        }
    }

    /**
     * @param msgID must be a valid message identifier.
     * @return the message with the specified id.
     * @throws DataException if msgID does not exist or a database
     * error occurs.
     */
    public Message getMessage(long msgID) throws DataException {
        Connection con = null;
        Statement stmt = null;
        try {
            con = DBUtil.getConnection(dbURL);
            stmt = con.createStatement( );
            ResultSet rs = stmt.executeQuery(
                    "SELECT inReplyToID, createDay, createMonth, createYear, "
                    + "boardID, subject, authorEmail, msgText "
                    + "FROM Message WHERE id="
                    + msgID);
            if (rs.next( )) {
                long inReplyToID = rs.getLong(1);
                int createDay = rs.getInt(2);
                int createMonth = rs.getInt(3);
                int createYear = rs.getInt(4);
                long boardID = rs.getLong(5);
                String subject = rs.getString(6);
                String authorEmail = rs.getString(7);
                String msgText = DBUtil.getLongString(rs, 8);

                BoardSummary boardSummary = this.getBoardSummary(boardID, stmt);

                return new MessageImpl(msgID,
                        new DayMonthYear(createDay, createMonth, createYear),
                        boardSummary, subject, authorEmail, msgText,
                        inReplyToID);
            } else {
                throw new DataException("Illegal msgID");
            }
        } catch (SQLException sqe) {
            sqe.printStackTrace( );
            throw new DataException(sqe.getMessage( ));
        } finally {
            DBUtil.close(stmt, con);
        }
    }

    /**
     * Add a reply to an existing message.
     *
     * @throws DataException if a database error occurs, or if any
     * parameter is illegal.
     */
    public Message replyToMessage(long origMsgID,
            String msgSubject, String authorEmail, String msgText)
            throws DataException {
        Message inReplyToMsg = this.getMessage(origMsgID);
        return insertMessage(inReplyToMsg.getBoard( ), origMsgID,
                msgSubject, authorEmail, msgText);

    }

    /**
     * Post a new message.
     *
     * @return the newly created message.
     * @throws DataException if a database error occurs, or if any
     * parameter is illegal.
     */
    public Message postNewMessage(long boardID, String msgSubject,
            String authorEmail, String msgText) throws DataException {

        BoardSummary board = this.getBoardSummary(boardID);
        return insertMessage(board, -1, msgSubject, authorEmail, msgText);
    }

    /**
     * If no messages exist for the specified board and month, return
     * an empty iterator.
     * @return an iterator of <code>MessageSummary</code> objects.
     * @throws DataException if the boardID is illegal or a database
     * error occurs.
     */
    public Iterator getAllMessages(long boardID, MonthYear month)
            throws DataException {
        List allMsgs = new ArrayList( );

        Connection con = null;
        Statement stmt = null;
        try {
            con = DBUtil.getConnection(dbURL);
            stmt = con.createStatement( );

            BoardSummary boardSum = this.getBoardSummary(boardID, stmt);

            ResultSet rs = stmt.executeQuery(
                    "SELECT id, inReplyToID, createDay, "
                    + "subject, authorEmail "
                    + "FROM Message WHERE createMonth="
                    + month.getMonth( )
                    + " AND createYear="
                    + month.getYear( )
                    + " AND boardID="
                    + boardID);

            while (rs.next( )) {
                long msgID = rs.getLong(1);
                long inReplyTo = rs.getLong(2);
                int createDay = rs.getInt(3);
                String subject = rs.getString(4);
                String authorEmail = rs.getString(5);

                DayMonthYear createDMY = new DayMonthYear(
                        createDay, month.getMonth(), month.getYear( ));

                allMsgs.add(new MessageSummaryImpl(msgID, createDMY,
                        boardSum,
                        subject, authorEmail, inReplyTo));
            }
            return allMsgs.iterator( );
        } catch (SQLException sqe) {
            sqe.printStackTrace( );
            throw new DataException(sqe);
        } finally {
            DBUtil.close(stmt, con);
        }
    }

    /**
     * @return an iterator of all <code>BoardSummary</code> objects.
     */
    public Iterator getAllBoards( ) throws DataException {
        List allBoards = new ArrayList( );

        Connection con = null;
        Statement stmt = null;
        Statement stmt2 = null;
        try {
            con = DBUtil.getConnection(dbURL);
            stmt = con.createStatement( );
            stmt2 = con.createStatement( );
            ResultSet rs = stmt.executeQuery(
                    "SELECT id, name, description FROM Board "
                    + "ORDER BY name");

            while (rs.next( )) {
                long id = rs.getLong(1);
                String name = rs.getString(2);
                String description = rs.getString(3);

                // get the months with messages. Use a different
                // Statement object because we are in the middle of
                // traversing a ResultSet that was created with the
                // first Statement.
                List monthsWithMessages =
                        this.getMonthsWithMessages(id, stmt2);

                allBoards.add(new BoardSummaryImpl(id, name, description,
                        monthsWithMessages));
            }
            return allBoards.iterator( );
        } catch (SQLException sqe) {
            sqe.printStackTrace( );
            throw new DataException(sqe);
        } finally {
            if (stmt2 != null) {
                try {
                    stmt2.close( );
                } catch (SQLException ignored) {
                }
            }
            DBUtil.close(stmt, con);
        }
    }

    /**
     * @return a board summary for the given id.
     * @throws DataException if boardID is illegal or a database
     *                       error occurs.
     */
    public BoardSummary getBoardSummary(long boardID)
            throws DataException {
        Connection con = null;
        Statement stmt = null;
        try {
            con = DBUtil.getConnection(dbURL);
            stmt = con.createStatement( );
            return getBoardSummary(boardID, stmt);
        } catch (SQLException sqe) {
            sqe.printStackTrace( );
            throw new DataException(sqe);
        } finally {
            DBUtil.close(stmt, con);
        }
    }

    private BoardSummary getBoardSummary(long boardID, Statement stmt)
            throws DataException, SQLException {
        ResultSet rs = stmt.executeQuery(
                "SELECT name, description FROM Board WHERE id=" + boardID);

        if (rs.next( )) {
            String name = rs.getString(1);
            String description = rs.getString(2);

            List monthsWithMessages = getMonthsWithMessages(boardID, stmt);

            return new BoardSummaryImpl(boardID, name, description,
                    monthsWithMessages);
        } else {
            throw new DataException("Unknown boardID");
        }
    }

    /**
     * @return a list of MonthYear objects
     */
    private List getMonthsWithMessages(long boardID, Statement stmt)
            throws SQLException {

        List monthsWithMessages = new ArrayList( );
        ResultSet rs = stmt.executeQuery(
                "SELECT DISTINCT createMonth, createYear "
                + "FROM Message "
                + "WHERE boardID=" + boardID);
        while (rs.next( )) {
            monthsWithMessages.add(new MonthYear(
                    rs.getInt(1), rs.getInt(2)));
        }
        return monthsWithMessages;
    }

    private Message insertMessage(BoardSummary board, long inReplyToID,
            String msgSubject, String authorEmail,
            String msgText) throws DataException {
        // avoid overflowing the max database column lengths
        if (msgSubject.length( ) > ForumConfig.MAX_MSG_SUBJECT_LEN) {
            msgSubject = msgSubject.substring(0,
                    ForumConfig.MAX_MSG_SUBJECT_LEN);
        }
        if (authorEmail.length( ) > ForumConfig.MAX_EMAIL_LEN) {
            authorEmail = authorEmail.substring(0,
                    ForumConfig.MAX_EMAIL_LEN);
        }

        DayMonthYear createDate = new DayMonthYear( );

        Connection con = null;
        PreparedStatement stmt = null;
        try {
            con = DBUtil.getConnection(dbURL);
            long newMsgID = DBUtil.getNextID("Message", con);
            stmt = con.prepareStatement("INSERT INTO Message "
                    + "(id, inReplyToID, createMonth, createDay, createYear, "
                    + "boardID, subject, authorEmail, msgText) "
                    + "VALUES (?,?,?,?,?,?,?,?,?)");
            stmt.setString(1, Long.toString(newMsgID));
            stmt.setString(2, Long.toString(inReplyToID));
            stmt.setInt(3, createDate.getMonth( ));
            stmt.setInt(4, createDate.getDay( ));
            stmt.setInt(5, createDate.getYear( ));
            stmt.setString(6, Long.toString(board.getID( )));
            stmt.setString(7, msgSubject);
            stmt.setString(8, authorEmail);
            DBUtil.setLongString(stmt, 9, msgText);

            stmt.executeUpdate( );

            return new MessageImpl(newMsgID, createDate,
                    board, msgSubject, authorEmail,
                    msgText, inReplyToID);

        } catch (SQLException sqe) {
            sqe.printStackTrace( );
            throw new DataException(sqe);
        } finally {
            DBUtil.close(stmt, con);
        }
    }
}

Since this is not a book about relational database access using Java, we will not focus on the low-level JDBC details found in this class. The SQL code is intentionally simple to make this class portable to several different relational databases. The database URL and JDBC driver class name are retrieved from the ForumConfig class instead of hardcoded into the class:

private static String dbURL = ForumConfig.getDatabaseURL( );

/**
* Construct the data adapter and load the JDBC driver.
 */
public JdbcDataAdapter( ) throws DataException {
    try {
       Class.forName(ForumConfig.getJDBCDriverClassName( ));
    } catch (Exception ex) {
        ex.printStackTrace( );
        throw new DataException("Unable to load JDBC driver: "
                + ForumConfig.getJDBCDriverClassName( ));
    }
}

Creating connections with the DBUtil class is another common pattern:

Connection con = null;
try {
    con = DBUtil.getConnection(dbURL);

As mentioned earlier, this approach leaves the door open for connection pooling in a future implementation. When the pool is written, it only needs to be added to the DBUtil class in a single place. When connections and statements are no longer needed, they should always be closed in a finally block:

} finally {
    DBUtil.close(stmt, con);
}

As mentioned earlier, this ensures that they will be closed because finally blocks are executed regardless of whether an exception occurs.

7.3.3. JDOM XML Production

The discussion forum code presented up to this point can extract data from a relational database and create instances of Java domain classes. The next step is to convert the domain objects into XML that can be transformed using XSLT. For this task, we use the JDOM class library. As mentioned in earlier chapters, JDOM is available at http://www.jdom.org and is open source software. Although the DOM API can also be used, JDOM is somewhat easier to work with, which results in cleaner code.[32]

[32] For a DOM example, see the LibraryDOMCreator class shown in Example 1-4.

The basic pattern relies on various JDOM "producer" classes, each of which knows how to convert one or more domain objects into XML. This approach capitalizes on the recursive nature of XML by having each class produce a JDOM Element instance. Some of these Element instances represent entire documents, while others represent a small fragment of XML. These fragments can be recursively embedded into other Element instances to build up more complex structures.

Keeping XML production outside of domain objects is useful for several reasons:

  • JDOM producer classes can be replaced with DOM producers or some other technology.

  • Additional producers can be written to generate new forms of XML without modifying the domain objects or existing XML producers.

  • Domain objects may be represented as Java interfaces with several different implementation classes. By keeping XML production separate, the same producer works with all implementations of the domain interfaces.

The HomeJDOM class, shown in Example 7-20, is quite simple. It merely produces a <home> element containing a list of <board> elements. Since a separate JDOM producer class creates the <board> elements, the HomeJDOM class merely assembles those XML fragments into a larger structure.

Example 7-20. HomeJDOM.java

package com.oreilly.forum.xml;

import com.oreilly.forum.domain.*;
import java.util.*;
import org.jdom.*;

/**
 * Produce JDOM data for the home page.
 */
public class HomeJDOM {

    /**
     * @param boards an iterator of <code>BoardSummary</code> objects.
     */
    public static Element produceElement(Iterator boards) {
        Element homeElem = new Element("home");
        while (boards.hasNext( )) {
            BoardSummary curBoard = (BoardSummary) boards.next( );
            homeElem.addContent(BoardSummaryJDOM.produceElement(curBoard));
        }

        return homeElem;
    }

    private HomeJDOM( ) {
    }
}

As shown in the HomeJDOM class, the constructor is private. This prevents instantiation of the class, another decision made in the name of efficiency. Since each of the JDOM producer classes for the discussion forum are stateless and thread-safe, the produceElement( ) method can be static. This means that there is no reason to create instances of the JDOM producers, because the same method is shared by many concurrent threads. Additionally, there is no common base class because each of the produceElement( ) methods accept different types of objects as parameters.

Other JDOM Options

The static-method technique shown in this chapter is certainly not the only way to produce JDOM data. You may prefer to create custom subclasses of JDOM's Element class. In your subclass, the constructor can take a domain object as a parameter. So instead of calling a static method to produce XML, you end up writing something like:

Iterator boards = ...
Element homeElem = new HomeElement(boards);

Yet another option is to embed the JDOM production code into the domain objects. In this approach, your code would resemble this:

BoardSummary board = ...
Element elem = board.convertToJDOM( );

This approach is probably not the best, because it tightly couples the JDOM code with the domain classes. It also will not work for cases where the XML data is produced from a group of domain objects instead of from a single object.

Regardless of the technique followed, consistency is the most important goal. If every class follows the same basic pattern, then the development team only has to understand one example to be familiar with the entire system.

The code for ViewMonthJDOM is shown in Example 7-21. This class creates XML data for an entire month's worth of messages.

Example 7-21. ViewMonthJDOM.java

package com.oreilly.forum.xml;

import java.util.*;
import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import org.jdom.*;

/**
 * Creates the JDOM for the month view of a board.
 */
public class ViewMonthJDOM {

    /**
     * @param board the message board to generate JDOM for.
     * @param month the month and year to view.
     */
    public static Element produceElement(BoardSummary board,
            MonthYear month) throws DataException {
        Element viewMonthElem = new Element("viewMonth");
        viewMonthElem.addAttribute("month",
                Integer.toString(month.getMonth( )));
        viewMonthElem.addAttribute("year",
                Integer.toString(month.getYear( )));

        // create the <board> element...
        Element boardElem = BoardSummaryJDOM.produceNameIDElement(board);
        viewMonthElem.addContent(boardElem);

        DataAdapter adapter = DataAdapter.getInstance( );

        MessageTree msgTree = new MessageTree(adapter.getAllMessages(
                board.getID( ), month));

        // get an iterator of MessageSummary objects
        Iterator msgs = msgTree.getTopLevelMessages( );

        while (msgs.hasNext( )) {
            MessageSummary curMsg = (MessageSummary) msgs.next( );
            Element elem = produceMessageElement(curMsg, msgTree);
            viewMonthElem.addContent(elem);
        }

        return viewMonthElem;
    }

    /**
     * Produce a fragment of XML for an individual message. This
     * is a recursive function.
     */
    private static Element produceMessageElement(MessageSummary msg,
            MessageTree msgTree) {
        Element msgElem = new Element("message");
        msgElem.addAttribute("id", Long.toString(msg.getID( )));
        msgElem.addAttribute("day",
                Integer.toString(msg.getCreateDate().getDay( )));

        msgElem.addContent(new Element("subject")
                .setText(msg.getSubject( )));
        msgElem.addContent(new Element("authorEmail")
                .setText(msg.getAuthorEmail( )));

        Iterator iter = msgTree.getReplies(msg);
        while (iter.hasNext( )) {
            MessageSummary curReply = (MessageSummary) iter.next( );

            // recursively build the XML for all replies
            msgElem.addContent(produceMessageElement(curReply, msgTree));
        }
        return msgElem;
    }

    private ViewMonthJDOM( ) {
    }
}

The recursive method that produces <message> elements is the only difficult code in ViewMonthJDOM. Since <message> elements are nested according to replies, the XML forms a recursive tree structure that could be arbitrarily deep. JDOM supports this nicely, because a JDOM Element can contain other nested Element s. The produceMessageElement( ) method is designed to create the required XML data.

The next JDOM producer class, shown in Example 7-22, is quite simple. It merely creates an XML view of an individual message.

Example 7-22. ViewMessageJDOM.java

package com.oreilly.forum.xml;

import com.oreilly.forum.domain.*;
import java.util.Date;
import org.jdom.*;
import org.jdom.output.*;

/**
 * Generate JDOM for the View Message page.
 */
public class ViewMessageJDOM {

    /**
     * @param message the message to view.
     * @param inResponseTo the message this one is in response to, or
     * perhaps null.
     */
    public static Element produceElement(Message message,
            MessageSummary inResponseTo) {
        Element messageElem = new Element("message");
        messageElem.addAttribute("id", Long.toString(message.getID( )));
        DayMonthYear d = message.getCreateDate( );
        messageElem.addAttribute("month",
                Integer.toString(d.getMonth( )));
        messageElem.addAttribute("day",
                Integer.toString(d.getDay( )));
        messageElem.addAttribute("year",
                Integer.toString(d.getYear( )));

        Element boardElem = BoardSummaryJDOM.produceNameIDElement(
                message.getBoard( ));
        messageElem.addContent(boardElem);

        if (inResponseTo != null) {
            Element inRespToElem = new Element("inResponseTo")
                    .addAttribute("id", Long.toString(inResponseTo.getID( )));
            inRespToElem.addContent(new Element("subject")
                    .setText(inResponseTo.getSubject( )));
            messageElem.addContent(inRespToElem);
        }

        messageElem.addContent(new Element("subject")
                .setText(message.getSubject( )));
        messageElem.addContent(new Element("authorEmail")
                .setText(message.getAuthorEmail( )));
        messageElem.addContent(new Element("text")
                .setText(message.getText( )));
        return messageElem;
    }

    private ViewMessageJDOM( ) {
    }
}

The JDOM producer shown in Example 7-23 is also quite simple. Its job is to create XML for a BoardSummary object. This class is unique because it is not designed to create an entire XML document. Instead, the elements produced by BoardSummaryJDOM are embedded into other XML pages in the application. For example, the home page shows a list of all <board> elements found in the system, each of which is generated by BoardSummaryJDOM. As you design your own systems, you will certainly find common fragments of XML that are reused by several pages. When this occurs, write a common helper class rather than duplicate code.

Example 7-23. BoardSummaryJDOM.java

package com.oreilly.forum.xml;

import com.oreilly.forum.domain.*;
import java.util.*;
import org.jdom.*;

/**
 * Produces JDOM for a BoardSummary object.
 */
public class BoardSummaryJDOM {
    public static Element produceNameIDElement(BoardSummary board) {
        // produce the following:
        // <board id="123">
        //   <name>the board name</name>
        //   <description>board description</description>
        // </board>
        Element boardElem = new Element("board");
        boardElem.addAttribute("id", Long.toString(board.getID( )));
        boardElem.addContent(new Element("name")
                .setText(board.getName( )));
        boardElem.addContent(new Element("description")
                .setText(board.getDescription( )));
        return boardElem;
    }

    public static Element produceElement(BoardSummary board) {
        Element boardElem = produceNameIDElement(board);

        // add the list of messages
        Iterator iter = board.getMonthsWithMessages( );
        while (iter.hasNext( )) {
            MonthYear curMonth = (MonthYear) iter.next( );
            Element elem = new Element("messages");
            elem.addAttribute("month", Integer.toString(curMonth.getMonth( )));
            elem.addAttribute("year", Integer.toString(curMonth.getYear( )));
            boardElem.addContent(elem);
        }

        return boardElem;
    }

    private BoardSummaryJDOM( ) {
    }
}

The final JDOM producer, PostMessageJDOM, is shown in Example 7-24. The produceElement( ) method takes numerous arguments that allow the method to produce XML for posting a new message or replying to an existing message. Also, values for the message subject, author email, and message text may be pre-filled in the XML. The application takes advantage of this capability whenever it must redisplay an HTML form to a user with its values filled in.

Example 7-24. PostMessageJDOM.java

package com.oreilly.forum.xml;

import com.oreilly.forum.domain.*;
import org.jdom.*;

/**
 * Produce JDOM for the "Post Message" page.
 */
public class PostMessageJDOM {

    public static Element produceElement(
            BoardSummary board,
            MessageSummary inResponseToMsg,
            boolean showError,
            String subject,
            String authorEmail,
            String msgText) {
        Element messageElem = new Element("postMsg");

        // reuse the BoardSummaryJDOM class to produce a
        // fragment of the XML
        messageElem.addContent(BoardSummaryJDOM.produceNameIDElement(board));

        if (inResponseToMsg != null) {
            Element inRespTo = new Element("inResponseTo")
                    .addAttribute("id", Long.toString(inResponseToMsg.getID( )));
            inRespTo.addContent(new Element("subject")
                    .setText(inResponseToMsg.getSubject( )));
            messageElem.addContent(inRespTo);
        }

        if (showError) {
            messageElem.addContent(new Element("error")
                    .addAttribute("code", "ALL_FIELDS_REQUIRED"));
        }

        Element prefill = new Element("prefill");
        prefill.addContent(new Element("subject")
                .setText(subject));
        prefill.addContent(new Element("authorEmail")
                .setText(authorEmail));
        prefill.addContent(new Element("message")
                .setText(msgText));
        messageElem.addContent(prefill);

        return messageElem;
    }

    private PostMessageJDOM( ) {
    }
}

Previous | Home | Next
7.2. Prototyping the XML | Book Index | 7.4. Servlet Implementation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Servlet Implementation (Java and XSLT)

 Java and XSLTSearch this book

7.4. Servlet Implementation

We are almost finished! The remaining piece of the puzzle is to coordinate activity between the web browser, database, domain objects, JDOM producers, and XSLT stylesheets. This task lies in the servlet implementation and related classes. In an XSLT-driven web application, the servlet itself really does not do all that much. Instead, it acts as a mediator between all of the other actions taking place in the application.

Figure 7-9 shows the UML class diagram for the com.oreilly.forum.servlet package. This design consists of a few key classes along with numerous subclasses of Renderer and ReqHandler. These subclasses are very repetitive in nature, which is indicative of the highly structured application design that XML and XSLT facilitate.

Figure 7-9. Servlet design

A single-servlet design has been adopted for this application. In this approach, the ForumServlet intercepts all inbound requests from clients. The requests are then delegated to subclasses of ReqHandler, which handle requests for individual pages. Once the request has been processed, a subclass of Renderer selects the XML and XSLT stylesheet. XSLTRenderHelper does the actual XSLT transformation, sending the resulting XHTML back to the browser.

This is not designed to be a heavyweight web application framework. Instead, it is just a simple set of coding conventions and patterns that help keep the application highly modular. It is easy to eliminate the ReqHandler classes and use several servlets instead. The main advantage of explicit request handlers and renderers is that the design is clearly modularized, which may promote more consistency across a team of developers.

The overall flow of control may be the hardest part to understand. Once this flow is clear, the implementation is a matter of creating additional request handlers and renderers. Figure 7-10 is a UML sequence diagram that shows how a single web browser request is intercepted and processed.

Figure 7-10. Sequence diagram

When a browser issues a request, it is always directed to the single servlet. This servlet then locates the appropriate request handler based on information found in the requested URL. The request handler is responsible for interacting with the data adapter layer to create and update domain objects and for creating the appropriate renderer.

Once the renderer is created, the servlet asks it to render( ) its content. The renderer then asks the appropriate JDOM producer to create the XML data and performs the transformation using an XSLT stylesheet. The result of the transformation is sent back to the client browser.

One request handler might map to several renderers. For example, suppose the user is trying to post a new message and submits this information to the PostMsgReqHandler class. If the request handler determines that some required fields are missing, it can return an instance of the PostMsgRenderer class. This allows the user to fill in the remaining fields. On the other hand, if a database error occurs, an instance of ErrorRenderer can be returned. Otherwise, ViewMsgRenderer is returned when the message is successfully posted. Because request handlers and renderers are cleanly separated, renderers can be invoked from any request handler.

The code for ForumServlet is shown in Example 7-25. As already mentioned, this is the only servlet in the application.

Example 7-25. ForumServlet.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.ForumConfig;
import com.oreilly.forum.jdbcimpl.DBUtil;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * The single servlet in the discussion forum.
 */
public class ForumServlet extends HttpServlet {
    private ReqHandlerRegistry registry;

    /**
     * Registers all request handlers and sets up the
     * ForumConfig object.
     */
    public void init(ServletConfig sc) throws ServletException {
        super.init(sc);

        // get initialization parameters from the deployment
        // descriptor (web.xml)
        String jdbcDriverClassName = sc.getInitParameter(
                "jdbcDriverClassName");
        String databaseURL = sc.getInitParameter(
                "databaseURL");
        String adapterClassName = sc.getInitParameter(
                "adapterClassName");
        ForumConfig.setValues(jdbcDriverClassName,
                databaseURL, adapterClassName);

        try {
            // load all request handlers
            this.registry = new ReqHandlerRegistry(new HomeReqHandler( ));
            this.registry.register(new PostMsgReqHandler( ));
            this.registry.register(new ViewMonthReqHandler( ));
            this.registry.register(new ViewMsgReqHandler( ));
        } catch (Exception ex) {
            log(ex.getMessage( ), ex);
            throw new UnavailableException(ex.getMessage( ), 10);
        }
    }

    /**
     * Closes all database connections. This method is invoked
     * when the Servlet is unloaded.
     */
    public void destroy( ) {
        super.destroy( );
        DBUtil.closeAllConnections( );
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {
        ReqHandler rh = this.registry.getHandler(request);
        Renderer rend = rh.doPost(this, request, response);
        rend.render(this, request, response);
    }

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws IOException,
            ServletException {
        ReqHandler rh = this.registry.getHandler(request);
        Renderer rend = rh.doGet(this, request, response);
        rend.render(this, request, response);
    }
}

ForumServlet overrides the init( ) method to perform one-time initialization before any client requests are handled. This is where context initialization parameters are read from the deployment descriptor and stored in the ForumConfig instance:

String jdbcDriverClassName = sc.getInitParameter("jdbcDriverClassName");
String databaseURL = sc.getInitParameter("databaseURL");
String adapterClassName = sc.getInitParameter("adapterClassName");
ForumConfig.setValues(jdbcDriverClassName, databaseURL, adapterClassName);

The init( ) method then sets up instances of each type of request handler. These are registered with the ReqHandlerRegistry class, which has the ability to locate request handlers later on.

In the destroy( ) method, which is called when the servlet is unloaded, any outstanding database connections are closed:

public void destroy( ) {
    super.destroy( );
    DBUtil.closeAllConnections( );
}

While this currently has no real effect, the code was put in place because a future version of the software may use database connection pooling. This allows the application to close all connections in the pool just before exiting.

The only remaining methods in the servlet are doGet( ) and doPost( ), which are virtually identical. All these methods do is locate the appropriate request handler instance, ask the handler to perform a GET or POST, and then use the renderer to send a response to the client.

The code for ReqHandler.java is shown in Example 7-26. This is an abstract class that provides doGet( ) and doPost( ) methods. By default, each method returns an error message back to the client, so a derived class must override one or both methods to enable HTTP GET and/or POST. Once the method is complete, the derived class must return an instance of Renderer, which produces the next page to display.

Example 7-26. ReqHandler.java

package com.oreilly.forum.servlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * All request handlers must extend from this class.
 */
public abstract class ReqHandler {
    protected abstract String getPathInfo( );

    protected Renderer doGet(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        return new ErrorRenderer("GET not allowed");
    }

    protected Renderer doPost(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        return new ErrorRenderer("POST not allowed");
    }
}

The Renderer class is shown in Example 7-27. This class, like ReqHandler, is abstract. Derived classes are responsible for nothing more than producing content to the HttpServletResponse. Basically, each page in the discussion forum application is created using a subclass of Renderer.

Example 7-27. Renderer.java

package com.oreilly.forum.servlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * All page renderers must extend from this class.
 */
public abstract class Renderer {
    public abstract void render(HttpServlet servlet,
            HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException;
}

The most basic renderer is ErrorRenderer, which is shown in Example 7-28. This class displays an error message in a web browser using simple println( ) statements that generate HTML. Unlike all other parts of this application, the ErrorRenderer class does not use XML and XSLT. The reason for this is that a large percentage of errors occurs because an XML parser is not properly configured on the CLASSPATH.[33] If this sort of error occurs, this renderer will not be affected.

[33] CLASSPATH issues are discussed in great detail in Chapter 9, "Development Environment, Testing, and Performance".

NOTE: ErrorRenderer can be written to use XML and XSLT, provided that a try/catch block catches any transformation errors and reverts to println( ) statements for error reporting.

Example 7-28. ErrorRenderer.java

package com.oreilly.forum.servlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * Shows an error page. Since errors are frequently caused by improperly
 * configured JAR files, XML And XSLT are not used by this class.
 * If XML and XSLT were used, then the same CLASSPATH issue that caused
 * the original exception to occur would probably cause this page
 * to fail as well.
 */
public class ErrorRenderer extends Renderer {
    private String message;
    private Throwable throwable;

    public ErrorRenderer(Throwable throwable) {
        this(throwable, throwable.getMessage( ));
    }

    public ErrorRenderer(String message) {
        this(null, message);
    }

    public ErrorRenderer(Throwable throwable, String message) {
        this.throwable = throwable;
        this.message = message;
    }

    public void render(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        response.setContentType("text/html");
        PrintWriter pw = response.getWriter( );
        // just show a simple error page for now.
        pw.println("<html>");
        pw.println("<body>");
        pw.println("<p>");
        pw.println(this.message);
        pw.println("</p>");
        if (this.throwable != null) {
            pw.println("<pre>");
            this.throwable.printStackTrace(pw);
            pw.println("</pre>");
        }
        pw.println("</body></html>");
    }
}

XSLTRenderHelper, shown in Example 7-29, is a utility class used by all remaining renderers. This class does the low-level XSLT transformations, eliminating a lot of duplicated code in each of the renderers. XSLTRenderHelper also maintains a cache of stylesheet filenames so they do not have to be repeatedly located using the ServletContext.getRealPath( ) method.

Example 7-29. XSLTRenderHelper.java

package com.oreilly.forum.servlet;

import com.oreilly.javaxslt.util.StylesheetCache;
import java.io.*;
import java.net.URL;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import org.jdom.*;
import org.jdom.output.*;

/**
 * A helper class that makes rendering of XSLT easier. This
 * eliminates the need to duplicate a lot of code for each
 * of the web pages in this app.
 */
public class XSLTRenderHelper {
    private static Map filenameCache = new HashMap( );

    /**
     * Perform an XSLT transformation.
     *
     * @param servlet provides access to the ServletContext so
     *                the XSLT directory can be determined.
     * @param xmlJDOMData JDOM data for the XML Document.
     * @param xsltBaseName the name of the stylesheet without a directory.
     * @param response the Servlet response to write output to.
     */
    public static void render(HttpServlet servlet, Document xmlJDOMData,
            String xsltBaseName, HttpServletResponse response)
            throws ServletException, IOException {

        String xsltFileName = null;
        try {
            // figure out the complete XSLT stylesheet file name
            synchronized (filenameCache) {
                xsltFileName = (String) filenameCache.get(xsltBaseName);
                if (xsltFileName == null) {
                    ServletContext ctx = servlet.getServletContext( );
                    xsltFileName = ctx.getRealPath(
                            "/WEB-INF/xslt/" + xsltBaseName);
                    filenameCache.put(xsltBaseName, xsltFileName);
                }
            }

            // write the JDOM data to a StringWriter
            StringWriter sw = new StringWriter( );
            XMLOutputter xmlOut = new XMLOutputter("", false, "UTF-8");
            xmlOut.output(xmlJDOMData, sw);

            response.setContentType("text/html");
            Transformer trans = StylesheetCache.newTransformer(xsltFileName);

            // pass a parameter to the XSLT stylesheet
            trans.setParameter("rootDir", "/forum/");

            trans.transform(new StreamSource(new StringReader(sw.toString( ))),
                            new StreamResult(response.getWriter( )));
        } catch (IOException ioe) {
            throw ioe;
        } catch (Exception ex) {
            throw new ServletException(ex);
        }
    }

    private XSLTRenderHelper( ) {
    }
}

XSLTRenderHelper performs the XSLT transformation by first converting the JDOM Document into a String of XML and then reading that String back into a JAXP-compliant XSLT processor. This is not necessarily the most efficient way to integrate JDOM with JAXP, but it works reliably with some beta versions of JDOM. By the time you read this, JDOM will have more standardized APIs for integrating with JAXP.

Another utility class, ReqHandlerRegistry, is shown in Example 7-30. This class is responsible for locating instances of ReqHandler based on path information found in the request URL. Basically, path information is any text that occurs after a slash character (/) following the servlet mapping. HttpServletRequest includes a method called getPathInfo( ) that returns any path information that is present.

Example 7-30. ReqHandlerRegistry.java

package com.oreilly.forum.servlet;

import java.util.*;
import javax.servlet.http.*;

/**
 * A utility class that locates request handler instances based
 * on extra path information.
 */
public class ReqHandlerRegistry {
    private ReqHandler defaultHandler;
    private Map handlerMap = new HashMap( );

    public ReqHandlerRegistry(ReqHandler defaultHandler) {
        this.defaultHandler = defaultHandler;
    }

    public void register(ReqHandler handler) {
        this.handlerMap.put(handler.getPathInfo( ), handler);
    }

    public ReqHandler getHandler(HttpServletRequest request) {
        ReqHandler rh = null;
        String pathInfo = request.getPathInfo( );
        if (pathInfo != null) {
            int firstSlashPos = pathInfo.indexOf('/');
            int secondSlashPos = (firstSlashPos > -1) ?
                    pathInfo.indexOf('/', firstSlashPos+1) : -1;

            String key = null;
            if (firstSlashPos > -1) {
                if (secondSlashPos > -1) {
                    key = pathInfo.substring(firstSlashPos+1, secondSlashPos);
                } else {
                    key = pathInfo.substring(firstSlashPos+1);
                }
            } else {
                key = pathInfo;
            }
            if (key != null && key.length( ) > 0) {
                rh = (ReqHandler) this.handlerMap.get(key);
            }
        }
        return (rh != null) ? rh : this.defaultHandler;
    }
}

Throughout the discussion forum application, URLs take on the following form:

http://hostname:port/forum/main/home

In this URL, forum represents the web application and is the name of the WAR file. The next part of the URL, main, is a mapping to ForumServlet. Since the WAR file and servlet will not change, this part of the URL remains constant. The remaining data, /home, is path information. This is the portion of the URL that ReqHandlerRegistry uses to locate instances of ReqHandler. If the path information is null or does not map to any request handlers, the default request handler is returned. This simply returns the user to the home page.

The first real request handler, HomeReqHandler, is shown in Example 7-31. This class is quite simple and merely returns an instance of HomeRenderer. The code is simple because the home page does not have any modes of operation other than to display all message boards. Other request handlers are more complex because they must process HttpServletRequest parameters.

Example 7-31. HomeReqHandler.java

package com.oreilly.forum.servlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * This is the 'default' request handler in the app. The
 * first inbound request generally goes to an instance
 * of this class, which returns the home page renderer.
 */
public class HomeReqHandler extends ReqHandler {

    protected String getPathInfo( ) {
        return "home";
    }

    protected Renderer doGet(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        return new HomeRenderer( );
    }

}

All of the request handlers must override the getPathInfo( ) method. This determines the path info portion of the URL, so each request handler must return a unique string.

The renderer for the home page, shown in Example 7-32, is also quite simple. As with the home request handler, this renderer is simple because it has only one mode of operation. Like other renderers, this class gets some data from the database using the DataAdapter class, asks a JDOM producer to convert the data into XML, and then tells XSLTRenderHelper which XSLT stylesheet to use when performing the transformation.

Example 7-32. HomeRenderer.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import com.oreilly.forum.xml.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.jdom.*;

/**
 * Shows the home page.
 */
public class HomeRenderer extends Renderer {

    public void render(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        try {
            // get the data for the home page
            DataAdapter adapter = DataAdapter.getInstance( );

            // an iterator of BoardSummary objects
            Iterator boards = adapter.getAllBoards( );

            // convert the data into XML (a JDOM Document)
            Document doc = new Document(HomeJDOM.produceElement(boards));

            // apply the appropriate stylesheet
            XSLTRenderHelper.render(servlet, doc, "home.xslt", response);
        } catch (DataException de) {
            new ErrorRenderer(de).render(servlet, request, response);
        }
    }
}

ViewMonthReqHandler, shown in Example 7-33, is slightly more complex than the home page request handler. Since this request handler requires the board id, month number, and year number as parameters, it must perform validation before it can handle the request properly.

Example 7-33. ViewMonthReqHandler.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * Handle a request to view a month for a message board.
 */
public class ViewMonthReqHandler extends ReqHandler {

    protected String getPathInfo( ) {
        return "viewMonth";
    }

    protected Renderer doGet(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        try {
            DataAdapter adapter = DataAdapter.getInstance( );

            // these are all required parameters
            long boardID = 0L;
            int month = 0;
            int year = 0;
            try {
                boardID = Long.parseLong(request.getParameter("boardID"));
                month = Integer.parseInt(request.getParameter("month"));
                year = Integer.parseInt(request.getParameter("year"));
            } catch (Exception ex) {
                return new ErrorRenderer("Invalid request");
            }
            BoardSummary board = adapter.getBoardSummary(boardID);
            if (board == null) {
                return new ErrorRenderer("Invalid request");
            }

            return new ViewMonthRenderer(board, new MonthYear(month, year));
        } catch (DataException de) {
            return new ErrorRenderer(de);
        }
    }
}

Throughout this application, a seemingly harsh approach to error handling is followed. If any "impossible" requests are detected, the user is presented with a terse error message:

try {
    boardID = Long.parseLong(request.getParameter("boardID"));
    month = Integer.parseInt(request.getParameter("month"));
    year = Integer.parseInt(request.getParameter("year"));
} catch (Exception ex) {
    return new ErrorRenderer("Invalid request");
}

When considering error-handling approaches, the primary concern should be break-in attempts by hackers. It is far too easy for a user to determine which parameters are passed to a web application and then try to wreak havoc by manually keying in various permutations of those parameters. By checking for illegal parameters and simply rejecting them as invalid, a web application gains a big security advantage.

Web Application Security

In the ViewMonthRegHandler class, a NumberFormatException is thrown if any of these parameters are nonnumeric or null. Basically, there are only two possible causes for this sort of error. First, one of the XSLT stylesheets may have a bug, making it forget to pass one of these required parameters. If this is the case, a developer should theoretically catch this error during development and testing. The second possibility is that someone is manually keying in parameters without using the standard XHTML user interface. This could be a hacker attacking the site by probing for an application error, so we simply deny the request.

Standalone GUI applications do not have to contend with such issues because the user interface can prevent illegal user input. But web applications are essentially wide open for the entire world to see, so developers must adopt a highly defensive style of programming. If suppressing hack attempts is not a priority, the code could simply redirect the user to the home page when an illegal request occurs. It might be a good idea to write a log file entry that contains the requesting user's IP address and any other relevant information when errors occur. Log entries can be very useful when diagnosing application bugs as well.

ViewMonthRenderer is shown in Example 7-34. This is another simple class that displays an entire month's worth of messages in a given board. Although the XHTML display can be quite complex for this page, the JDOM producer and XSLT stylesheet handle the real work, keeping the Java code to a minimum.

Example 7-34. ViewMonthRenderer.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import com.oreilly.forum.xml.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.jdom.*;

/**
 * Renders a page that shows all messages in a given month.
 */
public class ViewMonthRenderer extends Renderer {

    private BoardSummary board;
    private MonthYear month;

    public ViewMonthRenderer(BoardSummary board, MonthYear month) {
        this.board = board;
        this.month = month;
    }

    public void render(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        try {
            // convert the data into XML (a JDOM Document)
            Document doc = new Document(ViewMonthJDOM.produceElement(
                    this.board, this.month));

            // apply the appropriate stylesheet
            XSLTRenderHelper.render(servlet, doc,
                    "viewMonth.xslt", response);
        } catch (DataException de) {
            throw new ServletException(de);
        }
    }
}

ViewMsgReqHandler, shown in Example 7-35, requires a parameter named msgID. As before, if this parameter is invalid, an error page is displayed to the user. Otherwise, an instance of ViewMsgRenderer is returned to the servlet.

Example 7-35. ViewMsgReqHandler.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * Handle a request to view a message.
 */
public class ViewMsgReqHandler extends ReqHandler {

    protected String getPathInfo( ) {
        return "viewMsg";
    }

    protected Renderer doGet(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        try {
            DataAdapter adapter = DataAdapter.getInstance( );

            // msgID is a required parameter and must be valid
            String msgIDStr = request.getParameter("msgID");

            if (msgIDStr == null) {
                servlet.log("Required parameter 'msgID' was missing");
                return new ErrorRenderer("Invalid request");
            }

            Message msg = adapter.getMessage(Long.parseLong(msgIDStr));
            MessageSummary inResponseTo = null;
            if (msg.getInReplyTo( ) > -1) {
                inResponseTo = adapter.getMessage(msg.getInReplyTo( ));
            }
            return new ViewMsgRenderer(msg, inResponseTo);
        } catch (NumberFormatException nfe) {
            servlet.log("'msgID' parameter was not a number");
            return new ErrorRenderer("Invalid request");
        } catch (DataException de) {
            return new ErrorRenderer(de);
        }
    }
}

The corresponding renderer, ViewMsgRenderer, is shown in Example 7-36. This class follows the same basic approach as other renderers: it produces a JDOM Document and uses XSLTRenderHelper to perform the XSLT transformation.

Example 7-36. ViewMsgRenderer.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.*;
import com.oreilly.forum.domain.*;
import com.oreilly.forum.xml.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.jdom.*;

/**
 * Show the "view message" page.
 */
public class ViewMsgRenderer extends Renderer {

    private Message message;
    private MessageSummary inResponseTo;

    public ViewMsgRenderer(Message message, MessageSummary inResponseTo) {
        this.message = message;
        this.inResponseTo = inResponseTo;
    }

    public void render(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {

        // convert the data into XML (a JDOM Document)
        Document doc = new Document(ViewMessageJDOM.produceElement(
                this.message, this.inResponseTo));

        // apply the appropriate stylesheet
        XSLTRenderHelper.render(servlet, doc, "viewMsg.xslt", response);
    }
}

The next class, PostMsgReqHandler, is shown in Example 7-37. In the doGet( ) method, the mode parameter indicates whether the user is trying to post a new message or reply to an existing message. The doGet( ) method is invoked as a result of an HTTP GET request, such as the user clicking on a hyperlink or typing in a URL.

Example 7-37. PostMsgReqHandler.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * Handles GET and POST requests for the page that allows users
 * to post or reply to a message.
 */
public class PostMsgReqHandler extends ReqHandler {

    protected String getPathInfo( ) {
        return "postMsg";
    }

    /**
     * When an HTTP GET is issued, show the web page for the
     * first time.
     */
    protected Renderer doGet(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        try {
            // mode must be "postNewMsg" or "replyToMsg"
            String mode = request.getParameter("mode");

            DataAdapter adapter = DataAdapter.getInstance( );
            if ("replyToMsg".equals(mode)) {
                long origMsgID = Long.parseLong(
                        request.getParameter("origMsgID"));
                Message inResponseToMsg = adapter.getMessage(origMsgID);
                if (inResponseToMsg != null) {
                    return new PostMsgRenderer(inResponseToMsg);
                }
            } else if ("postNewMsg".equals(mode)) {
                long boardID = Long.parseLong(
                        request.getParameter("boardID"));
                BoardSummary board = adapter.getBoardSummary(boardID);
                if (board != null) {
                    return new PostMsgRenderer(board);
                }
            }

            return new ErrorRenderer("Invalid request");
        } catch (NumberFormatException nfe) {
            return new ErrorRenderer(nfe);
        } catch (DataException de) {
            return new ErrorRenderer(de);
        }
    }

    /**
     * Handles HTTP POST requests, indicating that the user has
     * filled in the form and pressed the Submit button.
     */
    protected Renderer doPost(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {

        // if the user hit the Cancel button, return to the home page
        if (request.getParameter("cancelBtn") != null) {
            return new HomeRenderer( );
        }

        // lots of error checking follows...
        if (request.getParameter("submitBtn") == null) {
            servlet.log("Expected 'submitBtn' parameter to be present");
            return new ErrorRenderer("Invalid request");
        }

        // a null parameter indicates either a hack attempt, or a
        // syntax error in the HTML
        String mode = request.getParameter("mode");
        String msgSubject = request.getParameter("msgSubject");
        String authorEmail = request.getParameter("authorEmail");
        String msgText = request.getParameter("msgText");
        if (mode == null || msgSubject == null || authorEmail == null
                || msgText == null) {
            return new ErrorRenderer("Invalid request");
        }
        // one of these may be null
        String origMsgIDStr = request.getParameter("origMsgID");
        String boardIDStr = request.getParameter("boardID");
        if (origMsgIDStr == null && boardIDStr == null) {
            return new ErrorRenderer("Invalid request");
        }

        long origMsgID = 0;
        long boardID = 0;
        try {
            origMsgID = (origMsgIDStr != null) ? Long.parseLong(origMsgIDStr) : 0;
            boardID = (boardIDStr != null) ? Long.parseLong(boardIDStr) : 0;
        } catch (NumberFormatException nfe) {
            return new ErrorRenderer("Invalid request");
        }

        // remove extra whitespace then verify that the user filled
        // in all required fields
        msgSubject = msgSubject.trim( );
        authorEmail = authorEmail.trim( );
        msgText = msgText.trim( );

        try {
            DataAdapter adapter = DataAdapter.getInstance( );
            if (msgSubject.length( ) == 0
                    || authorEmail.length( ) == 0
                    || msgText.length( ) == 0) {
                BoardSummary board = (boardIDStr == null) ? null
                        : adapter.getBoardSummary(boardID);
                MessageSummary inResponseToMsg = (origMsgIDStr == null) ? null
                        : adapter.getMessage(origMsgID);

                return new PostMsgRenderer(board, inResponseToMsg,
                        true, msgSubject, authorEmail, msgText);
            }

            //
            // If this point is reached, no errors were detected so the
            // new message can be posted, or a response can be created
            //
            Message msg = null;
            if ("replyToMsg".equals(mode)) {
                msg = adapter.replyToMessage(origMsgID, msgSubject,
                        authorEmail, msgText);
            } else if ("postNewMsg".equals(mode)) {
                msg = adapter.postNewMessage(boardID, msgSubject,
                        authorEmail, msgText);
            }

            if (msg != null) {
                MessageSummary inResponseTo = null;
                if (msg.getInReplyTo( ) > -1) {
                    inResponseTo = adapter.getMessage(msg.getInReplyTo( ));
                }
                return new ViewMsgRenderer(msg, inResponseTo);
            }
            return new ErrorRenderer("Invalid request");
        } catch (DataException dex) {
            return new ErrorRenderer(dex);
        }
    }
}

Unlike other request handlers in this application, PostMsgReqHandler also has a doPost( ) method. The doGet( ) method is responsible for returning a renderer that displays the XHTML form, while the doPost( ) method is responsible for processing the form submission. Because the XHTML form contains several required fields and buttons, the doPost( ) method is far more complex than doGet( ). As the code reveals, almost all of this complexity is introduced because of error checking and validation logic.

The doPost( ) method checks for illegal/impossible parameters first, returning an error page if any problems occur. Next, it checks to see what the user typed in. If the user left a required field blank, the parameter value will be an empty string rather than null. Of course, leading and trailing spaces must be trimmed in case the user hit the space bar:

msgSubject = msgSubject.trim( );
authorEmail = authorEmail.trim( );
msgText = msgText.trim( );

If any of these fields are empty, the PostMsgRenderer is returned with form field values pre-filled:

return new PostMsgRenderer(board, inResponseToMsg,
        true, msgSubject, authorEmail, msgText);

This gives the user an opportunity to fill in missing values and try to submit the form again. If all is well, an instance of ViewMsgRenderer is returned. This allows the user to view the message that was just submitted.

The source code for PostMsgRenderer is shown in Example 7-38.

Example 7-38. PostMsgRenderer.java

package com.oreilly.forum.servlet;

import com.oreilly.forum.*;
import com.oreilly.forum.domain.*;
import com.oreilly.forum.xml.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.jdom.*;

/**
 * Show the web page that allows a user to post or reply to
 * a message.
 */
public class PostMsgRenderer extends Renderer {
    private MessageSummary inResponseToMsg;
    private BoardSummary board;
    private String msgSubject;
    private String authorEmail;
    private String msgText;
    private boolean showError;

    /**
     * This constructor indicates that the user is replying to an
     * existing message.
     */
    public PostMsgRenderer(Message inResponseToMsg) {
        this.board = inResponseToMsg.getBoard( );
        this.inResponseToMsg = inResponseToMsg;
        this.showError = false;
        this.msgSubject = "Re: " + inResponseToMsg.getSubject( );
        this.authorEmail = "";

        StringTokenizer st = new StringTokenizer(
                inResponseToMsg.getText( ), "\n");
        StringBuffer buf = new StringBuffer( );
        buf.append("\n");
        buf.append("\n> -----Original Message-----");
        buf.append("\n>   Posted by ");
        buf.append(inResponseToMsg.getAuthorEmail( ));
        buf.append(" on ");
        buf.append(inResponseToMsg.getCreateDate().toString( ));
        buf.append("\n");
        while (st.hasMoreTokens( )) {
            String curLine = st.nextToken( );
            buf.append("> ");
            buf.append(curLine);
            buf.append("\n");
        }
        buf.append("> ");
        this.msgText = buf.toString( );
    }

    /**
     * This constructor indicates that the user is posting
     * a new message.
     */
    public PostMsgRenderer(BoardSummary board) {
        this(board, null, false, "", "", "");
    }

    /**
     * This constructor is used when the user submitted a form
     * but did not fill out all required fields.
     */
    public PostMsgRenderer(BoardSummary board,
            MessageSummary inResponseToMsg,
            boolean showError,
            String msgSubject,
            String authorEmail,
            String msgText) {
        this.board = board;
        this.inResponseToMsg = inResponseToMsg;
        this.showError = showError;
        this.msgSubject = msgSubject;
        this.authorEmail = authorEmail;
        this.msgText = msgText;
    }

    public void render(HttpServlet servlet, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {

        // convert the data into XML (a JDOM Document)
        Document doc = new Document(PostMessageJDOM.produceElement(
                this.board,
                this.inResponseToMsg,
                this.showError,
                this.msgSubject,
                this.authorEmail,
                this.msgText));

        // apply the appropriate stylesheet
        XSLTRenderHelper.render(servlet, doc, "postMsg.xslt", response);
    }
}

As the code shows, this class has several constructors that support different modes of operation. The first constructor does the most work, prefixing the original message with > characters as many email clients do when creating replies to existing messages. Other than having several constructors, however, the renderer works just like other renderers in the application. The JDOM producer and XSLT stylesheet actually do most of the work, distinguishing between the various modes of operation.

Previous | Home | Next
7.3. Making the XML Dynamic | Book Index | 7.5. Finishing Touches
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Finishing Touches (Java and XSLT)

 Java and XSLTSearch this book

7.5. Finishing Touches

That about does it for the code walkthrough. Since this is a moderately large application, downloading the code from this book's web site is much easier than typing everything in by hand. Do not forget that several additional classes are listed in Appendix B, "JAXP API Reference".

7.5.1. Deployment

A deployment descriptor and WAR file are required to deploy and test the application. The deployment descriptor, web.xml, is shown in Example 7-39.

Example 7-39. Deployment descriptor

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
  <servlet>
    <servlet-name>forumServlet</servlet-name>
    <servlet-class>com.oreilly.forum.servlet.ForumServlet</servlet-class>
    <init-param>
      <param-name>jdbcDriverClassName</param-name>
      <!-- MySQL version is commented out:
      <param-value>org.gjt.mm.mysql.Driver</param-value>
      -->
      <param-value>sun.jdbc.odbc.JdbcOdbcDriver</param-value>
    </init-param>
    <init-param>
      <param-name>databaseURL</param-name>
      <!-- MySQL version is commented out:
      <param-value>jdbc:mysql://localhost:3306/forum</param-value>
      -->
      <param-value>jdbc:odbc:forum</param-value>
    </init-param>
    <init-param>
      <param-name>adapterClassName</param-name>
      <!-- Relational database version is commented out:
      <param-value>com.oreilly.forum.jdbcimpl.JdbcDataAdapter</param-value>
      -->
      <param-value>com.oreilly.forum.fakeimpl.FakeDataAdapter</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>forumServlet</servlet-name>
    <url-pattern>/main/*</url-pattern>
  </servlet-mapping>
</web-app>

The deployment descriptor contains context initialization parameters for the data adapter layer. The default settings utilize a "fake" data adapter, allowing the discussion forum to function without creating any sort of database. Once this is up and running, you will want to create a relational database and configure the appropriate parameter values as shown in web.xml.

7.5.2. Ideas for Enhancements

A few key features were omitted to keep this chapter reasonably sized (as you can see, this is already far longer than any other chapter in the book). Some ideas for enhancements include:

  • Database connection pooling

  • Web-based administration tools

  • Authentication of users

  • The ability to search the entire archive

  • Alternate client user interfaces, such as XHTML Basic or WML

Any one of these features can be added without fundamentally changing the existing architecture. User authentication is probably the biggest change, because new database tables may be required to associate messages with users. For web-based administration tools, additional request handlers and renderers need to be written. These tools also need to be integrated with the security and authentication mechanism; otherwise, any user can run the administrative tools.

Searching is beyond the abilities of XML and XSLT and is best handled by a dedicated search engine technology. This could be as simple as embedding a few lines of HTML into each page that links to a search engine such as Google.[34] Another approach is to write custom search code that integrates more directly with the underlying database. Finally, the whole issue of supporting alternate client user interfaces will be discussed in the next chapter. In a nutshell, this will involve detecting the client browser type and selecting an appropriate XSLT stylesheet.

[34] Even though all pages are generated dynamically, many web crawlers such as Google index every page in the application.

Previous | Home | Next
7.4. Servlet Implementation | Book Index | 8. Additional Techniques
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Additional Techniques (Java and XSLT)

 Java and XSLTSearch this book

Chapter 8. Additional Techniques

Contents:

XSLT Page Layout Templates
Session Tracking Without Cookies
Identifying the Browser
Servlet Filters
XSLT as a Code Generator
Internationalization with XSLT

This chapter presents solutions to a few commonly encountered problems that were not covered in previous chapters, such as implementing session tracking without browser cookies, detecting the browser type, and using XSLT as a rudimentary code generator. None of these techniques are remarkably difficult to implement or use. However, they all build upon the technologies presented throughout this book and are important for programmers to understand. The chapter concludes with advice for internationalization using XSLT and Java.

8.1. XSLT Page Layout Templates

In many cases, dynamically generated, highly interactive web applications are overkill. A small company may need only a static web site that displays job openings, new product announcements, and other basic information. Corporate intranets present another common scenario. In a typical intranet, a large number of departments and individual project teams may be responsible for various web sites within the corporation. Many of these groups are composed of nonprogrammers who can create basic XHTML pages but are not technical enough to write XML, XSLT, and servlets. In either scenario, consistent look and feel are essential.

XSLT is very effective for defining consistent page layout. In the approach outlined here, web page authors create XHTML pages using whatever tools they are familiar with. These pages should not use frames or include navigation areas. As Figure 8-1 shows, an XSLT stylesheet is used to insert navigation areas on the top and left sides of input XHTML pages. This is why individual pages should not attempt to insert their own navigation areas.

Figure 8-1. XSLT template layout

Since the top navigation area is dynamic, page authors must also include a <meta> tag in every XHTML page that is published:

<meta name="navigationCategory" content="home"/>

This tag allows the top navigation area to visually highlight the category that the current page belongs to.[35] The XSLT stylesheet selects this tag and generates the appropriate XHTML for the navigation area. As shown in Figure 8-2, the sample stylesheet uses hyperlinks for each of the navigation categories. This same approach also works for fancy graphical navigation areas.

[35] You can extend this technique by adding a second <meta> tag for subcategories.

Since a single stylesheet controls page layout, changes to this stylesheet are visible across the entire web site. The code for the home page is listed in Example 8-1. The required elements are emphasized.

Example 8-1. home.xml

<?xml version="1.0" encoding="UTF-8"?>
<html>
  <head>
    <title>Home Page</title>
    <meta name="navigationCategory" content="home"/>
  </head>
  <body>
    <h1>Welcome to the Home Page!</h1>
    <div>
      This is a normal XHTML page that authors
      create. The guidelines are as follows:
      <ul>
        <li>Each page must be valid XHTML</li>
        <li>Each page must have a meta tag that
            indicates the navigation category.</li>
        <li>The templatePage.xslt stylesheet will add
            the top and side navigation bars.</li>
      </ul>
      Pages are published to the WEB-INF/xml directory
      of a web app. This forces clients to access pages
      through a Servlet, because the Servlet container
      prevents direct access to anything under WEB-INF.
    </div>
  </body>
</html>

Figure 8-2. XHTML output with navigation areas

Since XSLT is used to insert the appropriate navigation areas, all pages must be well-formed XML. This is a good practice, and anyone who knows HTML should be able to make the transition to XHTML.[36] Programmers can provide scripts for page authors to run that validate the XML against one of the XHTML DTDs, reporting errors before pages are published to the web site.

[36] HTML TIDY is a free tool that converts HTML to XHTML. It is available at http://www.w3.org/People/Raggett/tidy.

NOTE: Strictly adhering to XHTML DTDs makes it much easier for programmers to write all sorts of programs that manage web site content because page content is consistently structured and can be easily parsed.

The XSLT stylesheet searches for the <meta> tag; therefore, <html>, <head>, and <meta> are required elements. If the <meta> tag is not found, the navigation category defaults to "unknown," and none of the navigation links are highlighted. Any content found inside of <head> and <body> is simply copied to the appropriate location within the result tree document. Example 8-2 lists the XSLT stylesheet that inserts the navigation areas.

Example 8-2. templatePage.xslt

<?xml version="1.0" encoding="UTF-8"?>
<!--
*********************************************************************
** A stylesheet used by every page on a web site. This stylesheet
** defines where the page header and navigation bar are placed.
******************************************************************-->
<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!--
  *******************************************************************
  ** The result tree is XHTML
  ****************************************************************-->
  <xsl:output method="xml"
     doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
     doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
     encoding="UTF-8"/>

  <!--
  *******************************************************************
  ** The navigation category is determined by the <meta> tag in the
  ** source XHTML document. The top navigation bar uses this variable.
  ****************************************************************-->
  <xsl:variable name="global.nav.category">
    <xsl:choose>
      <xsl:when test="/html/head/meta[@name='navigationCategory']">
        <xsl:value-of select="/html/head/meta
                [@name='navigationCategory']/@content"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>unknown</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!--
  *******************************************************************
  ** This template produces the XHTML document.
  ****************************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <!-- copy the <head> from the source document -->
      <xsl:copy-of select="html/head"/>

      <body>
        <!-- this table defines the overall layout of the page -->
        <table width="100%" cellpadding="4"
               cellspacing="0" border="0">
          <tr bgcolor="#f0f0f0">
            <td colspan="2">
              <xsl:call-template name="createTopNavbar"/>
            </td>
          </tr>
          <tr valign="top">
            <td bgcolor="#cccccc" width="150px">
               <xsl:call-template name="createLeftNavbar"/>
            </td>
            <td bgcolor="white">
              <!--
              *******************************************************
              ** Copy all contents of the <body> from the source
              ** XHTML document to the result tree XHTML document.
              ****************************************************-->
              <xsl:copy-of select="html/body/* | html/body/text( )"/>
            </td>
          </tr>
        </table>
      </body>
    </html>
  </xsl:template>

  <!--
  *******************************************************************
  ** This template produces the top navigation bar.
  ****************************************************************-->
  <xsl:template name="createTopNavbar">
    <xsl:call-template name="navButton">
      <xsl:with-param name="category" select="'home'"/>
      <xsl:with-param name="displayName" select="'Home'"/>
      <xsl:with-param name="url" select="'home.xml'"/>
    </xsl:call-template>
    |
    <xsl:call-template name="navButton">
      <xsl:with-param name="category" select="'company'"/>
      <xsl:with-param name="displayName" select="'Company'"/>
      <xsl:with-param name="url" select="'company.xml'"/>
    </xsl:call-template>
    |
    <xsl:call-template name="navButton">
      <xsl:with-param name="category" select="'products'"/>
      <xsl:with-param name="displayName" select="'Products'"/>
      <xsl:with-param name="url" select="'products.xml'"/>
    </xsl:call-template>
    |
    <xsl:call-template name="navButton">
      <xsl:with-param name="category" select="'jobs'"/>
      <xsl:with-param name="displayName" select="'Jobs'"/>
      <xsl:with-param name="url" select="'jobs.xml'"/>
    </xsl:call-template>
  </xsl:template>

  <!--
  *******************************************************************
  ** This template produces a "button" in the top navigation bar.
  ****************************************************************-->
  <xsl:template name="navButton">
    <xsl:param name="category"/>
    <xsl:param name="displayName"/>
    <xsl:param name="url"/>
    <xsl:choose>
      <!-- The current category is displayed as text -->
      <xsl:when test="$category = $global.nav.category">
        <xsl:value-of select="$displayName"/>
      </xsl:when>

      <!-- All other categories are displayed as hyperlinks -->
      <xsl:otherwise>
        <a href="{$url}">
          <xsl:value-of select="$displayName"/>
        </a>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!--
  *******************************************************************
  ** This template creates the left navigation area.
  ****************************************************************-->
  <xsl:template name="createLeftNavbar">
    Left Navigation Area
  </xsl:template>
</xsl:stylesheet>

This stylesheet is quite simple in concept. First, it sets up the global.nav.category variable. The stylesheet uses XPath to check for the existence of a <meta> tag that contains a navigationCategory attribute:

<xsl:variable name="global.nav.category">
  <xsl:choose>
    <xsl:when test="/html/head/meta[@name='navigationCategory']">
      <xsl:value-of select="/html/head/meta
              [@name='navigationCategory']/@content"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>unknown</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

The first part of the XPath expression used by <xsl:when> locates any <meta> tags:

/html/head/meta

Next, a predicate is used to narrow down the list to the one <meta> tag that contains a navigationCategory attribute:

[@name='navigationCategory']

If this is found, the value of the content attribute is assigned to the global.nav.category variable. Otherwise, the value is unknown.

The XSLT stylesheet then contains a template that matches the / pattern. This template defines the overall XHTML page layout by creating a <table>. The document <head>, however, is simply copied from the input XHTML document:

<xsl:copy-of select="html/head"/>

Because the original <head> is merely copied to the result tree, any styles or scripts that page authors include in their own documents are preserved. The only drawback occurs when people define CSS styles that change the look and feel of the navigation areas, such as changing the fonts and colors used in a page. If this is a concern, you might want to include logic in the XSLT stylesheet that ignores all <style> tags and style attributes in the original XHTML document.

Once the <head> is copied, the XSLT stylesheet creates the <body> for the result tree. An XHTML <table> controls the overall page layout, and named XSLT templates are used to create the navigation areas:

<xsl:call-template name="createTopNavbar"/>
...
<xsl:call-template name="createLeftNavbar"/>

The createTopNavbar template is somewhat more complicated because it contains logic to display the current category differently. The createLeftNavbar template, on the other hand, simply copies some static content to the result. Finally, the contents of the <body> tag are copied from the original document to the result tree:

<xsl:copy-of select="html/body/* | html/body/text( )"/>

Unlike the <head>, the <body> is not copied directly. Instead, all elements and text within the <body> are copied. This prevents the following invalid XHTML from being produced:

<tr><td><body>...</body></td></tr>

The createTopNavbar named template is used to create the row of links in the top navigation area. For each navigation category, it calls the navButton template:

<xsl:call-template name="navButton">
  <xsl:with-param name="category" select="'home'"/>
  <xsl:with-param name="displayName" select="'Home'"/>
  <xsl:with-param name="url" select="'home.xml'"/>
</xsl:call-template>

The category parameter allows the navButton template to determine if the displayName parameter should be displayed as a hyperlink or text. The code to do this is emphasized in the navButton template (in Example 8-2) and is not repeated here.

None of this works without a servlet driving the process. In this example, all XHTML pages are stored in the web application's WEB-INF directory and saved with .xml filename extensions. Remember that these are the original web pages and do not contain any navigation areas. They are stored in the WEB-INF directory to ensure that clients cannot access them directly. Instead, clients must use a servlet called TemplateServlet to request all pages. This servlet locates the XML file, performs the XSLT transformation using templatePage.xslt, and sends the result tree back to the client browser. The entire process is transparent to clients because they see only the results of the transformation.

Table 8-1 shows the complete structure of the WAR file that supports this example.

Table 8-1. WAR file contents

File | Description
WEB-INF/web.xml | The deployment descriptor (see Example 8-3)
WEB-INF/classes/chap8/TemplateServlet.class | The servlet that drives the XSLT transformation (see Example 8-4)
WEB-INF/lib/javaxslt.jar | Contains the StylesheetCache class
WEB-INF/xml/company.xml | An example web page
WEB-INF/xml/home.xml | An example web page (see Example 8-1)
WEB-INF/xml/jobs.xml | An example web page
WEB-INF/xml/products.xml | An example web page
WEB-INF/xslt/templatePage.xslt | The XSLT stylesheet (see Example 8-2)

The deployment descriptor, web.xml , is shown in Example 8-3.

Example 8-3. Deployment descriptor

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
  <servlet>
    <servlet-name>template</servlet-name>
    <servlet-class>chap8.TemplateServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>template</servlet-name>
    <url-pattern>/template/*</url-pattern>
  </servlet-mapping>
</web-app>

Since all files are protected under the WEB-INF directory, the /template/* URL pattern specified in the deployment descriptor is the only way for clients to access this application. The URL users type into their browser is: http://localhost:8080/chap8/template/home.xml.

This displays the page shown earlier in Figure 8-2. In this URL, the word template maps to the servlet, and /home.xml is the path information. This is retrieved by the servlet using the getPathInfo( ) method of HttpServletRequest. The source code for TemplateServlet is shown in Example 8-4.

Example 8-4. TemplateServlet.java

package chap8;

import com.oreilly.javaxslt.util.StylesheetCache;
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * Applies a standard stylesheet to every XML page on a site.
 */
public class TemplateServlet extends HttpServlet {
    private String xsltFileName;

    /**
     * Locate the template stylesheet during servlet initialization.
     */
    public void init( ) throws UnavailableException {
        ServletContext ctx = getServletContext( );
        this.xsltFileName = ctx.getRealPath(
                    "/WEB-INF/xslt/templatePage.xslt");
        File f = new File(this.xsltFileName);

        if (!f.exists( )) {
            throw new UnavailableException(
                    "Unable to locate XSLT stylesheet: "
                    + this.xsltFileName, 30);
        }
    }

    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        try {
            // use the ServletContext to locate the XML file
            ServletContext ctx = getServletContext( );
            String xmlFileName = ctx.getRealPath("/WEB-INF/xml"
                    + req.getPathInfo( ));

            // verify that the file exists
            if (!new File(xmlFileName).exists( )) {
                res.sendError(HttpServletResponse.SC_NOT_FOUND, xmlFileName);
            } else {
                res.setContentType("text/html");

                // load the XML file
                Source xmlSource = new StreamSource(new BufferedReader(
                        new FileReader(xmlFileName)));

                // use a cached version of the XSLT
                Transformer trans =
                        StylesheetCache.newTransformer(xsltFileName);
                trans.transform(xmlSource, new StreamResult(res.getWriter( )));
            }
        } catch (TransformerConfigurationException tce) {
            throw new ServletException(tce);
        } catch (TransformerException te) {
            throw new ServletException(te);
        }
    }
}

This is a fairly basic servlet whose sole purpose is to locate XML files and perform XSLT transformations. The init( ) method is used to locate templatePage.xslt from the WEB-INF/xslt directory:

ServletContext ctx = getServletContext( );
this.xsltFileName = ctx.getRealPath(
            "/WEB-INF/xslt/templatePage.xslt");

As discussed in earlier chapters, the getRealPath( ) method converts the path into a system-specific pathname. This allows the StylesheetCache class to locate the XSLT stylesheet properly. Later, in the doGet( ) method of the servlet, the same method is used to locate the requested XML file:

ServletContext ctx = getServletContext( );
String xmlFileName = ctx.getRealPath("/WEB-INF/xml"
        + req.getPathInfo( ));

As shown back in the source for TemplateServlet, it then checks for the existence of this file and sends an error if necessary. Otherwise, it uses JAXP to perform the XSLT transformation. This is where the navigation areas get added to the document.

More on Caching

In the TemplateServlet class, the XSLT stylesheets are cached using the com.oreilly.javaxslt.util.StylesheetCache class. In this particular example, however, the XML data and XSLT stylesheets are all static files. Because these files are not dynamically generated, it becomes possible to cache the transformation result, yielding the highest possible performance. The next chapter discusses a class called ResultCache that makes this possible.

Using XSLT stylesheets for page layout templates is a useful technique because individual page authors do not have to duplicate headers, footers, and navigation areas into every page they create. By centralizing page layout in one or more standard XSLT stylesheets, fewer changes are required to update the look of an entire web site.

Previous | Home | Next
7.5. Finishing Touches | Book Index | 8.2. Session Tracking Without Cookies
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Session Tracking Without Cookies (Java and XSLT)

 Java and XSLTSearch this book

8.2. Session Tracking Without Cookies

Session tracking is an essential part of most web applications. By nature, the HTTP protocol is connectionless. This means that each time users click on a hyperlink or submit an XHTML form, the browser establishes a new connection to the web server. Once the request is sent and the response is received, the connection between browser and server is broken.

This presents a problem for servlet authors. Although the browser and web server do not maintain a persistent connection between page views, applications must maintain state information for each user. Stateful applications make technologies like shopping carts possible, for instance. With each request from the browser, the servlet must reestablish the identity of the user and locate his session information.

8.2.1. Servlet Session-Tracking API

The traditional servlet approach to session tracking utilizes the javax.servlet.http.HttpSession interface. This interface allows a web application to store information about a user that persists across page requests. The interface is easy to use, mapping attribute names to attribute values. The code shown here is part of a servlet that uses HttpSession:

public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {
    // retrieve an instance of HttpSession for this user. The "true" parameter
    // indicates that the object should be created if it does not exist.
    HttpSession session = req.getSession(true);

    // retrieve the cart for this user
    Cart cart = (Cart) session.getAttribute("shoppingCart");

    if (cart == null) {
        cart = new Cart( );
        session.setAttribute("shoppingCart", cart);
    }

    ...
}

The first line of the doGet( ) method locates the HttpSession instance associated with the current user. The true parameter indicates that a new session should be created if one does not already exist. Once the session is created, a Cart object can be retrieved using HttpSession's getAttribute( ) method.

Browser cookies provide the standard method of implementing HttpSession. A cookie is a small piece of information stored on the client machine and generally contains a randomly generated sequence of characters that uniquely identifies a user. When the browser issues a request to the servlet, the servlet looks for a cookie named jsessionid and uses its value to locate an instance of HttpSession. Figure 8-3 illustrates the normal session-tracking model.

Figure 8-3. Session tracking

Cookies are a mixed blessing. Although they make session tracking very easy to implement, this leads to security concerns because people do not want their browsing habits monitored. Therefore, quite a few people set their browsers to disable all cookies. When users disable cookies, servlets must use another technique to enable session tracking.

The standard servlet API has a fallback mechanism when cookies are disabled. It reverts to a technique called URL rewriting. If cookies are disabled, the session identifier is appended to the URL. This way, whenever a user clicks on a hyperlink or submits an XHTML form, the session identifier is transmitted along with the request. This cannot happen without some level of programmer intervention, however. Imagine a scenario where a servlet is requested, and it returns an XHTML page with the following content:

Click on the link to move next:
<a href="/shopping/moveNext"/>Move Next</a>

This causes session tracking to fail, because the session identifier is lost whenever the user clicks on the hyperlink. We really want the HTML to look like this:

Click on the link to move next:
<a href="/shopping/moveNext;jsessionid=0e394s8a576f67b38s7"/>Move Next</a>

Now, when the user clicks on the hyperlink, the session identifier (jsessionid) is transmitted to the servlet as part of the requested URL.

The value for jsessionid cannot be hardcoded. It must be dynamically generated for each instance of HttpSession, making it much harder for hackers to obtain session identifiers to impersonate users.[37] This means that the XHTML cannot be entirely static; the session identifier must be dynamically inserted into the XHTML whenever a link or form action is required. HttpServletResponse has a method called encodeURL( ) that makes this possible:

[37] Sessions and their associated identifiers typically expire after 30 minutes of inactivity and must be regenerated.

String originalURL = "/shopping/moveNext";
String encodedURL = response.encodeURL(originalURL);

Now, encodedURL will be encoded with the session id if the jsessionid cookie was not found. For session tracking to work, this technique must be used consistently for every hyperlink and form action on a web site.

8.2.2. Session Tracking with Java and XSLT

With XSLT, session tracking is a little harder because the stylesheet generates the URL rather than the servlet. For instance, a stylesheet might contain the following code:

<xsl:template match="cart">
  Click on the link to move next:
  <a href="/shopping/moveNext"/>Move Next</a>
  ...
</xsl:template>

Like before, the jsessionid needs to be concatenated to the URL. To make this happen, the following steps must be performed:

  1. In the servlet, determine if cookies are enabled or disabled.

  2. If cookies are disabled, get the value of jsessionid.

  3. Pass ;jsessionid=XXXX as a parameter to the XSLT stylesheet, where XXXX is the session identifier.

  4. In the stylesheet, append the session id parameter to all URLs in hyperlinks and form actions.

If cookies are enabled, there is no reason to manually implement session tracking. This is easy to check because the javax.servlet.http.HttpServletRequest interface provides the isRequestedSessionIdFromCookie( ) method. When this method returns true, cookies are enabled, and the remaining steps can be ignored. The code in Example 8-5 shows what a servlet's doGet( ) method looks like when implementing session tracking.

Example 8-5. Session-tracking code

public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {
    try {
        // retrieve current settings from the session
        HttpSession session = req.getSession(true);
        Cart cart = (Cart) session.getAttribute("shoppingCart");

        if (cart == null) {
            cart = new Cart( );
            session.setAttribute("shoppingCart", cart);
        }

        // produce the DOM tree
        Document doc = CartDOMProducer.createDocument(cart);

        // prepare the XSLT transformation
        Transformer trans = StylesheetCache.newTransformer(
                this.xsltFileName);

        // allow cookieless session tracking
        if (!req.isRequestedSessionIdFromCookie( )) {
            String sessionID = session.getId( );
            trans.setParameter("global.sessionID",
                ";jsessionid=" + sessionID);
        }

        // send the web page back to the user
        res.setContentType("text/html");
        trans.transform(new javax.xml.transform.dom.DOMSource(doc),
                new StreamResult(res.getWriter( )));

    } catch (ParserConfigurationException pce) {
        throw new ServletException(pce);
    } catch (TransformerConfigurationException tce) {
        throw new ServletException(tce);
    } catch (TransformerException te) {
        throw new ServletException(te);
    }
}

The critical lines of code are emphasized. The first of these checks to see if the session was not obtained using a cookie:

if (!req.isRequestedSessionIdFromCookie( )) {

For the very first request, the cookie will not be present because the servlet has not had a chance to create it. For all subsequent requests, the cookie will be missing if the user has disabled cookies in the browser. Under either scenario, the session identifier is obtained from the HttpSession instance:

String sessionID = session.getId( );

The servlet API takes care of generating a random session identifier; you are responsible for preserving this identifier by passing it as a parameter to the stylesheet. This is done as follows:

trans.setParameter("global.sessionID",
       ";jsessionid=" + sessionID);

This servlet also takes the liberty of prefixing the session identifier with ";jessionid=". This makes the XSLT stylesheet simpler, because it does not have to check if the session ID is an empty string or not. As implemented here, the value of global.sessionID can be appended to all URLs:

<a href="/whatever{$global.sessionID}">click here</a>

The end result is that if cookies are enabled, the URL will be unaffected. Otherwise, it will be properly encoded to use session tracking. A larger XSLT example follows in Example 8-6.

Example 8-6. Session-tracking XSLT stylesheet

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!--
  *********************************************************************
  ** global.sessionID : Used for URL-rewriting to implement
  **                    session tracking without cookies.
  ******************************************************************-->
  <xsl:param name="global.sessionID"/>

  <!-- This stylesheet produces XHTML -->
  <xsl:output method="xml" indent="yes" encoding="UTF-8"
  doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
  doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>

  <!--
  *********************************************************************
  ** This template produces the skeletal XHTML document.
  ******************************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Shopping Example</title>
      </head>
      <body>
        <!-- Create a form for this page -->
        <form method="post" action="/chap8/shopping{$global.sessionID}">
        <h1>Shopping Example</h1>
          ...remainder of page omitted
        </form>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="cart">
    Click on the link to move next:
    <a href="/shopping/moveNext{$global.sessionID}?param=value"/>Move Next</a>
    ...
  </xsl:template>

</xsl:stylesheet>

This stylesheet fully illustrates the three key components that make session tracking with XSLT possible. First, the session identifier is passed to the stylesheet as a parameter:

<xsl:param name="global.sessionID"/>

Next, this session identifier is used for the form action:

<form method="post" action="/chap8/shopping{$global.sessionID}">

And finally, it is used for all hyperlinks:

<a href="/shopping/moveNext{$global.sessionID}?param=value"/>Move Next</a>

The ?param=value string was added here to illustrate that request parameters are appended after the session identifier. Therefore, the full URL will look similar to the following when the user clicks on the hyperlink:

http://localhost:8080/shopping/moveNext;jsessionid=298ulkj2348734jkj43?param=value

Tracking sessions is essential, and the technique shown in this section works when browser cookies are disabled. You should always test your web applications by disabling all browser cookies to see if every URL is properly encoded with the session identifier.

Previous | Home | Next
8. Additional Techniques | Book Index | 8.3. Identifying the Browser
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Identifying the Browser (Java and XSLT)

 Java and XSLTSearch this book

8.3. Identifying the Browser

A strength of XSLT is its ability to help keep data and presentation separate. As you know, supporting different transformations is a matter of writing different XSLT stylesheets. Figuring out which stylesheet to apply is the only missing piece. For web applications, the User-Agent HTTP header offers the solution.

HTTP requests consist of a header followed by content; the header contains name/value pairs of data, allowing the client and server to exchange additional information with each other. The text shown in Example 8-7 contains the complete HTTP request issued by Netscape 6.0 when running on Windows 2000.

Example 8-7. Netscape 6 HTTP request

GET / HTTP/1.1
Host: localhost:80
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; m18) Gecko/20001108
Netscape6/6.0
Accept: */*
Accept-Language: en
Accept-Encoding: gzip,deflate,compress,identity
Keep-Alive: 300
Connection: keep-alive

For the purposes of browser detection, the value of User-Agent must be parsed to determine what kind of browser is requesting information from the servlet. Based on this information, the servlet can select an appropriate XSLT stylesheet that supports the particular strengths and weaknesses of the browser in question.

Unfortunately, there are hundreds of variations of User-Agent, and browser vendors do not rigorously adhere to any standard format. The common browsers can be identified, however, with a small amount of parsing logic. Table 8-2 lists some of the more common browsers you might encounter.

Table 8-2. Common User-Agent values

User-Agent | Browser
Lynx/2.8rel.3 libwww-FM/2.14 | Lynx 2.8rel3
Mozilla/4.0 (compatible; MSIE 4.0; Windows NT) | Internet Explorer 4.0
Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) | Internet Explorer 5.5
Mozilla/4.08 [en] (WinNT; U ;Nav) | Netscape 4.08
Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; m18) Gecko/20001108 Netscape 6/6.0 | Netscape 6
Mozilla/3.0 (compatible; Opera/3.0; Windows 95/NT4) v3.1 | Opera 3.0

The first browser, Lynx, is listed because it is the most common text-only browser. Whenever the User-Agent begins with Lynx, your web application can select an XSLT stylesheet that omits all graphics from the web page.

The three most popular browsers are clearly Microsoft Internet Explorer, Netscape Navigator, and Opera Software's Opera. Of these three browsers, Navigator was available first, and its User-Agent always begins with Mozilla. In the early days of web development, many sites checked for this and only provided fancy versions of their web sites to Netscape browsers.

When Microsoft Internet Explorer became available, it had to begin its User-Agent string with Mozilla to maintain compatibility with many existing web sites. Therefore, you cannot simply check for Mozilla to determine the browser type. As you can see in Table 8-2, Microsoft browsers include the text MSIE followed by the version number, making them easily identifiable.

A more recent entry, Opera, also begins with Mozilla. The User-Agent for Opera browsers always contains Opera/[version];, where [version] is something like 2.0 or 3.0. With these rules in mind, the algorithm for detecting a browser might look something like this:

if (begins-with "Lynx") {
  browser is only capable of displaying text
}
else if (contains "MSIE") {
  browser is Internet Explorer
}
else if (contains "Opera") {
  browser is Opera
}
else if (begins-with "Mozilla") {
  browser is Netscape-compatible
}
else {
  browser is unknown
}

In a servlet, the following code is used to obtain the value of User-Agent:

protected void doGet(HttpServletRequest req, HttpServletResponse res)
        throws IOException, ServletException {
    String userAgent = req.getHeader("User-Agent");
    String xslt = null;
    if (userAgent.startsWith("Lynx")) {
        xslt = "textHomePage.xslt";
    } else {
        xslt = "htmlHomePage.xslt";
    }
    ...

For more sophisticated applications, it is desirable to use a utility class that can identify the browser, its version number, and possibly even its platform. Although you can certainly write your own class using basic java.lang.String operations, a better option is to use an existing API that someone else has written. The screen capture shown in Figure 8-4 illustrates the output from a simple servlet that identifies various pieces of information about the browser.

Figure 8-4. Browser detection

This servlet utilizes the org.apache.turbine.util.BrowserDetector class, which is part of Apache's Turbine web application framework.[38] This class actually has only one dependency on anything else in Turbine, so you can either comment out its reference to Turbine's RunData class or simply include the Turbine JAR files in your CLASSPATH.[39] Turbine can be obtained from http://jakarta.apache.org. The code for the servlet is shown in Example 8-8.

[38] This example is based on Version 2.1 of Turbine.

[39] Be sure to read and follow the Apache licensing agreement if you extract this class from Turbine.

Example 8-8. BrowserID.java

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.turbine.util.BrowserDetector;

public class BrowserID extends HttpServlet {

    protected void doGet(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        BrowserDetector bd = new BrowserDetector(req.getHeader(
                "User-Agent"));

        res.setContentType("text/plain");

        PrintWriter pw = res.getWriter( );
        pw.println("User-Agent  : " + bd.getUserAgentString( ));
        pw.println("Supports CSS: " + bd.isCssOK( ));
        pw.println("JavaScript  : " + bd.isJavascriptOK( ));
        pw.println("Browser Name: " + bd.getBrowserName( ));
        pw.println("Platform    : " + bd.getBrowserPlatform( ));
        pw.println("Version     : " + bd.getBrowserVersion( ));
    }
}

Previous | Home | Next
8.2. Session Tracking Without Cookies | Book Index | 8.4. Servlet Filters
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Servlet Filters (Java and XSLT)

 Java and XSLTSearch this book

8.4. Servlet Filters

Version 2.3 of the Java servlet specification adds a new feature called filters. A filter is an object that intercepts requests to a servlet, JSP, or static file in a web application. The filter has the opportunity to modify the request before passing it along to the underlying resource and can capture and modify the response before sending it back to the client. Since filters can be specified declaratively using the web application deployment descriptor, they can be inserted into existing web applications without altering any of the existing code.

8.4.1. Filter Overview

Servlet filters are useful for many purposes, including logging, user authentication, data compression, encryption, and XSLT transformation. Many filters can be chained together, each performing one specific task. For the purposes of this book, XSLT transformations are the most interesting use of filters. Figure 8-5 illustrates the general filter architecture.

Figure 8-5. Servlet filters

javax.servlet.Filter is an interface that all custom filters must implement. It defines the following three methods:

void init(FilterConfig config)
void destroy( )
void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)

The init( ) and destroy( ) methods are virtually identical to the init( ) and destroy( ) methods found in any servlet. init( ) is called when the filter is first loaded, and the FilterConfig parameter provides access to the ServletContext and to a list of initialization parameters. The code in Example 8-11 demonstrates each of these features. destroy( ), as expected, is called once when the filter is unloaded. This gives the filter a chance to release resources.

The doFilter( ) method is called whenever a client request is received. The filter participates in a FilterChain set up by the servlet container, which allows multiple filters to be attached to one another. If this filter wishes to block the request, it can simply do nothing. Otherwise, it must pass control to the next resource in the chain:

chain.doFilter(req, res);

Although the next entry in the chain might be another filter, it is probably a servlet or a JSP. Either way, the filter does not have to know this.

Simply calling doFilter(req, res) merely passes control to the next entry in the chain. To modify the request or response, the filter must modify the ServletRequest and/or ServletResponse object. Unfortunately, these are both interfaces, and their implementation classes are specific to each servlet container. Furthermore, the interfaces do not allow values to be modified.

To facilitate this capability, Version 2.3 of the servlet API also adds wrapper classes that allow the request and response to be modified. The following new classes are now available:

  • javax.servlet.ServletRequestWrapper

  • javax.servlet.ServletResponseWrapper

  • javax.servlet.http.HttpServletRequestWrapper

  • javax.servlet.http.HttpServletResponseWrapper

Each of these classes merely wraps around another request or response, and all methods merely delegate to the wrapped object. To modify behavior, programmers must extend from one of these classes and override one or more methods. Here is how a custom filter might look:

public class MyFilter implements Filter {
    public void doFilter (ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        // wrap around the original request and response
        MyRequestWrapper reqWrap = new MyRequestWrapper(req);
        MyResponseWrapper resWrap = new MyResponseWrapper(res);

        // pass the wrappers on to the next entry
        chain.doFilter(reqWrap, resWrap);
    }
}

In this case, MyRequestWrapper and MyResponseWrapper are doing the actual work of modifying the request and response. This works fine for many types of simple filters but is more complex when modifying the response content. To illustrate this point, consider the getOutputStream( ) method in javax.servlet.ServletResponse:

public interface ServletResponse {
    ServletOutputStream getOutputStream( ) throws IOException;
    ...additional methods
}

Here is how javax.servlet.ServletResponseWrapper defines the same method:

public class ServletResponseWrapper implements ServletResponse {
    private ServletResponse response;

    public ServletResponseWrapper(ServletResponse response) {
        this.response = response;
    }

    // default implementation delegates to the wrapped response
    public ServletOutputStream getOutputStream( ) throws IOException {
        return this.response.getOutputStream( );
    }

    ...additional methods behave the same way
}

To modify the response sent to the client browser, the custom wrapper subclass must override the getOutputStream( ) method as follows:

public class MyResponseWrapper extends ServletResponseWrapper {

    public ServletOutputStream getOutputStream( ) throws IOException {
        // cannot return the ServletOutputStream from the superclass, because
        // that object does not allow us to capture its output. Therefore,
        // return a custom subclass of ServletOutputStream:
        return new MyServletOutputStream( );
    }
}

ServletOutputStream is an abstract class and does not provide methods that allow it to be modified. Instead, programmers must create custom subclasses of ServletOutputStream that allow them to capture the output and make any necessary modifications. This is what makes modification of the servlet response so difficult.

8.4.2. XSLT Transformation Filter

The previous discussion introduced a lot of concepts about servlet filters without a lot of details. Next, a complete example for performing XSLT transformations using a filter is presented. Hopefully this will illustrate some of the issues mentioned so far.

The basic goal is to create a servlet filter that performs XSLT transformations. A servlet, JSP, or static XML file will provide the raw XML data. The filter will intercept this XML before it is sent to the client browser and apply an XSLT transformation. The result tree is then sent back to the browser.

Example 8-9 is the first of three classes that comprise this example. This is a custom subclass of ServletOutputStream that captures its output in a byte array buffer. The XML data is queued up in this buffer as a first step before it is transformed.

Example 8-9. BufferedServletOutputStream.java

package com.oreilly.javaxslt.util;

import java.io.*;
import javax.servlet.*;

/**
 * A custom servlet output stream that stores its data in a buffer,
 * rather than sending it directly to the client.
 *
 * @author Eric M. Burke
 */
public class BufferedServletOutputStream extends ServletOutputStream {
    // the actual buffer
    private ByteArrayOutputStream bos = new ByteArrayOutputStream( );

    /**
     * @return the contents of the buffer.
     */
    public byte[] getBuffer( ) {
        return this.bos.toByteArray( );
    }

    /**
     * This method must be defined for custom servlet output streams.
     */
    public void write(int data) {
        this.bos.write(data);
    }

    // BufferedHttpResponseWrapper calls this method
    public void reset( ) {
        this.bos.reset( );
    }

    // BufferedHttpResponseWrapper calls this method
    public void setBufferSize(int size) {
        // no way to resize an existing ByteArrayOutputStream
        this.bos = new ByteArrayOutputStream(size);
    }
}

The BufferedServletOutputStream class extends directly from Servlet-OutputStream. The only abstract method in ServletOutputStream is write( ); therefore, our class must define that method. Instead of writing the data to the client, however, our class writes the data to a ByteArrayOutput-Stream . The remaining methods, reset( ) and setBufferSize( ), are required by the class shown in Example 8-10.

Example 8-10. BufferedHttpResponseWrapper.java

package com.oreilly.javaxslt.util;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * A custom response wrapper that captures all output in a buffer.
 */
public class BufferedHttpResponseWrapper extends HttpServletResponseWrapper {
    private BufferedServletOutputStream bufferedServletOut
            = new BufferedServletOutputStream( );

    private PrintWriter printWriter = null;
    private ServletOutputStream outputStream = null;

    public BufferedHttpResponseWrapper(HttpServletResponse origResponse) {
        super(origResponse);
    }

    public byte[] getBuffer( ) {
        return this.bufferedServletOut.getBuffer( );
    }

    public PrintWriter getWriter( ) throws IOException {
        if (this.outputStream != null) {
            throw new IllegalStateException(
                    "The Servlet API forbids calling getWriter( ) after"
                    + " getOutputStream( ) has been called");
        }

        if (this.printWriter == null) {
            this.printWriter = new PrintWriter(this.bufferedServletOut);
        }
        return this.printWriter;
    }

    public ServletOutputStream getOutputStream( ) throws IOException {
        if (this.printWriter != null) {
            throw new IllegalStateException(
                "The Servlet API forbids calling getOutputStream( ) after"
                + " getWriter( ) has been called");
        }

        if (this.outputStream == null) {
            this.outputStream = this.bufferedServletOut;
        }
        return this.outputStream;
    }

    // override methods that deal with the response buffer

    public void flushBuffer( ) throws IOException {
        if (this.outputStream != null) {
            this.outputStream.flush( );
        } else if (this.printWriter != null) {
            this.printWriter.flush( );
        }
    }

    public int getBufferSize( ) {
        return this.bufferedServletOut.getBuffer( ).length;
    }

    public void reset( ) {
        this.bufferedServletOut.reset( );
    }

    public void resetBuffer( ) {
        this.bufferedServletOut.reset( );
    }

    public void setBufferSize(int size) {
        this.bufferedServletOut.setBufferSize(size);
    }
}

BufferedHttpResponseWrapper is an extension of HttpServlet-ResponseWrapper and overrides all methods that affect the Writer or OutputStream back to the client. This allows us to fully capture and control the response before anything is sent back to the client browser.

According to the servlet API, either getWriter( ) or getOutputStream( ) can be called, but not both. This custom response wrapper class cannot know which is needed, so it must support both. This is definitely an area where the servlet filtering API can make things a lot easier for programmers.

WARNING: Very little of this is currently documented in the servlet specification. Perhaps this will improve by the time this book is published. However, there are currently very few examples that show how to capture and modify the response. Hopefully this will improve as more containers are upgraded to support the servlet 2.3 specification.

The primary class in this example is shown in Example 8-11. This is the actual filter that performs XSLT transformations.

Example 8-11. Servlet filter for XSLT transformations

package com.oreilly.javaxslt.util;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * A utility class that uses the Servlet 2.3 Filtering API to apply
 * an XSLT stylesheet to a servlet response.
 *
 * @author Eric M. Burke
 */
public class StylesheetFilter implements Filter {
    private FilterConfig filterConfig;
    private String xsltFileName;

    /**
     * This method is called once when the filter is first loaded.
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;

        // xsltPath should be something like "/WEB-INF/xslt/a.xslt"
        String xsltPath = filterConfig.getInitParameter("xsltPath");
        if (xsltPath == null) {
            throw new UnavailableException(
                    "xsltPath is a required parameter. Please "
                    + "check the deployment descriptor.");
        }

        // convert the context-relative path to a physical path name
        this.xsltFileName = filterConfig.getServletContext( )
                .getRealPath(xsltPath);

        // verify that the file exists
        if (this.xsltFileName == null ||
                !new File(this.xsltFileName).exists( )) {
            throw new UnavailableException(
                    "Unable to locate stylesheet: " + this.xsltFileName, 30);
        }
    }

    public void doFilter (ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {

        if (!(res instanceof HttpServletResponse)) {
            throw new ServletException("This filter only supports HTTP");
        }

        BufferedHttpResponseWrapper responseWrapper =
                new BufferedHttpResponseWrapper((HttpServletResponse) res);
        chain.doFilter(req, responseWrapper);

        // Tomcat 4.0 reuses instances of its HttpServletResponse
        // implementation class in some scenarios. For instance, hitting
        // reload( ) repeatedly on a web browser will cause this to happen.
        // Unfortunately, when this occurs, output is never written to the
        // BufferedHttpResponseWrapper's OutputStream. This means that the
        // XML output array is empty when this happens. The following
        // code is a workaround:
        byte[] origXML = responseWrapper.getBuffer( );
        if (origXML == null || origXML.length == 0) {
            // just let Tomcat deliver its cached data back to the client
            chain.doFilter(req, res);
            return;
        }

        try {
            // do the XSLT transformation
            Transformer trans = StylesheetCache.newTransformer(
                    this.xsltFileName);

            ByteArrayInputStream origXMLIn = new ByteArrayInputStream(origXML);
            Source xmlSource = new StreamSource(origXMLIn);

            ByteArrayOutputStream resultBuf = new ByteArrayOutputStream( );
            trans.transform(xmlSource, new StreamResult(resultBuf));

            res.setContentLength(resultBuf.size( ));
            res.setContentType("text/html");
            res.getOutputStream().write(resultBuf.toByteArray( ));
            res.flushBuffer( );
        } catch (TransformerException te) {
            throw new ServletException(te);
        }
    }

    /**
     * The counterpart to the init( ) method.
     */
    public void destroy( ) {
        this.filterConfig = null;
    }
}

This filter requires the deployment descriptor to provide the name of the XSLT stylesheet as an initialization parameter. The following line of code retrieves the parameter:

String xsltPath = filterConfig.getInitParameter("xsltPath");

By passing the stylesheet as a parameter, the filter can be configured to work with any XSLT. Since the filter can be applied to a servlet, JSP, or static file, the XML data is also completely configurable.

The doFilter( ) method illustrates another weakness of the current filtering API:

if (!(res instanceof HttpServletResponse)) {
    throw new ServletException("This filter only supports HTTP");
}

Since there is no HTTP-specific filter interface, custom filters must use instanceof and downcasts to ensure that only HTTP requests are filtered.

Next, the filter creates the buffered response wrapper and delegates to the next entry in the chain:

BufferedHttpResponseWrapper responseWrapper =
        new BufferedHttpResponseWrapper((HttpServletResponse) res);
chain.doFilter(req, responseWrapper);

This effectively captures the XML output from the chain, making the XSLT transformation possible. Before doing the transformation, however, one "hack" is required to work with Tomcat 4.0:

byte[] origXML = responseWrapper.getBuffer( );
if (origXML == null || origXML.length == 0) {
    // just let Tomcat deliver its cached data back to the client
    chain.doFilter(req, res);
    return;
}

The complete explanation is captured in the source code comments in Example 8-11. Basically, Tomcat seems to cache its response when the user tries to reload the same static file consecutive times. Without this check, the code fails because the origXML byte array is empty.[40]

[40] This was quite difficult to figure out. Because the servlet specification is not specific on this topic, different servlet containers may behave slightly differently.

Finally, the filter uses JAXP to perform the XSLT transformation, sending the result tree to the original servlet response.

The deployment descriptor is listed in Example 8-12.

Example 8-12. Filter deployment descriptor

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC
  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2.3.dtd">

<web-app>
  <filter>
    <filter-name>xsltFilter</filter-name>
    <filter-class>com.oreilly.javaxslt.util.StylesheetFilter</filter-class>
    <init-param>
      <param-name>xsltPath</param-name>
      <param-value>/WEB-INF/xslt/templatePage.xslt</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>xsltFilter</filter-name>
    <url-pattern>/home.xml</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>xsltFilter</filter-name>
    <url-pattern>/company.xml</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>xsltFilter</filter-name>
    <url-pattern>/jobs.xml</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>xsltFilter</filter-name>
    <url-pattern>/products.xml</url-pattern>
  </filter-mapping>

</web-app>

As the first few lines of the deployment descriptor indicate, filters require Version 2.3 of the web application DTD.

The filter initialization parameter is specified next, inside of the <filter> element. This provides the name of the XSLT stylesheet for this particular filter instance. It is also possible to specify multiple <filter> elements using the same filter class but different filter names. This allows the same web application to utilize a single filter with many different configurations.

Finally, the deployment descriptor lists several explicit mappings for this filter. In the examples shown, the filter is applied to static XML files. It can just as easily be applied to a servlet or JSP, however.

8.4.3. Closing Thoughts on Filters

Using filters for XSLT transformations is an interesting concept, primarily because it allows different stylesheets to be applied to XML from many different sources using the web application deployment descriptor. To use a different stylesheet, merely change the deployment descriptor. One interesting approach is using JSP to generate pure XML, then applying a filter to transform that XML into XHTML for the client.

Filters do suffer drawbacks and probably are not the best solution for most applications. First and foremost, the filter API is available only in Version 2.3 of the servlet specification; many existing servlet containers do not support filters at all. In the case of XSLT transformations, a custom ServletOutputStream must be written to capture the response output, and downcasts are required because there is no HTTP-specific filter class. Because some servlet containers may cache the response for performance reasons, workarounds must be implemented to function reliably.

Finally, this approach is slower than others. The XML must be converted into text and buffered in memory before the XSLT transformation can be performed, which is generally slower than sending SAX events or a DOM tree directly to the XSLT processor. Generating XML and performing the XSLT transformation in a servlet can avoid the extra conversions to and from text that filters require.

Previous | Home | Next
8.3. Identifying the Browser | Book Index | 8.5. XSLT as a Code Generator
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT as a Code Generator (Java and XSLT)

 Java and XSLTSearch this book

8.5. XSLT as a Code Generator

For performance reasons, EJB components typically return dependent objects rather than many individual fields. These are implemented as read-only classes that encapsulate a group of related fields. Borrowing an example from Enterprise JavaBeans by Richard Monson-Haefel (O'Reilly), Example 8-13 shows a typical dependent object.

Example 8-13. Address.java

public class Address implements java.io.Serializable {

    private String street;
    private String city;
    private String state;
    private String zip;

    /**
     * Construct a new dependent object instance.
     */
    public Address(String street, String city, String state, String zip) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zip = zip;

    }

    public String getStreet( ) {
        return this.street;
    }

    public String getCity( ) {
        return this.city;
    }

    public String getState( ) {
        return this.state;
    }

    public String getZip( ) {
        return this.zip;
    }
}

Now, rather than containing numerous fine-grained methods, an entity bean can provide a single method to retrieve an instance of Address. This reduces load on the network and database and makes the code somewhat easier to understand. As you can see, the Address class is very straightforward. It has a constructor that initializes all fields and a series of get methods.

Although Address is small, some dependent objects may have dozens of fields. These are tedious to write at best, resulting in a typing exercise rather than programming creativity. XSLT can help by acting as a simple code generator, minimizing the tedious part of the programmer's job. AddressDO.xml, shown in Example 8-14, contains the data that will feed into our code generator.

Example 8-14. AddressDO.xml

<?xml version="1.0" encoding="UTF-8"?>
<dependentObject class="Address">
    <property name="street" type="String" getter="getStreet"/>
    <property name="city" type="String" getter="getCity"/>
    <property name="state" type="String" getter="getState"/>
    <property name="zip" type="String" getter="getZip"/>
</dependentObject>

The XML data is obviously much shorter than the generated code, and the difference is magnified for larger dependent objects with many fields. The <dependentObject> element contains a list of <property> elements, each of which defines the field name, datatype, and get method name. Now that the data is captured in a well-defined XML format, a DTD or Schema can be used to perform validation. A really ambitious programmer might want to create a simple GUI front-end that allows graphical editing of the <dependentObject> structure.

An XSLT stylesheet performs the actual code generation. The output method should be set to text, and particular attention must be given to whitespace. With HTML or XHTML output, whitespace is largely irrelevant. Since browsers collapse multiple spaces and linefeeds into a single space, the XSLT stylesheet can be indented and spaced however you like. But with a code generator, formatting is a much higher priority. This can lead to stylesheets that are much harder to read, which is the main drawback of using XSLT as a code generator. Example 8-15 shows the dependent object code generator stylesheet.

Example 8-15. dependentObject.xslt

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:variable name="className" select="/dependentObject/@class"/>
    <!--
    ********************************************************************
    ** Generate the class skeleton. Other templates will generate
    ** portions of the class.
    *****************************************************************-->
    <xsl:template match="/dependentObject">public class <xsl:value-of
             select="$className"/>
       <xsl:text> implements java.io.Serializable {
</xsl:text>
    <xsl:apply-templates select="property" mode="generateField"/>
    <xsl:text>

    /**
     * Construct a new dependent object instance.
     */
    public </xsl:text>
            <xsl:value-of select="$className"/>(<xsl:apply-templates
                 select="property" mode="generateConstructorParam"/>
            <xsl:text>) {
</xsl:text>
            <xsl:apply-templates select="property"
                 mode="generateInitializers"/>
    }

    <xsl:apply-templates select="property" mode="generateGetter"/>
}
    </xsl:template>

    <!--
    *****************************************************************
    ** Generate a private field declaration.
    **************************************************************-->
    <xsl:template match="property" mode="generateField">
    private <xsl:value-of select="@type"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="@name"/>;</xsl:template>

    <!--
    *****************************************************************
    ** Generate a "get" method for a property.
    **************************************************************-->
    <xsl:template match="property" mode="generateGetter">
    public <xsl:value-of select="@type"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="@getter"/>( ) {
        return this.<xsl:value-of select="@name"/>;
    }
    </xsl:template>

    <!--
    *****************************************************************
    ** Generate one of the constructor parameters.
    **************************************************************-->
    <xsl:template match="property" mode="generateConstructorParam">
        <xsl:text xml:space="preserve"/>
        <xsl:value-of select="@type"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="@name"/>
        <xsl:if test="position() != last( )">, </xsl:if>
    </xsl:template>

    <!--
    *****************************************************************
    ** Generate the initialization code inside of the constructor.
    **************************************************************-->
    <xsl:template match="property" mode="generateInitializers">
        <xsl:text xml:space="preserve">        this.</xsl:text>
        <xsl:value-of select="@name"/>
        <xsl:text> = </xsl:text>
        <xsl:value-of select="@name"/>;
    </xsl:template>
</xsl:stylesheet>

This stylesheet produces the code for Address.java. It starts by setting the output method to text and creating a variable for the class name. The variable allows us to avoid typing <xsl:value-of select="/dependentObject/@class"/> whenever the class name is needed.

The <xsl:text> element is used frequently in code-generator stylesheets because it allows for more control over whitespace. In several places, this element is used to introduce linefeeds in the output. For instance:

       <xsl:text> implements java.io.Serializable {
</xsl:text>

Because the closing tag is on the next line, the linefeed character will be preserved faithfully. <xsl:text> is also used to introduce individual spaces:

private <xsl:value-of select="@type"/>
<xsl:text> </xsl:text>
<xsl:value-of select="@name"/>;</xsl:template>

By default, XSLT processors ignore whitespace between two XSLT elements unless some nonwhitespace characters are also present. The private text shown just before <xsl:value-of select="@type"/>, for example, contains nonwhitespace text followed by a space. In this case, the space after the word private will be preserved. But the space between the two <xsl:value-of> elements will be ignored unless it is explicitly preserved with <xsl:text> </xsl:text>.

Getting everything to indent and line up is challenging but is not an insurmountable problem. It usually boils down to a lot of XSLT tweaking until everything looks just right. Using a code beautifier is another option. Products such as JIndent (http://www.jindent.com) can automatically clean up Java code by wrapping long lines, inserting spaces, and putting braces at the correct locations. If you are fortunate enough to have access to a tool like this, you can ignore most whitespace issues in the XSLT and rely on JIndent to fix formatting problems later on.

Previous | Home | Next
8.4. Servlet Filters | Book Index | 8.6. Internationalization with XSLT
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Internationalization with XSLT (Java and XSLT)

 Java and XSLTSearch this book

8.6. Internationalization with XSLT

In this section, we explore the key techniques for internationalization (i18n) using XSLT. Although both Java and XSLT offer excellent support for i18n, pulling everything together into a working application is quite challenging. Hopefully this material will help to minimize some of the common obstacles.

8.6.1. XSLT Stylesheet Design

In its simplest form, i18n is accomplished by providing a separate XSLT stylesheet for each supported language. While this is easy to visualize, it results in far too much duplication of effort. This is because XSLT stylesheets typically contain some degree of programming logic in addition to pure display information. To illustrate this point, directory.xml is presented in Example 8-16. This is a very basic XML datafile that will be transformed using either English or Spanish XSLT stylesheets.

Example 8-16. directory.xml

<?xml version="1.0" encoding="UTF-8"?>
<directory>
  <employee category="manager">
    <name>Joe Smith</name>
    <phone>4-0192</phone>
  </employee>
  <employee category="programmer">
    <name>Sally Jones</name>
    <phone>4-2831</phone>
  </employee>
  <employee category="programmer">
    <name>Roger Clark</name>
    <phone>4-3345</phone>
  </employee>
</directory>

The screen shot shown in Figure 8-6 shows how an XSLT stylesheet transforms this XML into HTML.

And finally, Example 8-17 lists the XSLT stylesheet that produces this output.

Figure 8-6. English XSLT output

Example 8-17. directory_basic.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="UTF-8"/>
  <xsl:template match="/">
    <html>
      <head>
        <title>Employee Directory</title>
      </head>
      <body>
        <h1>Employee Directory</h1>
        <table cellpadding="4" cellspacing="0" border="1">
          <tr>
            <th>Name</th>
            <th>Category</th>
            <th>Phone</th>
          </tr>
          <xsl:for-each select="directory/employee">
            <tr>
              <td>
                <xsl:value-of select="name"/>
              </td>
              <td>
                <xsl:choose>
                  <xsl:when test="@category='manager'">
                    <xsl:text>Manager</xsl:text>
                  </xsl:when>
                  <xsl:when test="@category='programmer'">
                    <xsl:text>Programmer</xsl:text>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:text>Other</xsl:text>
                  </xsl:otherwise>
                </xsl:choose>
              </td>
              <td>
                <xsl:value-of select="phone"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

In this stylesheet, all locale-specific content is highlighted. This is information that must be changed to support a different language. As you can see, only a small portion of the XSLT is specific to the English language and is embedded directly within the stylesheet logic. The entire stylesheet must be rewritten to support another language.

Fortunately, there is an easy solution to this problem. XSLT stylesheets can import other stylesheets; templates and variables in the importing stylesheet take precedence over conflicting items in the imported stylesheet. By isolating locale-specific content, we can use <xsl:import> to support multiple languages while reusing all of the stylesheet logic. Example 8-18 shows a revised version of our XSLT stylesheet.

Example 8-18. directory_en.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="UTF-8"/>

  <!-- Isolate locale-specific content -->
  <xsl:variable name="lang.pageTitle" select="'Employee Directory'"/>
  <xsl:variable name="lang.nameHeading" select="'Name'"/>
  <xsl:variable name="lang.categoryHeading" select="'Category'"/>
  <xsl:variable name="lang.phoneHeading" select="'Phone'"/>
  <xsl:variable name="lang.manager" select="'Manager'"/>
  <xsl:variable name="lang.programmer" select="'Programmer'"/>
  <xsl:variable name="lang.other" select="'Other'"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="$lang.pageTitle"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="$lang.pageTitle"/></h1>
        <table cellpadding="4" cellspacing="0" border="1">
          <tr>
            <th><xsl:value-of select="$lang.nameHeading"/></th>
            <th><xsl:value-of select="$lang.categoryHeading"/></th>
            <th><xsl:value-of select="$lang.phoneHeading"/></th>
          </tr>
          <xsl:for-each select="directory/employee">
            <tr>
              <td>
                <xsl:value-of select="name"/>
              </td>
              <td>
                <xsl:choose>
                  <xsl:when test="@category='manager'">
                    <xsl:value-of select="$lang.manager"/>
                  </xsl:when>
                  <xsl:when test="@category='programmer'">
                    <xsl:value-of select="$lang.programmer"/>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:value-of select="$lang.other"/>
                  </xsl:otherwise>
                </xsl:choose>
              </td>
              <td>
                <xsl:value-of select="phone"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

The XSLT stylesheet is now much more amenable to i18n. All locale-specific content is declared as a series of variables. Therefore, importing stylesheets can override them. The lang. naming convention makes the stylesheet more maintainable; it is not a requirement or part of the XSLT specification. Other than isolating this content, the remainder of the stylesheet is exactly the same as it was before.

The Spanish version of the stylesheet is shown in Example 8-19.

Example 8-19. directory_es.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="directory_en.xslt"/>
  <xsl:output method="html" encoding="UTF-8"/>

  <!-- Isolate locale-specific content -->
  <xsl:variable name="lang.pageTitle" select="'Empleado guía telefónica'"/>
  <xsl:variable name="lang.nameHeading" select="'Nombre'"/>
  <xsl:variable name="lang.categoryHeading" select="'Categoría'"/>
  <xsl:variable name="lang.phoneHeading" select="'Teléfono'"/>
  <xsl:variable name="lang.manager" select="'Gerente'"/>
  <xsl:variable name="lang.programmer" select="'Programador'"/>
  <xsl:variable name="lang.other" select="'Otro'"/>

</xsl:stylesheet>

The Spanish stylesheet is much shorter because it merely overrides each of the locale-specific variables. The <xsl:import> is key:

<xsl:import href="directory_en.xslt"/>

Because of XSLT conflict-resolution rules, the variables defined in directory_es.xslt take precedence over those defined in directory_en.xslt. The same logic can be applied to templates, as well. This is useful in scenarios where the importing stylesheet needs to change behavior in addition to simply defining text translations.

The following line is optional:

<xsl:output method="html" encoding="UTF-8"/>

In this example, the output method and encoding are identical to the English version of the stylesheet, so this line has no effect. However, the importing stylesheet may specify a different output method and encoding if desired.

To perform the Spanish transformation using Xalan, issue the following command:

$ java org.apache.xalan.xslt.Process -IN directory.xml -XSL directory_es.xslt

Figure 8-7 shows the result of this transformation when displayed in a web browser.

Figure 8-7. Spanish output

NOTE: In the i18n example stylesheets presented in this chapter, common functionality is placed into one stylesheet. Importing stylesheets then replace locale-specific text. This same technique can be applied to any stylesheet and is particularly important when writing custom XSLT for a specific browser. Most of your code should be portable across a variety of browsers and should be placed into reusable stylesheets. The parts that change should be placed into browser-specific stylesheets that import the common stylesheets.

8.6.2. Encodings

A character encoding is a numeric representation of a particular character.[41] The US-ASCII encoding for the A character, for example, is 65. When computers read and write files using US-ASCII encoding, each character is stored as one byte of data. Of this byte, only seven bits are actually used to represent characters. The first (most significant) bit must always be 0. Therefore, US-ASCII can represent only 128 different characters. Of course, this presents a problem for languages that require more than 128 characters. For these languages, another character encoding must be used.

[41] Refer to Java Internationalization by Andy Deitsch and David Czarnecki (O'Reilly) for more detailed information on character encodings.

The most comprehensive character encoding is called ISO/IEC 10646. This is also known as the Universal Character Set (UCS) and allocates a 32-bit number for each character. Although this allows UCS to uniquely identify every character in every language, it is not directly compatible with most computer software. Also, using 32 bits to represent each character results in a lot of wasted memory.

Unicode is the official implementation of ISO/IEC 10646 and currently uses 16-bit characters. You can learn more about Unicode at http://www.unicode.org. UCS Transformation Formats (UTFs) are designed to support the UCS encoding while maintaining compatibility with existing computer software and encodings. UTF-8 and UTF-16 are the most common transformation formats, and all XML parsers and XSLT processors are required to support both.

If you deal mostly with English text, UTF-8 is the most efficient and easiest to use. Because the first 128 UTF-8 characters are the same as the US-ASCII characters, existing applications can utilize many UTF-8 files transparently. When additional characters are required, however, UTF-8 encoding will use up to three bytes per character.

UTF-16 is more efficient than UTF-8 for Chinese, Japanese, and Korean (CJK) ideographs. When using UTF-16, each character requires two bytes, while many will require three bytes under UTF-8 encoding. Either UTF-8 or UTF-16 should work. However, it is wise to test actual transformations with both encodings to determine which results in the smallest file for your particular data. On a pragmatic note, many applications and operating systems, particularly Unix and Linux variants, offer better support for UTF-8 encoding.

As nearly every XSLT example in this book has shown, the <xsl:output> element determines the encoding of the XSLT result tree:

<xsl:output method="html" encoding="UTF-16"/>

If this element is missing from the stylesheet, the XSLT processor is supposed to default to either UTF-8 or UTF-16 encoding.[42]

[42] The XSLT specification does not say how the processor is supposed to select between UTF-8 and UTF-16.

8.6.2.1. Creating the XML and XSLT

The XML input data, XSLT stylesheet, and result tree do not have to use the same character encodings or language. For example, an XSLT stylesheet may be encoded in UTF-16, but may specify UTF-8 as its output method:

<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="UTF-8"/>
...

Even though the first line specifies UTF-16, it is important that the text editor used to create this stylesheet actually uses UTF-16 encoding when saving the file. Otherwise, tools such as XML Spy (http://www.xmlspy.com) may report errors as shown in Figure 8-8.

Figure 8-8. Error dialog

To further complicate matters, there are actually two variants of UTF-16. In UTF-16 Little Endian (UTF-16LE) encoding, the low byte of each two-byte character precedes the high byte. As expected, the high byte precedes the low byte in UTF-16 Big Endian (UTF-16BE) encoding. Fortunately, XML parsers can determine the encoding of a file by looking for a byte order mark. In UTF-16LE, the first byte of the file should start with 0xFFFE. In UTF-16BE files, the byte order mark is 0xFEFF.

For the upcoming Chinese example, the NJStar Chinese word processor (http://www.njstar.com) was used to input the Chinese characters. This is an example of an editor that has the ability to input ideographs and store files in various encodings. The Windows NT version of Notepad can save files in Unicode (UTF-16LE) format, and the Windows 2000 version of Notepad adds support for UTF-8 and UTF-16BE.

If all else fails, encoded text files can be created with Java using the java.io.OutputStreamWriter class as follows:

FileOutputStream fos = new FileOutputStream("myFile.xml");
// the OutputStreamWriter specifies the encoding of the file
PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos, "UTF-16"));
...write to pw just like any other PrintWriter
pw.close( );

8.6.3. Putting It All Together

Getting all of the pieces to work together is often the trickiest aspect of i18n. To demonstrate the concepts, we will now look at XML datafiles, XSLT stylesheets, and a servlet that work together to support any combination of English, Chinese, and Spanish. A basic HTML form makes it possible for users to select which XML file and XSLT stylesheet will be used to perform a transformation. The screen shot in Figure 8-9 shows what this web page looks like.

Figure 8-9. XML and XSLT language selection

As you can see, there are three versions of the XML data, one for each language. Other than the language, the three files are identical. There are also three versions of the XSLT stylesheet, and the user can select any combination of XML and XSLT language. The character encoding for the resulting transformation is also configurable. UTF-8 and UTF-16 are compatible with Unicode and can display the Spanish and Chinese characters directly. ISO-8859-1, however, can display only extended character sets using entities such as &#25991;.

In this example, users explicitly specify their language preference. It is also possible to write a servlet that uses the Accept-Language HTTP header, which may contain a list of preferred languages:

en, es, ja

From this list, the application can attempt to select the appropriate language and character encoding without prompting the user. Chapter 13 of Java Servlet Programming, Second Edition by Jason Hunter (O'Reilly) presents a detailed discussion of this technique along with a class called LocaleNegotiator that maps more than 30 language codes to their appropriate character encodings.

In Figure 8-10, the results of three different transformations are displayed. In the first window, a Chinese XSLT stylesheet is applied to a Chinese XML datafile. In the second window, the English version of the XSLT stylesheet is applied to the Spanish XML data. Finally, the Spanish XSLT stylesheet is applied to the Chinese XML data.

Figure 8-10. Several language combinations

The character encoding is generally transparent to the user. Switching to a different encoding makes no difference to the output displayed in Figure 8-10. However, it does make a difference when the page source is viewed. For example, when the output is UTF-8, the actual Chinese or Spanish characters are displayed in the source of the HTML page. When using ISO-8859-A, however, the source code looks something like this:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>&#20013;&#25991;XSLT</title>
</head>
<body>
<h1>&#20013;&#25991;XSLT</h1>
...remainder of page omitted

As you can see, the Chinese characters are replaced by their corresponding character entities, such as &#20013;. The XSLT processor creates these entities automatically when the output encoding type cannot display the characters directly.

Browser Fonts

Recent versions of any major web browser can display UTF-8 and UTF-16 encoded characters without problems. Font configuration is the primary concern. If you are using Internet Explorer, be sure to select the View Figure 8.6.3 Encoding Figure 8.6.3 Auto Select menu option. Under Netscape 6, the View Figure 8.6.3 Character Coding Figure 8.6.3 Auto Detect menu option is comparable. If you run the examples and see question marks and garbled text, this is a good indication that the proper fonts are not installed on your system.

For the Chinese examples shown in this chapter, the Windows 2000 SimHei and SimSun fonts were installed. These and many other fonts are included with Windows 2000 but are not automatically installed unless the appropriate language settings are selected under the regional options window. This window can be found in the Windows 2000 Control Panel. A good source for font information on other versions of Windows is Fontboard at http://www.geocities.com/fontboard.

Sun Solaris users should start at the Sun Global Application Developer Corner web site at http://www.sun.com/developers/gadc/. This offers information on internationalization support in the latest versions of the Solaris operating system. For other versions of Unix or Linux, a good starting point is the Netscape 6 Help menu. The International Users option brings up a web page that provides numerous sources of fonts for various versions of Unix and Linux on which Netscape runs.

8.6.3.1. XML data

Each of the three XML datafiles used by this example follows the format shown in Example 8-20. As you can see, the XML data merely lists translations from English to another language. All three files follow the same naming convention: numbers_english.xml, numbers_spanish.xml, and numbers_chinese.xml.

Example 8-20. numbers_spanish.xml

<?xml version="1.0" encoding="UTF-8"?>
<numbers>
  <language>Español (Spanish)</language>
  <number english="one">uno</number>
  <number english="two">dos</number>
  <number english="three">tres</number>
  <number english="four">cuatro</number>
  <number english="five">cinco</number>
  <number english="six">seis</number>
  <number english="seven">siete</number>
  <number english="eight">ocho</number>
  <number english="nine">nueve</number>
  <number english="ten">diez</number>
</numbers>

8.6.3.2. XSLT stylesheets

The numbers_english.xslt stylesheet is shown in Example 8-21 and follows the same pattern that was introduced earlier in this chapter. Specifically, it isolates locale-specific data as a series of variables.

Example 8-21. numbers_english.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="UTF-8"/>

  <xsl:variable name="lang.pageTitle">XSLT in English</xsl:variable>
  <xsl:variable name="lang.tableCaption">
    Here is a table of numbers:
  </xsl:variable>
  <xsl:variable name="lang.englishHeading">English</xsl:variable>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="$lang.pageTitle"/></title>
      </head>
      <body>
        <xsl:apply-templates select="numbers"/>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="numbers">
    <h1><xsl:value-of select="$lang.pageTitle"/></h1>
    <xsl:value-of select="$lang.tableCaption"/>
    <table border="1">
      <tr>
        <th><xsl:value-of select="$lang.englishHeading"/></th>
        <th>
          <xsl:value-of select="language"/>
        </th>
      </tr>
      <xsl:apply-templates select="number"/>
    </table>
  </xsl:template>
  <xsl:template match="number">
    <tr>
      <td>
        <xsl:value-of select="@english"/>
      </td>
      <td>
        <xsl:value-of select="."/>
      </td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

As you can see, the default output encoding of this stylesheet is UTF-8. This can (and will) be overridden by the servlet, however. The Spanish stylesheet, numbers_spanish.xslt, is shown in Example 8-22.

Example 8-22. numbers_spanish.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="numbers_english.xslt"/>

  <xsl:variable name="lang.pageTitle">XSLT en Español</xsl:variable>
  <xsl:variable name="lang.tableCaption">
    Aquí está un vector de números:
  </xsl:variable>
  <xsl:variable name="lang.englishHeading">Inglés</xsl:variable>

</xsl:stylesheet>

The Chinese stylesheet, numbers_chinese.xslt, is not listed here because it is structured exactly like the Spanish stylesheet. In both cases, numbers_english.xslt is imported, and the three variables are overridden with language-specific text.

8.6.3.3. Web page and servlet

The user begins with the web page that was shown in Figure 8-9. The HTML source for this page is listed in Example 8-23. The language and encoding selections are posted to a servlet when the user clicks on the Submit button.

Example 8-23. i18n.html

<html>
<head>
<title>Internationalization Demo</title>
</head>
<body>
<form method="post" action="/chap8/languageDemo">
  <table border="1">
    <tr valign="top">
    <td>XML Language:</td>
    <td>
      <input type="radio" name="xmlLanguage"
             checked="checked" value="english"> English<br />
      <input type="radio" name="xmlLanguage" value="spanish"> Spanish<br />
      <input type="radio" name="xmlLanguage" value="chinese"> Chinese
    </td>
  </tr>

  <tr valign="top">
    <td>XSLT Language:</td>
    <td>
      <input type="radio" name="xsltLanguage"
              checked="checked" value="english"> English<br />
      <input type="radio" name="xsltLanguage" value="spanish"> Spanish<br />
      <input type="radio" name="xsltLanguage" value="chinese"> Chinese
    </td>
  </tr>

  <tr valign="top">
    <td>Character Encoding:</td>
    <td>
      <input type="radio" name="charEnc" value="ISO-8859-1"> ISO-8859-1<br />
      <input type="radio" name="charEnc" value="UTF-8"
               checked="checked"> UTF-8<br />
      <input type="radio" name="charEnc" value="UTF-16"> UTF-16<br />
    </td>
  </tr>
  </table>

  <p>
  <input type="submit" name="submitBtn" value="Submit">
  </p>
</form>
</body>
</html>

The servlet, LanguageDemo.java, is shown in Example 8-24. This servlet accepts input from the i18n.html web page and then applies the XSLT transformation.

Example 8-24. LanguageDemo.java servlet

package chap8;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * Allows any combination of English, Spanish, and Chinese XML
 * and XSLT.
 */
public class LanguageDemo extends HttpServlet {

    public void doPost(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        ServletContext ctx = getServletContext( );

        // these are all required parameters from the HTML form
        String xmlLang = req.getParameter("xmlLanguage");
        String xsltLang = req.getParameter("xsltLanguage");
        String charEnc = req.getParameter("charEnc");

        // convert to system-dependent path names
        String xmlFileName = ctx.getRealPath(
                "/WEB-INF/xml/numbers_" + xmlLang + ".xml");
        String xsltFileName = ctx.getRealPath(
                "/WEB-INF/xslt/numbers_" + xsltLang + ".xslt");

        // do this BEFORE calling HttpServletResponse.getWriter( )
        res.setContentType("text/html; charset=" + charEnc);

        try {
            Source xmlSource = new StreamSource(new File(xmlFileName));
            Source xsltSource = new StreamSource(new File(xsltFileName));

            TransformerFactory transFact = TransformerFactory.newInstance( );
            Transformer trans = transFact.newTransformer(xsltSource);

            trans.setOutputProperty(OutputKeys.ENCODING, charEnc);

            // note: res.getWriter( ) will use the encoding type that was
            //       specified earlier in the call to res.setContentType( )
            trans.transform(xmlSource, new StreamResult(res.getWriter( )));

        } catch (TransformerConfigurationException tce) {
            throw new ServletException(tce);
        } catch (TransformerException te) {
            throw new ServletException(te);
        }
    }
}

After getting the three request parameters for XML, XSLT, and encoding, the servlet converts the XML and XSLT names to actual filenames:

String xmlFileName = ctx.getRealPath(
        "/WEB-INF/xml/numbers_" + xmlLang + ".xml");
String xsltFileName = ctx.getRealPath(
        "/WEB-INF/xslt/numbers_" + xsltLang + ".xslt");

Because the XML files and XSLT stylesheets are named consistently, it is easy to determine the filenames. The next step is to set the content type of the response:

// do this BEFORE calling HttpServletResponse.getWriter( )
res.setContentType("text/html; charset=" + charEnc);

This is a critical step that instructs the servlet container to send the response to the client using the specified encoding type. This gets inserted into the Content-Type HTTP response header, allowing the browser to determine which encoding to expect. In our example, the three possible character encodings result in the following possible content types:

Content-Type: text/html; charset=ISO-8869-1
Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-16

Next, the servlet uses the javax.xml.transform.Source interface and the javax.xml.transform.stream.StreamSource class to read from the XML and XSLT files:

Source xmlSource = new StreamSource(new File(xmlFileName));
Source xsltSource = new StreamSource(new File(xsltFileName));

By using java.io.File, the StreamSource will correctly determine the encoding of the XML and XSLT files by looking at the XML declaration within each of the files. The StreamSource constructor also accepts InputStream or Reader as parameters. Special precautions must be taken with the Reader constructors, because Java Reader implementations use the default Java character encoding, which is determined when the VM starts up. The InputStreamReader is used to explicitly specify an encoding as follows:

Source xmlSource = new StreamSource(new InputStreamReader(
        new FileInputStream(xmlFileName), "UTF-8"));

For more information on how Java uses encodings, see the JavaDoc package description for the java.lang package.

Our servlet then overrides the XSLT stylesheet's output encoding as follows:

trans.setOutputProperty(OutputKeys.ENCODING, charEnc);

This takes precedence over the encoding that was specified in the <xsl:output> element shown earlier in Example 8-21.

Finally, the servlet performs the transformation, sending the result tree to a Writer obtained from HttpServletResponse:

// note: res.getWriter( ) will use the encoding type that was
//       specified earlier in the call to res.setContentType( )
trans.transform(xmlSource, new StreamResult(res.getWriter( )));

As the comment indicates, the servlet container should set up the Writer to use the correct character encoding, as specified by the Content-Type HTTP header.[43]

[43] UTF-16 works under Tomcat 3.2.x but fails under Tomcat 4.0 beta 5. Hopefully this will be addressed in later versions of Tomcat.

8.6.4. I18n Troubleshooting Checklist

Here are a few things to consider when problems occur. First, rule out obvious problems:

  • Visit a web site that uses the language you are trying to produce. For example, http://www.chinadaily.com.cn/ has an option to view the site in Chinese. This will confirm that your browser loads the correct fonts.

  • Test your application with English XML data and XSLT stylesheets to verify that the transformations are performed correctly.

  • Perform the XSLT transformation on the command line. Save the result to a file and view with a Unicode-compatible text editor. If all else fails, view with a binary editor to see how the characters are being encoded.

  • Verify that your XML parser supports the encodings you are trying to parse.[44]

    [44] Encoding supported by Apache's Xerces parser are documented at http://xml.apache.org/xerces-j/faq-general.html.

If these tests do not uncover the problem, try the following:

  • Stick with UTF-8 encoding until problems are resolved. This is the most compatible encoding.

  • Verify that the servlet sets the Content-Type header to:

    Content-Type: text/html; charset=UTF-8

  • Verify that the XSLT stylesheet sets the appropriate encoding on the <xsl:output> element or override the encoding programmatically:

    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

  • Insert some code into the servlet that performs the transformation but sends the result to a file instead of the HttpServletResponse's Writer. Inspect this file with a Unicode-compatible text editor.

  • Use java.io.File or java.io.InputStream instead of java.io.Reader when reading XML and XSLT files.

Previous | Home | Next
8.5. XSLT as a Code Generator | Book Index | 9. Development Environment, Testing, and Performance
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Development Environment, Testing, and Performance (Java and XSLT)

 Java and XSLTSearch this book

Chapter 9. Development Environment, Testing, and Performance

Contents:

Development Environment
Testing and Debugging
Performance Techniques

This chapter provides an overview of many different technologies that comprise a typical Java and XSLT software development environment. Once the most commonly used tools are introduced, strategies for testing XSLT and tuning performance are presented. Instead of presenting specific performance benchmarks for various XSLT processors, this chapter's focus is on effective programming techniques that should be applicable to a wide range of tools. XSLT is a very young technology, and tools are improving all the time.

9.1. Development Environment

Specialized, lightweight development tools have never been more important to Java developers. Commercial integrated development environments (IDEs) are now only one small piece of a larger suite of essential tools used by a majority of Java development projects. These build tools such as Ant, testing tools such as JUnit, and various XML parsers and XSLT processors. Figure 9-1 illustrates some of the tools found in a typical Java and XSLT development environment.

Figure 9-1. Common Java, XML, and XSLT tools

Although this is a typical development environment, it can be a large number of tools to keep track of. Table 9-1 summarizes how each of these tools is used.

Table 9-1. Tool overview

Tool | Description
Java 2 SDK | The Java 2 software development kit, i.e., the JDK.
Apache's JMeter | A stress-testing tool, primarily used to test scalability and performance of servlets and web sites.
EJB Container | Enterprise JavaBeans server, such as JBoss, Enhydra, WebLogic, or WebSphere.
XML Parser | Xerces, Crimson, or another SAX and/or DOM parser.
XSLT Processor | Xalan, SAXON, or any other XSLT processor.
Servlet Container | Apache's Tomcat or any other servlet host. Many application servers include both servlet containers and EJB containers.
JAXP | Provides a common API to XML parsers and XSLT processors.
Apache's Ant | A Java replacement for make. Ant build files provide a consistent way for every member of the development team to compile and test code.
IDE | An integrated development environment, such as Borland's JBuilder.
JUnit | An open source unit testing framework.

Some individual tools are much more powerful when used in the context of an overall development environment. JUnit is much more effective when used in combination with Ant, because Ant ensures that every developer on the team is compiling and testing with the same settings. This means that unit tests executed by one developer should work the same way for everyone else. Without Ant, unit tests that succeed for one developer may fail for others, since they may be using different versions of some tools.

9.1.1. CLASSPATH Issues

The migration from first generation XML parsers and XSLT processors has been a somewhat painful experience for Java developers. Although the newer APIs are great, older JAR files linger throughout many applications and directories, causing more than their fair share of configuration difficulties. This section describes some of the most common problems and offers advice for configuring several popular tools.

9.1.1.1. JAR hell?

A common complaint against Microsoft Windows systems is known as "DLL Hell." This refers to problems that occur when two applications require different versions of the same DLL file. Installing a new application may overwrite an older version of a DLL file that existing applications depend on, causing erratic behavior or outright system crashes.[45]

[45] Commonly referred to as the blue screen of death.

More frequently than ever before, Java developers must contend with incompatible JAR file versions. For instance, JAXP 1.0 and JAXP 1.1 both ship with a JAR file named jaxp.jar. Applications that require JAXP 1.1 functionality will fail if the 1.0 version of jaxp.jar is listed on the CLASSPATH earlier than the newer version. This happens more often than developers expect, because many commercial and open source development tools ship with XML parsers and XSLT processors. The installation routines for these tools may install JAR files without informing developers or asking for their consent.

The simple fix is to locate and remove old versions of JAR files. This is easier said than done, because in many cases (such as JAXP), the version number is not part of the JAR filename. Since many tools ignore or modify the CLASSPATH when they are executed, simply removing older JAR files from the CLASSPATH will not eradicate all problems. Instructions for fixing this problem in Ant, Tomcat, and JBuilder are coming up.

Some JAR files are beginning to include version information inside of the META-INF/MANIFEST.MF file. This is called the manifest and can be extracted with the following command, where filename.jar is the name of the JAR file:

jar -xf filename.jar META-INF/MANIFEST.MF

Once extracted, the manifest can be viewed with any text editor. Example 9-1 shows the content of the manifest from Version 1.0 of jaxp.jar:

Example 9-1. Version 1.0 jaxp.jar manifest contents

Manifest-Version: 1.0
Specification-Title:  Java API for XML Parsing Interfaces
Specification-Vendor: Sun Microsystems
Created-By: 1.2.2 (Sun Microsystems Inc.)
Specification-Version: 1.0.0

Name: javax/xml/parsers
Package-Version: 1.0.0
Specification-Title:  Java API for XML Parsing
Specification-Vendor: Sun Microsystems
Sealed: true
Specification-Version: 1.0.0
Package-Vendor: Sun Microsystems, Inc.
Package-Title: javax.xml.parsers

This manifest makes it quite easy to identify the contents of this JAR file. Although Sun's products tend to be very good about this, the manifest contents are entirely optional, and many other products omit all manifest information.

9.1.1.2. Sealing violations

The dreaded "sealing violation" is one of the more cryptic exceptions encountered. Example 9-2 shows a stack trace that is displayed when a sealing violation occurs.

Example 9-2. Sealing violation stack trace

Exception in thread "main" java.lang.SecurityException: sealing violation
  at java.net.URLClassLoader.defineClass(URLClassLoader.java:234)
  at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
  at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:297)
  at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:286)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:253)
  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
  at java.lang.Class.forName0(Native Method)
  at java.lang.Class.forName(Class.java:120)
  at javax.xml.transform.TransformerFactory.newInstance(TransformerFactory.java:117)
  at Test.main(Test.java:17)

This exception is hard to diagnose because the error message is not very descriptive, and the stack trace consists mostly of internal Java classes. According to the stack trace, line 17 of Test.java caused the problem. Here it is:

TransformerFactory transFact = TransformerFactory.newInstance( );

Actually, this line of code is perfectly correct. The problem lies in the CLASSPATH instead. The key to understanding this error is the sealing violation description. This indicates that one or more sealed JAR files are on the CLASSPATH in the wrong order.

A sealed JAR file has a manifest entry Sealed: true, as shown in Example 9-1.[46] The package sealing mechanism was introduced in Java Version 1.2 to enforce version consistency. Whenever a package is sealed, all classes in that package must be loaded from the same JAR file. If some of the classes are loaded from one JAR file and others from another, an instance of java.lang.SecurityException is thrown. Figure 9-2 illustrates the problem.

[46] It is also possible to seal individual packages within a JAR file. Refer to the Java 2 Standard Edition documentation for more information.

Figure 9-2. Sealing violation

In this diagram, parser.jar is listed on the CLASSPATH before crimson.jar. This is a problem because Java applications search JAR files in the order in which they appear on the CLASSPATH. Once the org.xml.sax.SAXException class has been loaded by the JVM, any additional classes or interfaces in the org.xml.sax package must be loaded from parser.jar because it is sealed. When the application requests an instance of ContentHandler, the class loader attempts to load the requested class file from crimson.jar, which triggers the SecurityException shown in Example 9-2. The simple fix to this problem is to remove parser.jar from the CLASSPATH, which will load all classes in the org.xml.sax package from crimson.jar.

9.1.1.3. Other exceptions and errors

Other various "configuration" exceptions defined by JAXP are javax.xml.transform.TransformerConfigurationException and javax.xml.parsers.Factory -ConfigurationError. These may occur when an older version of jaxp.jar is still listed on the CLASSPATH. Since JAXP 1.0 is not aware of SAX 2, DOM 2, or XSLT transformations, applications requesting any of these new features may see one of these exceptions when JAXP 1.0 is installed instead of JAXP 1.1.

As mentioned earlier, the filename jaxp.jar is used with Versions 1.0 and 1.1 of JAXP. Therefore, special care must be taken to ensure that the newer copy is present instead of the old one. Since JAXP 1.1 is backwards-compatible with Version 1.0, the older version can be safely replaced without breaking currently installed applications that depend on it.

The easiest exception to debug is java.lang.ClassNotFoundException . This may occur when JAXP 1.1 is listed on the CLASSPATH. However, an XSLT processor or XML parser is not listed. To remedy this situation, merely add a JAXP-compliant parser and XSLT processor to the CLASSPATH.

9.1.1.4. Java optional packages

The Java VM does not simply load classes based on the CLASSPATH environment variable. Before searching the CLASSPATH, the VM attempts to load classes from an optional package directory. An installed optional package is a JAR file located in the Java 2 Runtime Environment's lib/ext directory or in the jre/lib/ext directory of the Java 2 SDK.

If an installed optional package is not located, the VM then searches for download optional packages. These are JAR files that are explicitly referenced by the Class-Path manifest header of another JAR file. For example, a manifest might contain the following line:

Class-Path: jaxp.jar

In this case, the VM would look for jaxp.jar in the same directory as the JAR file that contains the manifest entry.

The best way to ensure that the correct version of XML parser, XSLT processor, and JAXP are installed is to manually copy the required JAR files to the installed optional package directory. Software developers should have the Java 2 SDK installed and should place JAR files in the JAVA_HOME/jre/lib/ext directory. End users, however, will probably use the Java 2 Runtime Environment instead of the entire SDK. For these users, the JAR files can be installed to the lib/ext directory where the JRE is installed.

To uninstall a Java optional package, merely delete the JAR file from the appropriate directory.

9.1.1.5. Java IDEs

Many developers use tools such as Borland's JBuilder as Java development environments. These tools can introduce problems because they typically include a copy of the Java 2 SDK. When running and compiling within the IDE, the VM uses the tool's own Java directory rather than the Sun Java 2 SDK that is probably already installed elsewhere on the system. Figure 9-3 is a typical directory structure that illustrates this potential problem.

Figure 9-3. Typical directory structure

In this example, JBuilder is properly configured with JAXP 1.1, the Crimson XML parser, and the Xalan 2.0 JAR file. This means that compilation, running, and debugging will all work properly within the JBuilder IDE. But once the application is executed outside of JBuilder, it will probably fail. This is because the Java 2 SDK contains the older JAXP 1.0 JAR file and its older XML parser.

Merely adding the newer JAXP-related JAR files to the CLASSPATH will probably introduce a sealing exception rather than fix the problem, because the VM will still load files from the installed optional package directory before searching the CLASSPATH. One way to fix this problem is to replace jaxp.jar and parser.jar with the same JAR files found in JBuilder's directory. Another option is to update the JAVA_HOME environment variable and PATH to point to JBuilder's version of Java.

9.1.2. Ant

Configuring JAR files and the CLASSPATH on a single developer's machine can be difficult; keeping an entire team of developers in sync requires support from tools. For this reason, it is critical that every team member use the same build process when testing and integrating code changes. In this section, we take a brief look at Apache's Ant.

As discussed in Chapter 3, "XSLT Part 2 -- Beyond the Basics", Apache's Ant is a Java-based build tool that is an excellent alternative to make.[47] Ant is good for numerous reasons, including:

[47] These notes apply to Ant 1.3. Newer versions of Ant may handle JAR files differently.

  • Its XML build files are easier to create than Makefiles.

  • It is written in Java and is quite portable.

  • Builds are extremely fast because the same VM instance is used for most steps in the build process.

Ant can be acquired form http://jakarta.apache.org/ant.[48]

[48] The original author of Ant is working on a new Java build tool called Amber, available at http://www.xiyo.org/Amber.

9.1.2.1. Installing Ant

To install Ant, simply download the binary distribution and uncompress to a convenient directory. Then set the ANT_HOME environment variable to point to this directory and JAVA_HOME to point to the Java installation directory. To test, type ant -version. This should display something similar to the following:

Ant version 1.3 compiled on March 2 2001

Since Ant is written in Java, care must be taken to avoid conflicts with Ant's JAR files and JAR files found in the system CLASSPATH. This is a particular concern when using Ant to drive the XSLT transformation process because Ant ships with JAXP 1.0 JAR files that are not compatible with newer JAXP 1.1 implementations.

Once Ant is installed, update ANT_HOME/lib/jaxp.jar and ANT_HOME/lib/parser.jar, which are part of the older JAXP 1.0 reference implementation. Any JAR files added to the ANT_HOME/lib directory are automatically added to Ant's CLASSPATH and will be seen by the various Ant tasks during the build process. Simply adding JAXP 1.1-compatible JAR files to the ANT_HOME/lib directory will prevent most conflicts with newer applications that require DOM 2, SAX 2, or support for XSLT transformations.

9.1.2.2. A typical Ant build file

The best way to learn about Ant is to download it, read the first part of the user manual, and then study several example build files. Example 9-3 presents one such build file, which can be used to compile some of the example code in this chapter as well as perform an XSLT transformation.

Example 9-3. build.xml

<?xml version="1.0"?>
<!--
  *******************************************************
  ** Example Ant build file as shown in Chapter 9, "Development Environment, Testing, and Performance".
  **
  ** Assumes the following directory structure:
  **  examples
  **    +-chapters
  **    |   +-chap9
  **    |       build.xml (this file)
  **    |       aidan.xml (example XML file)
  **    |       condensePerson.xslt (example XSLT file)
  **    +-common
  **    |   +-src
  **    |       +-com/oreilly/javaxslt/swingtrans/...
  **    |
  **    +-build (created by this build file)
  **
  ****************************************************-->
<project name="chap9" default="help" basedir="../..">

  <!--
  *******************************************************
  ** Global properties.
  ****************************************************-->
  <property name="builddir" value="build"/>

  <path id="thirdparty.class.path">
    <pathelement path="lib/saxon_6.2.2.jar"/>
    <pathelement path="lib/jaxp_1.1.jar"/>
    <pathelement path="lib/servlet_2.3.jar"/>
    <pathelement path="lib/junit_3.5.jar"/>
    <pathelement path="lib/jdom_beta6.jar"/>
  </path>

  <!--
  *******************************************************
  ** Create the output directory structure.
  ****************************************************-->
  <target name="prepare">
    <mkdir dir="${builddir}"/>
  </target>

  <!--
  *******************************************************
  ** Show a brief usage message. This is the default
  ** target, and shows up when the user types "ant"
  ****************************************************-->
  <target name="help" description="Show a brief help message">
    <echo message=Chapter 9, "Development Environment, Testing, and Performance" Example Ant Build File"/>
    <echo message="Type 'ant -projecthelp' for more assistance..."/>
  </target>

  <!--
  ********************************************************
  ** Remove the entire build directory
  *****************************************************-->
  <target name="clean"
        description="Remove all generated code">
    <delete dir="${builddir}"/>
  </target>

  <!--
  ********************************************************
  ** Compile the com.oreilly.javaxslt.swingtrans package
  *****************************************************-->
  <target name="compile" depends="prepare"
        description="Compile the SwingTransformer application">
    <javac srcdir="common/src" destdir="${builddir}"
            includes="com/oreilly/javaxslt/swingtrans/**">
      <classpath refid="thirdparty.class.path"/>
    </javac>
  </target>

  <!--
  ********************************************************
  ** Run com.oreilly.javaxslt.swingtrans.SwingTransformer
  *****************************************************-->
  <target name="run" depends="compile"
        description="Run the SwingTransformer application">
    <java fork="yes"
          classname="com.oreilly.javaxslt.swingtrans.SwingTransformer">
      <classpath>
        <pathelement path="${builddir}"/>
      </classpath>
      <classpath refid="thirdparty.class.path"/>
    </java>
  </target>

  <!--
  ********************************************************
  ** Performs an XSLT transformation. If either the XML
  ** file or the XSLT stylesheet change, the transformation
  ** is performed again.
  **
  ** basedir - specifies the location of the XSLT
  ** destdir - a required attribute, however Ant 1.3 is
  **           ignoring this. The messages on the console
  **           indicate that the destdir is being used,
  **           however it was found that the "out"
  **           attribute also has to specify the output
  **           directory.
  *****************************************************-->
  <target name="transform"
        description="Perform an XSLT transformation">
    <style processor="trax"
        basedir="chapters/chap9"
        destdir="${builddir}"
        style="condensePerson.xslt"
        in="chapters/chap9/aidan.xml"
        out="${builddir}/aidan_condensed.xml">

      <!-- pass a stylesheet parameter -->
      <param name="includeMiddle" expression="yes"/>
    </style>
  </target>
</project>

All Ant build files are XML and have a <project> root element. This specifies the default target, as well as the base directory. Each of the targets is specified using <target> elements, which can have dependencies on each other. Targets, in turn, contain tasks, which are responsible for performing individual units of work.

The CLASSPATH used by various tasks can be defined once and reused throughout the build file. The <path> element is emphasized in Example 9-3, including several JAR files from the lib directory. For instance:

<pathelement path="lib/servlet_2.3.jar"/>

This illustrates two key points about defining a consistent development environment. First, it is a good idea to rename JAR files to include version numbers. This is a great way to avoid conflicts and unexpected errors, because different versions of most tools use the same filenames for JAR files. By renaming them, it is easier to keep track of what is installed on the system. The only drawback to this approach is that build files must be manually updated whenever new versions of JAR files are installed.

Second, this particular Ant build file defines its own CLASSPATH, rather than relying on the developer's CLASSPATH. Relying on the CLASSPATH environment variable introduces problems because each developer on a team may have a completely different set of JAR files defined in his environment. By encoding everything in the Ant build file, everyone will compile and test with the same setup.

The following target shows how the build file compiles the application:

<target name="compile" depends="prepare"
      description="Compile the SwingTransformer application">
  <javac srcdir="common/src" destdir="${builddir}"
          includes="com/oreilly/javaxslt/swingtrans/**">
    <classpath refid="thirdparty.class.path"/>
  </javac>
</target>

So, to execute this target, simply type ant compile from the command prompt. Since this target depends on the prepare target, the build directory will be created before the code is compiled. Fortunately, the <javac> task is smart enough to compile only source code files that have changes since the last build, making Ant much faster than manually typing javac *.java.

The srcdir and destdir attributes are relative to the basedir that was specified in the <project> element. Since Ant always uses forward slashes (/) as path separators, these relative directories will work on Windows and Unix/Linux systems. As you might guess, the includes attribute defines a filter that limits which files are included in the build.

The last target in this build file performs an XSLT transformation using Ant's <style> task, which is described next.

9.1.2.3. Transforming using Ant's style task

Of particular interest to XSLT developers is Ant's <style> task. This is a core task that performs one or more XSLT transformations. Ant's JAXP JAR files must be updated as described earlier for this task to work. Here is a simple example of this task:

<style basedir="." destdir="." style="sample.xslt" processor="trax"
    in="company.xml" out="report.txt"/>

This will look in the project's base directory for the specified XML and XSLT files, placing the output into report.txt. The processor is trax, which means the same thing as JAXP 1.1. Ant will use the first JAXP-compliant processor found on the CLASSPATH. Table 9-2 lists the complete set of attributes for the style task.

Table 9-2. Ant style attributes

Attribute | Description | Required?
basedir | The directory where XML files are located. | yes
destdir | The directory where the result tree should be placed. | yes
extension | The default filename extension for the result of the transformation(s). | no
style | The XSLT stylesheet filename. | yes
processor | Specifies which XSLT processor is used. Legal values are "trax" for a TrAX-compliant processor, "xslp" for the XSL:P processor, and "xalan" for Xalan Version 1.x. May also contain the name of a class that implements org.apache.tools.ant.taskdefs.XSLTLiaison. When omitted, defaults to "trax." | no
includes | The comma-separated list of file patterns to include. | no
includesfile | The name of a file that contains include patterns. | no
excludes | The comma-separated list of file patterns to exclude. | no
excludesfile | The name of a file that contains exclude patterns. | no
defaultexcludes | May be "yes" or "no," defaults to "yes." | no
in | A single XML file input. | no
out | A single output filename. | no

The pattern attributes, such as includes and excludes, work just like other patterns in Ant. Basically, these allow the task to filter which files are included and excluded from the transformations. When omitted, all files in the base directory are included. Here is how an entire directory of XML files can be transformed:

<style basedir="xmlfiles" includes="*.xml" destdir="build/doc" style="report.xslt"
    extension="html"/>

As shown back in Example 9-3, parameters can be passed using nested <param> elements. This element has required name and expression attributes:

<style basedir="xmlfiles" includes="*.xml" destdir="build/doc" style="report.xslt"
    extension="html">
  <param name="reportType" expression="'detailed'"/>
</style>

9.1.3. Tomcat

Apache's Tomcat is a Servlet and JSP container and has been mentioned throughout this book. It is available from http://jakarta.apache.org/tomcat. Tomcat is fairly easy to install and configure:

  • Download the latest Tomcat release build for your operating system.

  • Uncompress the distribution to a directory.

  • Set the TOMCAT_HOME environment variable to point to this directory.

  • Set the JAVA_HOME environment variable to point to your Java distribution.

Since web applications are required to read configuration information from their XML deployment descriptors (web.xml), all current versions of Tomcat ship with an XML parser.

9.1.3.1. Configuring Tomcat 3.2.x

Tomcat 3.2.x includes several JAR files in its $TOMCAT_HOME/lib directory. Among these are jaxp.jar and parser.jar, which support JAXP Version 1.0 along with a SAX 1.0 and DOM 1.0 XML parser. Any JAR file added to the lib directory becomes available to every web application. Tomcat uses a simple script to locate *.jar in the lib directory, adding each JAR file to the CLASSPATH as it is encountered. The order of inclusion depends on how the operating system lists files, which is generally alphabetically. The complete CLASSPATH used by Tomcat 3.2.x includes the following:

  • $TOMCAT_HOME/classes

  • $TOMCAT_HOME/lib/*.jar

  • Any existing CLASSPATH

  • $JAVA_HOME/jre/lib/tools.jar

Although the lib directory provides a convenient way to install utility code that all web applications must use, conflicts arise when individual applications require different versions of SAX, DOM, or JAXP. If Tomcat finds an older version of one of these tools before it finds a newer version, exceptions typically occur. For instance, a sealing violation exception may occur if the existing CLASSPATH contains the newer crimson.jar, but an older version of parser.jar is still present.

The best approach to fully configure Tomcat 3.2.x for XML support is as follows:

  • Remove jaxp.jar and parser.jar from the $TOMCAT_HOME/lib directory.

  • Install the following files from the JAXP 1.1 distribution into the $TOMCAT_HOME/lib directory: jaxp.jar, crimson.jar, and xalan.jar.

Of course, JAXP 1.1 supports other tools besides Crimson and Xalan. If you prefer, simply replace crimson.jar and xalan.jar with competing products that are JAXP 1.1-compatible.

9.1.3.2. Configuring Tomcat 4.0.x

Tomcat 4.0 improves upon Tomcat 3.2.x configuration issues in two key ways. First, the user's existing CLASSPATH is no longer appended to Tomcat's CLASSPATH. This helps to avoid situations where code works for one developer (who happens to have some critical file on her CLASSPATH) but fails for other developers who have slightly different personal CLASSPATH configurations.

Secondly, Tomcat 4.0 no longer places JAXP JAR files in a location visible to web applications. This means that if XML support is required, you must install the proper XML JAR files before anything will work. This is far better than the old Tomcat model, because it avoids unexpected collisions with XML libraries used internally by Tomcat. Instead, if you forget to install XML support, you simply see a java.lang.NoClassDefFoundError.

To install XML support into Tomcat 4.0, simply install the required JAR files into the $TOMCAT_HOME/lib directory. These will then be available to all web applications. The other option is to install JAR files into the WEB-INF/lib directory of individual web applications. With this approach, each application can use different versions of various packages without fear of conflicts.

Previous | Home | Next
8.6. Internationalization with XSLT | Book Index | 9.2. Testing and Debugging
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Testing and Debugging (Java and XSLT)

 Java and XSLTSearch this book

9.2. Testing and Debugging

The software development community has shown a renewed interest in testing during the past few years. Much of this has been driven by the eXtreme Programming methodology, which emphasizes lightweight processes and constant unit testing to promote quality.[49] To demonstrate how to test XSLT transformations, a few simple files will be used. The XML data is shown first in Example 9-4.

[49] See http://www.xprogramming.com for more information on eXtreme Programming.

Example 9-4. aidan.xml

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <firstName>Aidan</firstName>
    <middleName>Garrett</middleName>
    <lastName>Burke</lastName>
    <birthDate month="6" day="25" year="1999"/>
</person>

Although this data is trivial, the same concepts apply to larger, more realistic examples. The sample XSLT stylesheet is shown in Example 9-5.

Example 9-5. condensePerson.xslt

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** Transforms an XML file representing a person into a
** more concise format.
********************************************************-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="includeMiddle" select="'yes'"/>

  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"
      doctype-system="condensed.dtd"/>

  <!-- match an existing <person> element -->
  <xsl:template match="person">
    <!-- produce a new <person> element in a condensed form -->
    <xsl:element name="person">
      <xsl:element name="name">
        <xsl:value-of select="firstName"/>
        <xsl:text> </xsl:text>
        <xsl:if test="$includeMiddle = 'yes'">
          <xsl:value-of select="middleName"/>
          <xsl:text> </xsl:text>
        </xsl:if>
        <xsl:value-of select="lastName"/>
      </xsl:element>

      <xsl:element name="birthDate">
        <xsl:value-of select="birthDate/@month"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="birthDate/@day"/>
        <xsl:text>/</xsl:text>
        <xsl:value-of select="birthDate/@year"/>
      </xsl:element>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

The job of this stylesheet is to transform XML data into a more concise format as shown in Example 9-6.

Example 9-6. Expected output

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE person
  SYSTEM "condensed.dtd">
<person>
   <name>Aidan Garrett Burke</name>
   <birthDate>6/25/1999</birthDate>
</person>

Finally, the DTD for the condensed XML file is shown in Example 9-7.

Example 9-7. condense.dtd

<!ELEMENT person (name, birthDate)>
<!ELEMENT birthDate (#PCDATA)>
<!ELEMENT name (#PCDATA)>

By providing a DTD for the expected XML output, a unit test can easily validate the result tree after performing one or more transformations. Such a test simply writes the transformation results to a file and then attempts to parse them using a validating XML parser.

9.2.1. JUnit

JUnit is an open source testing framework available from http://www.junit.org. It is a lightweight tool designed to be used by programmers specifically for unit tests. Other tools are generally better for integration testing and functional testing, but these are not discussed here.

Since XSLT transformations can be performed independently of the remainder of an application, they are a perfect candidate for automated unit testing. A technology such as JSP, however, is quite difficult to test in an automated fashion because JSPs must be executed within the context of a JSP container and web browser.

An automated test is one that reports "success" or "failure" after execution and does not require a human being to interact as the test is running. For instance, requiring a user to type in specific values into HTML form fields and then look at the resulting web page is clearly not automated. Also, a test that merely displays a long text report is not automated because it requires a knowledgeable person to read over the report and inspect it for errors.

By automating tests, every developer can run an entire suite of tests by executing a command-line program. The test suite then reports exactly which tests failed and where, so the problems can be fixed immediately.

A key philosophy behind a successful unit testing methodology is the idea that every test must always run at 100 percent success. Now, when programmers change an XSLT stylesheet or XML code and a test suddenly fails, they know that their changes were likely the source of the problem. When "broken" tests are left in the project for long periods of time, developers will probably stop running the testing suite because it becomes too difficult to manually filter through all of the error reports caused by the broken tests.

WARNING: It is important to adopt a policy in which every developer runs the suite of unit tests before checking any modified code into a shared code repository such as CVS.

For XSLT transformations, performing a transformation and then validating against a DTD or Schema is the easiest kind of testing. Once the structure of the result is validated, additional tests can be performed to determine if the semantic content of the result is correct. For instance, a DTD can report that a <firstName> element is present, but it takes additional testing to determine if the content of <firstName> is actually the correct name.

9.2.1.1. An example unit test

Example 9-8 shows how to write a simple test fixture using the JUnit framework. JUnit describes fixtures as a group of unit tests.

Example 9-8. Sample test fixture

package chap9;

import java.io.*;
import java.net.*;
import java.util.*;

// JAXP used for XSLT transformations
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

// JDOM used for XML parsing and validation
import org.jdom.*;
import org.jdom.input.*;

// JUnit classes
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;

/**
 * An example JUnit test. This class performs an XSLT transformation
 * and validates the result.
 */
public class SampleUnitTest extends TestCase {
    private String workingDir;

    // input XML files
    private File aidanXMLFile;
    private File johnXMLFile;

    // a stylesheet that condenses the XML data
    private File condenseXSLTFile;

    // the transformation results
    private File aidanCondensedXMLFile;
    private File johnCondensedXMLFile;

    private TransformerFactory transFact;

    /**
     * All JUnit tests have a constructor that takes the test name.
     */
    public SampleUnitTest(String name) {
        super(name);
    }

    /**
     * Initialization before each test[...] method is called.
     */
    public void setUp( ) {
        // locate a file named test.properties in the chap9 package
        ResourceBundle rb = ResourceBundle.getBundle("chap9.test");
        this.workingDir = rb.getString("chap9.workingDir");

        assertNotNull(workingDir);
        assert("Unable to locate " + this.workingDir,
                new File(this.workingDir).exists( ));

        this.aidanXMLFile = new File(workingDir + File.separator
                + "aidan.xml");
        this.johnXMLFile = new File(workingDir + File.separator
                + "john.xml");
        this.condenseXSLTFile = new File(workingDir + File.separator
                + "condensePerson.xslt");

        this.aidanCondensedXMLFile = new File(this.workingDir + File.separator
                + "aidanCondensed.xml");
        this.johnCondensedXMLFile = new File(this.workingDir + File.separator
                + "johnCondensed.xml");

        this.transFact = TransformerFactory.newInstance( );
    }

    /**
     * Clean up after each test[...] method
     */
    public void tearDown( ) {
        // the transformation results could be deleted here, but the
        // cleanup code is intentionally commented out so the
        // developer can see the generated files:

        // this.aidanCondensedXMLFile.delete( );
        // this.johnCondensedXMLFile.delete( );
    }

    /**
     * An individual unit test.
     */
    public void testTransformWithTemplates( ) throws Exception {
        Templates templates = this.transFact.newTemplates(
                new StreamSource(this.condenseXSLTFile));

        Transformer trans = templates.newTransformer( );

        // do two transformations using the same Transformer
        trans.transform(new StreamSource(this.aidanXMLFile),
                new StreamResult(this.aidanCondensedXMLFile));
        trans.transform(new StreamSource(this.johnXMLFile),
                new StreamResult(this.johnCondensedXMLFile));

        // validate both files
        validateCondensedFile(this.aidanCondensedXMLFile,
                "Aidan Garrett Burke", "6/25/1999");
        validateCondensedFile(this.johnCondensedXMLFile,
                "John Fitzgerald Kennedy", "5/29/1917");
    }

    /**
     * Another unit test.
     */
    public void testTransformer( ) throws Exception {
        Transformer trans = this.transFact.newTransformer(
                new StreamSource(this.condenseXSLTFile));

        trans.transform(new StreamSource(this.aidanXMLFile),
                new StreamResult(this.aidanCondensedXMLFile));

        validateCondensedFile(this.aidanCondensedXMLFile,
                "Aidan Garrett Burke", "6/25/1999");
    }

    // a helper method used by each of the unit tests
    private void validateCondensedFile(File file, String expectedName,
            String expectedBirthDate) {
        try {
            // first do a simple validation against the DTD
            SAXBuilder builder = new SAXBuilder(true); // validate
            Document doc = builder.build(file);

            // now perform some additional checks
            Element nameElem = doc.getRootElement( ).getChild("name");
            assertEquals("Name was not correct",
                    expectedName, nameElem.getText( ));

            Element birthDateElem = doc.getRootElement( ).getChild("birthDate");
            assertEquals("Birth date was not correct",
                    expectedBirthDate, birthDateElem.getText( ));

        } catch (JDOMException jde) {
            fail("XML was not valid: " + jde.getMessage( ));
        }
    }

    /**
     * @return a TestSuite, which is a composite of Test objects.
     */
    public static Test suite( ) {
        // uses reflection to locate each method named test[...]
        return new TestSuite(SampleUnitTest.class);
    }

    /**
     * Allow the unit tests to be invoked from the command line
     * in text-only mode.
     */
    public static void main(String[] args) {
        TestRunner.run(suite( ));
    }
}

First, notice that SampleUnitTest extends from junit.framework.TestCase. Each subclass of TestCase defines a fixture and can contain multiple individual unit tests. Each method that begins with the word "test" is a unit test. All of the private fields in SampleUnitTest are specific to our particular needs and are not part of the JUnit framework.

The constructor takes the name of a unit test as an argument:

public SampleUnitTest(String name) {
    super(name);
}

The name argument is the test method name, and JUnit uses the Java reflection API to locate and instantiate the correct method. As we will see in a moment, this constructor is rarely called directly.

The setUp( ) method is called before each unit test is executed. As expected, this method is used to set up preconditions before a test is executed. Its counterpart is the tearDown( ) method, which is called just after each test is executed. If a fixture contains four unit test methods, then setUp( ) and tearDown( ) will each be called four times.

For our purposes, the setUp( ) method locates all of the files that will be used for XSLT transformations. These include XML input files, the XSLT stylesheet, and the XSLT result targets. It also performs some simple testing:

assertNotNull(workingDir);
assert("Unable to locate " + this.workingDir,
    new File(this.workingDir).exists( ));

These assert( ) methods are part of the JUnit framework, causing test failures when the tested condition is not true.[50] These are the heart of what programmers write when creating unit tests and can be used in any of the test methods or in the setUp( ) and tearDown( ) methods. When an assertion is not true, JUnit reports an error message and the line number where the failure occurred. This is known as a test failure, which is different than a test error. An error is reported when JUnit catches an exception that one of the unit tests throws.

[50] JUnit 3.7 renamed the assert() method assertTrue() to avoid conflicts with the new JDK 1.4 assertion facility.

This first unit test in our example is the testTransformWithTemplates( ) method. Because this method name begins with "test," JUnit can use reflection to locate it. The job of this test is to merely perform an XSLT transformation using JAXP's Templates interface, delegating to the validateCondensedFile( ) method to do the actual testing. This approach is taken because the same testing code can be shared among a group of individual unit tests.

The validateCondensedFile( ) method performs two levels of testing. First, the result of the transformation is validated against its DTD. If an exception is thrown, the test fails:

fail("XML was not valid: " + jde.getMessage( ));

JUnit will intercept this failure and display the error message to the programmer running the test. If the validation succeeds, the unit test then uses the assertEquals( ) method to test some of the actual XML content:

assertEquals("Name was not correct",
        expectedName, nameElem.getText( ));

In this method, if the second two arguments are not equal, the provided error message is displayed and the test fails.

One key additional method is suite( ):

public static Test suite( ) {
    // uses reflection to locate each method named test[...]
    return new TestSuite(SampleUnitTest.class);
}

This is useful because it automatically locates all methods whose names begin with "test" and adds them to a test suite. Both TestCase and TestSuite implement the Test interface; TestSuite is a composite of many individual Test objects. By organizing tests into suites, entire families of tests can be executed by running the suite. As expected with a composite pattern, test suites can also consist of other test suites. At some point, one top-level test suite can directly or indirectly include every other test in the application. Therefore, all tests can be executed with a single command.

9.2.1.2. Running the test

To run the test from the command line, type the following command:

java chap9.SampleUnitTest

This works because the fixture contains the following main( ) method:

public static void main(String[] args) {
    TestRunner.run(suite( ));
}

The TestRunner class is a command-line tool that reports the following output when all tests succeed:

Time: 1.081

OK (2 tests)

The two dots in the first line of output represent each of the test methods. As each unit test executes, a new dot appears. If a test fails, JUnit reports a stack trace, a (sometimes) descriptive message, and the line number of the failure. At the end, the number of tests, failures, and errors are reported.

JUnit also has a Swing GUI client that can be executed with the following command:

java junit.swingui.TestRunner chap9.SampleUnitTest

Figure 9-4 shows the graphical output when errors occur.

Figure 9-4. JUnit output with errors

The rectangular area to the left of "U" is a progress bar that expands as tests are executed. When dozens or hundreds of tests are executed, the progress bar gives a good visual indication of how many tests have executed. It also changes from green to red when errors or failures occur, so programmers know exactly when something went wrong. The scrolling list in the middle of the screen shows individual test errors and failures, and the text area near the bottom of the screen shows details for the selected error.

The GUI interface is great for interactive testing, while the command-line interface is more appropriate for batch-mode, automated tests. These are the sorts of tests that execute as part of a nightly build process. We now move past unit testing software into the realm of custom application error handling using JAXP error listeners.

9.2.2. JAXP 1.1 Error Listeners

When performing XSLT transformations using JAXP, errors are typically reported to System.err. While this is sufficient for command-line transformations, some custom applications require more control over the error-reporting process. For this class of applications, the javax.xml.transform.ErrorListener interface is provided.

By implementing this interface, an application can capture and report detailed information about where transformation errors occur and why. In Example 9-9, a custom Swing table model is presented. This class implements the javax.xml.transform.ErrorListener interface and is used by a JTable to display errors graphically. Later, in Example 9-11, we show how to register this error listener with a TransformerFactory and Transformer.

Example 9-9. ErrorListenerModel

package com.oreilly.javaxslt.swingtrans;

import java.io.*;
import java.util.*;
import javax.swing.table.*;

// XML-related imports
import javax.xml.transform.ErrorListener;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;

/**
 * A JTable data model that provides detail information about a list
 * of javax.xml.transform.TransformerException objects.
 */
public class ErrorListenerModel extends AbstractTableModel
        implements ErrorListener {

    // column positions in the table
    private static final int LINE_COL = 0;
    private static final int COLUMN_COL = 1;
    private static final int PUBLIC_ID_COL = 2;
    private static final int SYSTEM_ID_COL = 3;
    private static final int MESSAGE_AND_LOC_COL = 4;
    private static final int LOCATION_COL = 5;
    private static final int EXCEPTION_COL = 6;
    private static final int CAUSE_COL = 7;

    private static final String[] COLUMN_NAMES = {
        "Line",
        "Column",
        "Public ID",
        "System ID",
        "Message & Location",
        "Location",
        "Exception",
        "Cause"
    };

    // the actual data
    private List exceptionList = null;

    /**
     * @return a detailed text report of the exception at the specified row.
     */
    public String getDetailReport(int row) {
        if (this.exceptionList == null
                || row < 0 || row >= this.exceptionList.size( )) {
            return "";
        }

        TransformerException te = (TransformerException)
                this.exceptionList.get(row);
        SourceLocator loc = te.getLocator( ); // may be null

        // buffer the report
        StringWriter sw = new StringWriter( );
        PrintWriter pw = new PrintWriter(sw);

        pw.println(te.getClass().getName( ));
        pw.println("-----------------------------------------------------");
        if (loc == null) {
            pw.println("Line Number  : [null SourceLocator]");
            pw.println("Column Number: [null SourceLocator]");
            pw.println("Public ID    : [null SourceLocator]");
            pw.println("System ID    : [null SourceLocator]");
        } else {
            pw.println("Line Number  : " + loc.getLineNumber( ));
            pw.println("Column Number: " + loc.getColumnNumber( ));
            pw.println("Public ID    : " + loc.getPublicId( ));
            pw.println("System ID    : " + loc.getSystemId( ));
        }

        pw.println("Message & Location : " + te.getMessageAndLocation( ));
        pw.println("Location           : " + te.getLocationAsString( ));

        pw.println("Exception          : " + te.getException( ));
        if (te.getException( ) != null) {
            te.getException( ).printStackTrace(pw);
        }

        pw.println("Cause              : " + te.getCause( ));
        if (te.getCause() != null && (te.getCause() != te.getException( ))) {
            te.getCause( ).printStackTrace(pw);
        }

        return sw.toString( );
    }

    /**
     * Part of the TableModel interface.
     */
    public Object getValueAt(int row, int column) {
        if (this.exceptionList == null) {
            return "No errors or warnings";
        } else {
            TransformerException te = (TransformerException)
                    this.exceptionList.get(row);
            SourceLocator loc = te.getLocator( );

            switch (column) {
            case LINE_COL:
                return (loc != null)
                        ? String.valueOf(loc.getLineNumber( )) : "N/A";
            case COLUMN_COL:
                return (loc != null)
                        ? String.valueOf(loc.getColumnNumber( )) : "N/A";
            case PUBLIC_ID_COL:
                return (loc != null) ? loc.getPublicId( ) : "N/A";
            case SYSTEM_ID_COL:
                return (loc != null) ? loc.getSystemId( ) : "N/A";
            case MESSAGE_AND_LOC_COL:
                return te.getMessageAndLocation( );
            case LOCATION_COL:
                return te.getLocationAsString( );
            case EXCEPTION_COL:
                return te.getException( );
            case CAUSE_COL:
                return te.getCause( );
            default:
                return "[error]"; // shouldn't happen
            }
        }
    }

    /**
     * Part of the TableModel interface.
     */
    public int getRowCount( ) {
        return (this.exceptionList == null) ? 1 :
                this.exceptionList.size( );
    }

    /**
     * Part of the TableModel interface.
     */
    public int getColumnCount( ) {
        return (this.exceptionList == null) ? 1 :
                COLUMN_NAMES.length;
    }

    /**
     * Part of the TableModel interface.
     */
    public String getColumnName(int column) {
        return (this.exceptionList == null)
                ?  "Transformation Problems"
                : COLUMN_NAMES[column];
    }

    /**
     * @return true if any errors occurred.
     */
    public boolean hasErrors( ) {
        return this.exceptionList != null;
    }

    /**
     * This is part of the javax.xml.transform.ErrorListener interface.
     * Indicates that a warning occurred. Transformers are required to
     * continue processing after warnings, unless the application
     * throws TransformerException.
     */
    public void warning(TransformerException te) throws TransformerException {
        report(te);
    }

    /**
     * This is part of the javax.xml.transform.ErrorListener interface.
     * Indicates that a recoverable error occurred.
     */
    public void error(TransformerException te) throws TransformerException {
        report(te);
    }

    /**
     * This is part of the javax.xml.transform.ErrorListener interface.
     * Indicates that a non-recoverable error occurred.
     */
    public void fatalError(TransformerException te) throws TransformerException {
        report(te);
    }

    // adds the exception to exceptionList and notifies the JTable that
    // the content of the table has changed.
    private void report(TransformerException te) {
        if (this.exceptionList == null) {
            this.exceptionList = new ArrayList( );
            this.exceptionList.add(te);
            fireTableStructureChanged( );
        } else {
            this.exceptionList.add(te);
            int row = this.exceptionList.size( )-1;
            super.fireTableRowsInserted(row, row);
        }
    }
}

Code related to the ErrorListener interface is emphasized; the remaining code is used to present errors in a Swing table. The Swing JTable component displays rows and columns of data, getting its information from an underlying javax.swing.table.TableModel interface. javax.swing.table.AbstractTableModel is an abstract class that implements TableModel, serving as the base class for application-defined table models as shown here. As you can see, ErrorListenerModel extends from AbstractTableModel.

Since our table model implements the ErrorListener interface, it can be attached to a JAXP Transformer. When transformation problems occur, warning( ), error( ), or fatalError( ) is called. Since these methods have the same signature, they all delegate to the report( ) method. Comments in the code indicate which types of problems are supposed to call each method, although XSLT processors are not consistent in the way they report errors.

The report( ) method simply adds the TransformerException object to a private list of exceptions and then triggers a Swing event to indicate that the JTable should redraw its contents. When the JTable receives this event, it asks the ErrorListenerModel for the row count, column count, and values at individual cells within the table model. This functionality is contained within the getRowCount( ), getColumnCount( ), and getValueAt( ) methods, all of which are defined in the TableModel interface.

Our class also has an additional method called getDetailReport( ), which is used to produce a text report of a TransformerException object. This method is worth studying because it shows which methods are available when dealing with transformation problems. As the code in Example 9-9 shows, many of the fields may be null. Some XSLT processors may provide a lot of detailed error reporting, while others may simply leave these fields null.

9.2.3. A Custom XSLT Transformer GUI

In this section, an XSLT transformer GUI is developed. This is a simple Swing application that allows an XML file to be transformed using an XSLT stylesheet. The results of the transformation are then shown in a text area, along with a JTable that shows all errors using the ErrorListenerModel class shown in Example 9-9.

XML validation of the transformation result is also provided. Given that the stylesheet produces XML, this tool will attempt to parse and validate the result tree. This is a great way to make sure that your XSLT is producing valid XHTML, for example, because the result can be validated against one of the XHTML DTDs as soon as each transformation is performed.

The Ant build file shown earlier in Example 9-3 contains a "run" target that can be used to execute this application by typing ant run.

9.2.3.1. Screen shots

The first window to appear is shown in Figure 9-5. This window is always displayed and allows the user to select the XML and XSLT input files.[51]

[51] These screen shots show the Macintosh OS/X Aqua look and feel using Java Swing.

Figure 9-5. SwingTransformer frame

When the Transform button is clicked, the window shown in Figure 9-6 appears. Subsequent transformations can be performed, and each one will cause an additional window to appear. Since the XML and XSLT files are parsed with each transformation, this application does not have to be restarted to see changes to those files.

Figure 9-6. XML output panel

The first tab, Text Output, is actually the first one to be displayed. It is not shown here because it merely shows the raw text contents of the XSLT result tree, which includes any whitespace produced by the transformation. When the user clicks on the XML Output tab, the result tree is parsed and validated against its DTD. The XML is then displayed in the GUI using JDOM's XMLOutputter class, which removes ignorable whitespace and pretty-prints the XML.

If errors occur during the transformation process, the text and XML output panels are blank. Instead, the user is presented with the display shown in Figure 9-7.

Figure 9-7. Transformation problems

This display shows how the ErrorListenerModel presented in Example 9-9 is used. The JTable at the top of the screen shows a tabular view of all errors, and the text area at the bottom of the screen shows the output from ErrorListenerModel's getDetailReport( ) method. In the error shown, a select attribute was intentionally misspelled in an XSLT stylesheet as seelect.

9.2.3.2. Source code

The source code for the main window is shown in Example 9-10. This is a subclass of JFrame that allows the user to select XML and XSLT filenames. This class is almost entirely GUI-related code and is not discussed further.

Example 9-10. SwingTransformer.java

package com.oreilly.javaxslt.swingtrans;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;

/**
 * The entry point into this application. This class displays the main
 * window, allowing the user to select an XML file and an XSLT file.
 */
public class SwingTransformer extends JFrame {
    private JTextField xmlFileFld = new JTextField(30);
    private JTextField xsltFileFld = new JTextField(30);

    // file filters used with the JFileChooser class
    private XMLFileFilter xmlFilter = new XMLFileFilter( );
    private XSLTFileFilter xsltFilter = new XSLTFileFilter( );
    private JFileChooser fileChooser = new JFileChooser( );

    // actions are hooked up to the JButtons
    private Action loadXMLAction =
            new javax.swing.AbstractAction("Select XML") {
        public void actionPerformed(ActionEvent evt) {
            selectXMLFile( );
        }
    };

    private Action loadXSLTAction =
            new javax.swing.AbstractAction("Select XSLT") {
        public void actionPerformed(ActionEvent evt) {
            selectXSLTFile( );
        }
    };

    private Action transformAction =
            new javax.swing.AbstractAction("Transform") {
        public void actionPerformed(ActionEvent evt) {
            File xmlFile = new File(xmlFileFld.getText( ));
            File xsltFile = new File(xsltFileFld.getText( ));

            if (!xmlFile.exists() || !xmlFile.canRead( )) {
                showErrorDialog("Unable to read XML file");
                return;
            }
            if (!xsltFile.exists() || !xsltFile.canRead( )) {
                showErrorDialog("Unable to read XSLT file");
                return;
            }

            // show the results of the transformation in a new window
            new TransformerWindow( ).transform(xmlFile, xsltFile);
        }
    };

    /**
     * The entry point into the application; shows the main window.
     */
    public static void main(String[] args) {
        new SwingTransformer( ).setVisible(true);
    }

    /**
     * Construct the main window and layout the GUI.
     */
    public SwingTransformer( ) {
        super("Swing XSLT Transformer");

        // note: this line requires Java 2 v1.3
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container cp = getContentPane( );
        cp.setLayout(new GridBagLayout( ));

        GridBagConstraints gbc = new GridBagConstraints( );
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridx = GridBagConstraints.RELATIVE;
        gbc.gridy = 0;
        gbc.insets.top = 2;
        gbc.insets.left = 2;
        gbc.insets.right = 2;

        cp.add(new JLabel("XML File:"), gbc);
        gbc.weightx = 1.0;
        cp.add(this.xmlFileFld, gbc);
        gbc.weightx = 0.0;
        cp.add(new JButton(this.loadXMLAction), gbc);

        gbc.gridy++;
        cp.add(new JLabel("XSLT File:"), gbc);
        gbc.weightx = 1.0;
        cp.add(this.xsltFileFld, gbc);
        gbc.weightx = 0.0;
        cp.add(new JButton(this.loadXSLTAction), gbc);

        gbc.gridy++;
        gbc.gridx = 0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.anchor = GridBagConstraints.CENTER;
        gbc.fill = GridBagConstraints.NONE;
        cp.add(new JButton(this.transformAction), gbc);

        pack( );
    }

    /**
     * Show the file chooser, listing all XML files.
     */
    private void selectXMLFile( ) {
        this.fileChooser.setDialogTitle("Select XML File");
        this.fileChooser.setFileFilter(this.xmlFilter);
        int retVal = this.fileChooser.showOpenDialog(this);
        if (retVal == JFileChooser.APPROVE_OPTION) {
            this.xmlFileFld.setText(
                    this.fileChooser.getSelectedFile().getAbsolutePath( ));
        }
    }

    /**
     * Show the file chooser, listing all XSLT files.
     */
    private void selectXSLTFile( ) {
        this.fileChooser.setDialogTitle("Select XSLT File");
        this.fileChooser.setFileFilter(this.xsltFilter);
        int retVal = this.fileChooser.showOpenDialog(this);
        if (retVal == JFileChooser.APPROVE_OPTION) {
            this.xsltFileFld.setText(
                    this.fileChooser.getSelectedFile().getAbsolutePath( ));
        }
    }

    private void showErrorDialog(String msg) {
        JOptionPane.showMessageDialog(this, msg, "Error",
                JOptionPane.ERROR_MESSAGE);
    }
}

/**
 * Used with JFileChooser to only show files ending with .xml or .XML.
 */
class XMLFileFilter extends javax.swing.filechooser.FileFilter {
    public boolean accept(File f) {
        String name = f.getName( );
        return f.isDirectory( ) || name.endsWith(".xml")
                || name.endsWith(".XML");
    }

    public String getDescription( ) {
        return "XML Files";
    }

}

/**
 * Used with JFileChooser to only show files ending with .xslt or .XSLT.
 */
class XSLTFileFilter extends javax.swing.filechooser.FileFilter {
    public boolean accept(File f) {
        String name = f.getName( );
        return f.isDirectory( ) || name.endsWith(".xsl")
                || name.endsWith(".xslt") || name.endsWith(".XSL")
                || name.endsWith(".XSLT");
    }

    public String getDescription( ) {
        return "XSLT Files";
    }
}

The next class, shown in Example 9-11, creates the window shown in Figures 9-5 and 9-6. Much of this code is responsible for arranging the JTabbedPane component, which contains the three folder tabs that make up the window. This class also does the actual XSLT transformation; these lines are emphasized.

Example 9-11. TransformerWindow.java

package com.oreilly.javaxslt.swingtrans;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;

// XML-related imports
import javax.xml.transform.SourceLocator;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

/**
 * A secondary JFrame that shows the result of a single XSLT
 * transformation. This frame has a JTabbedPane interface, showing
 * the transformation result, error messages, and the XML output.
 */
public class TransformerWindow extends JFrame {
    // the result of the XSLT transformation as text
    private String resultText;

    private JTabbedPane tabPane = new JTabbedPane( );
    private JTextArea textOutputArea = new JTextArea(30, 70);
    private XMLOutputPanel xmlOutputPanel = new XMLOutputPanel( );
    private ErrorListenerModel errModel = new ErrorListenerModel( );
    private JTable errorTable = new JTable(this.errModel);
    private JTextArea errorDetailArea = new JTextArea(10, 70);
    private String xsltURL;

    /**
     * Construct a new instance and layout the GUI components.
     */
    public TransformerWindow( ) {
        super("XSLT Transformation");

        // add the tab pane to the frame
        Container cp = getContentPane( );
        cp.add(this.tabPane, BorderLayout.CENTER);

        // add individual tabs
        this.tabPane.add("Text Output", new JScrollPane(this.textOutputArea));
        this.tabPane.add("Transformation Problems",
                createErrorPanel( ));
        this.tabPane.add("XML Output", this.xmlOutputPanel);

        // listen to new tab selections
        this.tabPane.addChangeListener(new ChangeListener( ) {
            public void stateChanged(ChangeEvent evt) {
                tabChanged( );
            }
        });

        this.textOutputArea.setEditable(false);

        // listen to selection changes on the table of errors
        this.errorTable.getSelectionModel( ).addListSelectionListener(
            new ListSelectionListener( ) {
                public void valueChanged(ListSelectionEvent evt) {
                    if (!evt.getValueIsAdjusting( )) {
                        showErrorDetails( );
                    }
                }
            });
        pack( );
    }

    /**
     * Show details for the currently selected error.
     */
    private void showErrorDetails( ) {
        int selRow = this.errorTable.getSelectedRow( );
        this.errorDetailArea.setText(this.errModel.getDetailReport(selRow));
    }

    /**
     * Perform an XSLT transformation.
     */
    public void transform(File xmlFile, File xsltFile) {
        setVisible(true);
        try {
            // figure out the directory of the XSLT file. This will be
            // used to locate the DTD
            if (xsltFile != null) {
                File xsltDir = xsltFile.getParentFile( );
                if (xsltDir.isDirectory( )) {
                    this.xsltURL = xsltDir.toURL().toExternalForm( );
                }
            }

            TransformerFactory transFact = TransformerFactory.newInstance( );

            // register the table model as an error listener
            transFact.setErrorListener(this.errModel);

            Transformer trans = transFact.newTransformer(
                    new StreamSource(xsltFile));

            // check for null, because the factory might not throw
            // exceptions when the call to newTransformer( ) fails. This
            // is because we registered an error listener that does not
            // throw exceptions.
            if (trans != null) {
                trans.setErrorListener(this.errModel);

                // capture the result of the XSLT transformation
                StringWriter sw = new StringWriter( );
                trans.transform(new StreamSource(xmlFile),
                        new StreamResult(sw));

                // show the results
                this.resultText = sw.toString( );
                this.textOutputArea.setText(this.resultText);
            }

        } catch (TransformerConfigurationException tce) {
            try {
                this.errModel.fatalError(tce);
            } catch (TransformerException ignored) {
            }
        } catch (TransformerException te) {
            try {
                this.errModel.fatalError(te);
            } catch (TransformerException ignored) {
            }
        } catch (Exception unexpected) {
            System.err.println(
                    "The XSLT processor threw an unexpected exception");
            unexpected.printStackTrace( );
        }

        // show the error tab
        if (this.errModel.hasErrors( )) {
            this.tabPane.setSelectedIndex(1);
        }
    }

    // the user clicked on a different tab
    private void tabChanged( ) {
        try {
            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            int selIndex = this.tabPane.getSelectedIndex( );
            String selTab = this.tabPane.getTitleAt(selIndex);

            // when the XML tab is selected, set the text on the XML panel.
            // Although the text may not be XML, we won't know that until
            // it is parsed.
            if ("XML Output".equals(selTab)) {
                this.xmlOutputPanel.setXML(this.resultText,
                        this.xsltURL);
            }
        } finally {
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
    }

    // a helper method to create the panel that displays errors
    private JComponent createErrorPanel( ) {
        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        this.errorTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        int size = this.errorDetailArea.getFont().getSize( );
        this.errorDetailArea.setEditable(false);
        this.errorDetailArea.setFont(
                new Font("Monospaced", Font.PLAIN, size+2));

        splitPane.setTopComponent(new JScrollPane(this.errorTable));
        splitPane.setBottomComponent(new JScrollPane(this.errorDetailArea));
        return splitPane;
    }
}

As the emphasized code shows, the error listener table model is registered on the TransformerFactory as well as the Transformer instance. In addition to registering the error listener, exceptions also have to be caught because XSLT processors may still throw exceptions and errors even though an error listener is registered. Generally, errors on the TransformerFactory indicate problems while parsing the XSLT stylesheet, while Transformer error listeners are notified of problems with the actual transformation or in the XML data.

The final class, XMLOutputPanel, is shown in Example 9-12.

Example 9-12. XMLOutputPanel.java

package com.oreilly.javaxslt.swingtrans;

import java.awt.*;
//import java.awt.event.*;
import java.io.*;
import javax.swing.*;

// XML-related imports
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

/**
 * Displays XML text in a scrolling text area. A status label indicates
 * whether or not the XML is well formed and valid.
 */
public class XMLOutputPanel extends JPanel {
    // displays the XML
    private JTextArea xmlArea = new JTextArea(20,70);
    private String xml;
    private JLabel statusLabel = new JLabel( );

    /**
     * Construct the panel and layout the GUI components.
     */
    public XMLOutputPanel( ) {
        super(new BorderLayout( ));
        add(new JScrollPane(this.xmlArea), BorderLayout.CENTER);
        add(this.statusLabel, BorderLayout.NORTH);
    }

    /**
     * @param xml the actual XML data to display.
     * @param uri the location of the XML, thus allowing the parser
     * to locate the DTD.
     */
    public void setXML(String xml, String uri) {
        // return quickly if the XML has already been set
        if (xml == null || xml.equals(this.xml)) {
            return;
        }
        this.xml = xml;

        // use JDOM to parse the XML
        Document xmlDoc = null;
        try {
            // attempt to validate the XML
            SAXBuilder saxBuilder = new SAXBuilder(true);
            xmlDoc = saxBuilder.build(new StringReader(this.xml), uri);
            this.statusLabel.setText("XML is well formed and valid");
        } catch (Exception ignored) {
            // the data is not valid, but we should parse it again
            // to see if it is well formed
        }

        if (xmlDoc == null) {
            try {
                // don't validate
                SAXBuilder saxBuilder = new SAXBuilder(false);
                xmlDoc = saxBuilder.build(new StringReader(this.xml));
                this.statusLabel.setText("XML is well formed, but not valid");
            } catch (Exception ex) {
                this.statusLabel.setText("Data is not well formed XML");

                // show the stack trace in the text area
                StringWriter sw = new StringWriter( );
                ex.printStackTrace(new PrintWriter(sw));
                this.xmlArea.setText(sw.toString( ));
            }
        }

        // if the document was parsed, show it
        if (xmlDoc != null) {
            try {
                // pretty-print the XML by indenting two spaces
                XMLOutputter xmlOut = new XMLOutputter("  ", true);
                StringWriter sw = new StringWriter( );
                xmlOut.output(xmlDoc, sw);
                this.xmlArea.setText(sw.toString( ));
            } catch (Exception ex) {
                this.statusLabel.setText("Data could not be displayed.");

                // show the stack trace in the text area
                StringWriter sw = new StringWriter( );
                ex.printStackTrace(new PrintWriter(sw));
                this.xmlArea.setText(sw.toString( ));
            }
        }
    }
}

XMLOutputPanel is responsible for parsing the result tree to determine if it is well-formed and valid XML. It starts by parsing the text using a validating parser, simply ignoring errors. If no errors occur, the document is well-formed, valid XML and can be displayed in the text area. Otherwise, the document is parsed again, only without any validation. This allows the code to determine if the XML is at least well-formed.

If the document is not well-formed or valid, the parser's stack trace is displayed in the GUI. For many XSLT transformations, the result tree may not be XML, and this message can simply be ignored. When errors do occur, however, this should make it much easier to locate the problems.

Previous | Home | Next
9. Development Environment, Testing, and Performance | Book Index | 9.3. Performance Techniques
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Performance Techniques (Java and XSLT)

 Java and XSLTSearch this book

9.3. Performance Techniques

One common criticism of XSLT is its performance. The overhead of transformation from XML to another format is the price paid for clean separation between data and programming logic, as well as the ability to customize transformations for different clients. In this section, we look at strategies for improving performance without giving up the benefits that XSLT offers.

The actual XSLT transformation is not always the root of performance problems. XML parsers have a significant impact on performance, along with many other factors such as database access time, time spent processing business logic, and network latency.

Obsessing over performance can be a dangerous trap to fall into. Focusing too heavily on optimization techniques often results in code that is difficult or impossible to understand and maintain. From a strictly technical viewpoint, the fastest technology sounds great. From a business viewpoint, time to market and maintainability are often far more important than runtime performance metrics. An application that meets performance requirements and is easy to maintain over the years makes better business sense than a highly tuned, cryptic application that runs fast but cannot be modified because the original author quit the company and nobody can figure out the code.

9.3.1. Stress Testing with JMeter

One good way to measure overall application throughput is to simulate how real users use an application. Apache's JMeter is designed to perform this task for web applications, measuring response time as the number of concurrent users increases. JMeter is written in Java and can display response time graphically as shown in Figure 9-8.

Figure 9-8. JMeter output

It should be noted that this is the output from Version 1.6 Alpha of JMeter, so later versions may change significantly. The GUI interface has been completely rewritten for Version 1.6, and many features are unfinished as this book is being written. On this display, the dots represent actual response times, the top line represents the average response time, and the bottom line represents the standard deviation from average.

Of the measurements shown on the graph, the average response time is the most useful. The numbers to the right are in milliseconds, so we can see that the average response time for this web application is 151 milliseconds. When using a tool such as JMeter, it is best to leave the stress test running for several minutes until the average response time stabilizes. It generally takes much longer for the first few tests to run because Java is loading classes, starting threads, and allocating memory. Over time, performance will fluctuate as the garbage collector runs and VMs such as HotSpot optimize code. Unless the application has memory leaks or simply cannot keep up with demand, the response time should eventually level off.

Table 9-3 shows how the View Month page from the discussion forum example in Chapter 7, "Discussion Forum" fared when tested with JMeter. In this table, worst case and best case scenarios are shown. Other combinations of servlet container, XSLT processor, and database are not shown. The number of threads in the fourth column indicates the number of simulated users. In the final column, lower numbers are better.

Table 9-3. View month response time

Servletcontainer | XSLTprocessor | Database | Threads | Average responsetime (ms)
Tomcat 3.2.1 | Xalan 2.0 | Access 2000 | 1 | 130
'' | '' | '' | 5 | 320
'' | '' | '' | 10 | 760
'' | '' | '' | 20 | 1600
Tomcat 4.0 | SAXON 6.2.2 | MySQL | 1 | 18
'' | '' | '' | 5 | 150
'' | '' | '' | 10 | 320
'' | '' | '' | 20 | 610

This table does not paint the complete picture. However, it should illustrate the point that in many cases, merely changing to different tools and libraries can dramatically improve performance. For the measurements shown, JMeter was instructed to simulate up to 20 concurrent users with absolutely zero delay between requests. JMeter can also be configured to insert fixed or random delays between requests, which tends to be much more representative of real-world conditions.

The goal of this test was not to benchmark the performance of the discussion forum, but rather to compare the relative performance when switching to different tools.[52] Over a period of many hours, the discussion forum was restarted with various combinations of servlet containers, XSLT processors, and databases. Although Tomcat 4.0 and SAXON 6.2.2 tend to be slightly faster than Tomcat 3.2.1 and Xalan 2.0, the most dramatic performance delta occurs when switching from Microsoft Access to MySQL.

[52] Because XSLT processors and servlet containers are changing so fast, the actual tool names are not listed here.

9.3.2. More Detailed Measurements

In the previous example, the overall performance of an application was measured with Apache's JMeter. This is a great way to prove that an application scales well under stress, and it allows for comparisons between different software, hardware, and database configurations. If you reach a point where an application is simply not scaling as required, however, additional techniques must be employed to isolate bottlenecks within Java code. Tools such as JMeter show performance from an end user's perspective, rather than on a per-method basis within a Java application.

9.3.2.1. Commercial options

One approach is to purchase a commercial profiling tool such as Sitraka's JProbe, available from http://www.sitraka.com, or VMGear's OptimizeIt from http://www.vmgear.com. Among other things, these tools can report how many times each method in an application is called, how much time is spent in each method, and how much memory is allocated. The huge advantage of tools such as these is their unique ability to tell you exactly where the hotspots are within Java code. In general, a small fraction of methods consumes a vast majority of resources.[53] Optimizing these specific methods often yields the biggest performance gains with the least amount of effort.

[53] Typically referred to as the 80/20 rule.

9.3.2.2. JVMPI

Sun's Java 2 SDK offers a set of command-line options that enable the Java Virtual Machine Profiling Interface (JVMPI), which can write detailed profiling information to a log file as an application runs. Example 9-13 shows the Help page reported by typing the command: java -Xrunhprof:help.

Example 9-13. JVMPI command-line options

C:\>java -Xrunhprof:help
Hprof usage: -Xrunhprof[:help]|[<option>=<value>, ...]

Option Name and Value   Description             Default
---------------------   -----------             -------
heap=dump|sites|all     heap profiling          all
cpu=samples|times|old   CPU usage               off
monitor=y|n             monitor contention      n
format=a|b              ascii or binary output  a
file=<file>             write data to file      java.hprof(.txt for ascii)
net=<host>:<port>       send data over a socket write to file
depth=<size>            stack trace depth       4
cutoff=<value>          output cutoff point     0.0001
lineno=y|n              line number in traces?  y
thread=y|n              thread in traces?       n
doe=y|n                 dump on exit?           y

Example: java -Xrunhprof:cpu=samples,file=log.txt,depth=3 FooClass

Although this is only an experimental feature of the JVM, it is useful when other tools are not available. To locate processor bottlenecks, the cpu option should be set to samples, which uses statistical estimates based on periodic samples of performance. It defaults to off because this feature can significantly decrease performance.

Example 9-14 lists a very small portion of the output from a single run of a Java application using the JVMPI feature. This section ranks the methods according to which consume the most time. For instance, a method that takes 20 ms to execute but is called millions of times will probably rank very high on this list, while a method that consumes one second but is only called once will be much further down on the list.

Example 9-14. Partial JVMPI output

rank   self  accum   count trace method
   1 13.70% 13.70%      20    31 java.lang.ClassLoader.defineClass0
   2  7.53% 21.23%      11    19 java.util.zip.ZipFile.getEntry
   3  5.48% 26.71%       8    35 java.io.Win32FileSystem.getBooleanAttributes
   4  4.11% 30.82%       6    26 java.util.zip.ZipFile.read
   5  3.42% 34.25%       5    92 java.util.zip.Inflater.inflateBytes
   6  3.42% 37.67%       5     6 java.lang.ClassLoader.findBootstrapClass
   7  2.74% 40.41%       4    22 java.util.zip.ZipFile.getEntry
   8  2.74% 43.15%       4   143 org.apache.xalan.templates
                                    StylesheetRootnewTransformer
   9  2.74% 45.89%       4    14 java.util.zip.ZipFile.open
  10  1.37% 47.26%       2     4 java.net.URLClassLoader.defineClass

The actual file will grow to many megabytes in size, depending on how large an application is and how long the profiler runs. As expected, a difficult task is filtering through a file of this size to find bottlenecks that are actually caused by an application's code rather than by the Java class libraries.

A majority of the JVMPI output file consists of stack traces. A number identifies each trace, and the depth command-line option affects how many lines are displayed for each stack trace. The fifth column of data in Example 9-14 contains the trace number, making it possible to search through the file for the actual stack trace:

TRACE 31:
    java.lang.ClassLoader.defineClass0(ClassLoader.java:Native method)
    java.lang.ClassLoader.defineClass(ClassLoader.java:486)
    java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
    java.net.URLClassLoader.defineClass(URLClassLoader.java:248)
    java.net.URLClassLoader.access$100(URLClassLoader.java:56)
    java.net.URLClassLoader$1.run(URLClassLoader.java:195)

By making the stack trace depth larger, it is more likely that some of your code will show up somewhere in the report. This makes the report much larger, however. These traces are useful because they show which methods are called leading up to each hotspot.

Companies that have the budget to do so should definitely consider purchasing a tool such as JProbe or OptimizeIt. Although the JVMPI interface offers much of the same functionality that these tools offer, JVMPI output is entirely textual, requiring quite a bit of manual detective work to isolate problems. The commercial profiling tools also require some analysis but present the results in a graphical format that is substantially easier to navigate.

9.3.2.3. Unit testing

The effectiveness of simpler approaches to measurement should not be dismissed. Often, the easiest technique is to simply write a 15-line Java program that tests a specific piece of functionality. For instance, you might want to start with a short Java program that performs an XSLT transformation as shown in Chapter 5, "XSLT Processing with Java". Next, use System.currentTimeInMillis( ) to measure the time immediately before and after each transformation. Then simply experiment with different XSLT stylesheets to see which approaches offer the best performance.

Similar standalone tests can be written for various database access routines, key pieces of business logic, and code that generates XML. Since JUnit reports the time spent in each unit test, you may want to combine these performance metrics with meaningful unit tests.

Regardless of how these individual test routines are written, it is critical that the first runs are ignored. This is because the results will be greatly skewed by Java class loading and initialization time. A good approach is to execute the unit test once before recording the system time. Then execute the test many thousands of times, recording the overall time at the end of the runs. The average response time, calculated by dividing the total time by the number of runs, will be much more accurate than taking a few isolated measurements.

Another point to consider is caching. In a real application, data may change with every single request, making it difficult to cache the transformation result. Unit tests that repeatedly transform the same file are not a good representation of real-world behavior because the processor may cache the transformation result and report artificially high performance.

9.3.3. Using XSLT Processors Effectively

Measuring performance is the first step towards making Java and XSLT applications faster. Once the bottlenecks have been located, it is time to fix the problems.

9.3.3.1. Stylesheet caching

As mentioned several times in this book, caching XSLT stylesheets is an essential performance technique. JAXP includes the Templates interface for this purpose, and we already saw the implementation of a stylesheet cache in Chapter 5, "XSLT Processing with Java". Table 9-4 illustrates the performance gains seen when using the Templates interface to transform a small XML file repeatedly. For this test, the same transformation is performed 100 times using various programming techniques.

Table 9-4. Benefits of caching

Processor | No templates | Templates | Templates and cached XML
Xalan 2.0 | 71.8ms | 45.9ms | 39.2ms
SAXON 6.2.2 | 52.7ms | 37.3ms | 34.2ms

In the "No templates" column, the Templates interface was not used for transformations. As you can see, this resulted in the slowest performance because the stylesheet had to be parsed from a file with each transformation. In the next column, a Templates instance was created once and reused for each transformation. As you can see, the performance increased substantially.

In the final column of the table, the XML data was read into memory and cached as a DOM Document. Instead of reparsing the XML file with each request, the same DOM tree was cached and reused for each of the transformations. This yielded a slight performance gain because the XML file did not have to be read from the file system with each transformation.

Although these results seem to imply that SAXON is faster than Xalan, this may be a faulty assumption. Performance can vary greatly depending on how large the input files are and which features of XSLT are used. It is wise to test performance with your application before choosing one set of tools over another.

9.3.3.2. Result caching

When the XML is highly dynamic and changes with each request, XSLT caching may be the best one can hope for. But when the same data is requested repeatedly, such as on the home page for your company, it makes sense to cache the result of the transformation rather than the XSLT stylesheet. This way, the transformation is performed only when the XML or XSLT actually change.

Example 9-15 presents a utility class that caches the results of XSLT transformations. In this implementation, both the XML data and XSLT stylesheet must come from static files. If the timestamp of either file changes, the transformation is performed again. Otherwise, a cached copy of the transformation result is returned to the caller.

Example 9-15. ResultCache.java

package com.oreilly.javaxslt.util;

import java.io.*;
import java.util.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * A utility class that caches XSLT transformation results in memory.
 *
 * @author Eric M. Burke
 */
public class ResultCache {
    private static Map cache = new HashMap( );

    /**
     * Flush all results from memory, emptying the cache.
     */
    public static synchronized void flushAll( ) {
        cache.clear( );
    }

    /**
     * Perform a single XSLT transformation.
     */
    public static synchronized String transform(String xmlFileName,
            String xsltFileName) throws TransformerException {

        MapKey key = new MapKey(xmlFileName, xsltFileName);

        File xmlFile = new File(xmlFileName);
        File xsltFile = new File(xsltFileName);

        MapValue value = (MapValue) cache.get(key);
        if (value == null || value.isDirty(xmlFile, xsltFile)) {
            // this step performs the transformation
            value = new MapValue(xmlFile, xsltFile);
            cache.put(key, value);
        }

        return value.result;
    }

    // prevent instantiation of this class
    private ResultCache( ) {
    }

    /////////////////////////////////////////////////////////////////////
    // a helper class that represents a key in the cache map
    /////////////////////////////////////////////////////////////////////
    static class MapKey {
        String xmlFileName;
        String xsltFileName;

        MapKey(String xmlFileName, String xsltFileName) {
            this.xmlFileName = xmlFileName;
            this.xsltFileName = xsltFileName;
        }

        public boolean equals(Object obj) {
            if (obj instanceof MapKey) {
                MapKey rhs = (MapKey) obj;
                return this.xmlFileName.equals(rhs.xmlFileName)
                        && this.xsltFileName.equals(rhs.xsltFileName);
            }
            return false;
        }

        public int hashCode( ) {
            return this.xmlFileName.hashCode() ^ this.xsltFileName.hashCode( );
        }
    }

    /////////////////////////////////////////////////////////////////////
    // a helper class that represents a value in the cache map
    /////////////////////////////////////////////////////////////////////
    static class MapValue {
        long xmlLastModified;  // when the XML file was modified
        long xsltLastModified;  // when the XSLT file was modified
        String result;

        MapValue(File xmlFile, File xsltFile) throws TransformerException {
            this.xmlLastModified = xmlFile.lastModified( );
            this.xsltLastModified = xsltFile.lastModified( );

            TransformerFactory transFact = TransformerFactory.newInstance( );
            Transformer trans = transFact.newTransformer(
                    new StreamSource(xsltFile));

            StringWriter sw = new StringWriter( );
            trans.transform(new StreamSource(xmlFile), new StreamResult(sw));

            this.result = sw.toString( );
        }

        /**
         * @return true if either the XML or XSLT file has been
         * modified more recently than this cache entry.
         */
        boolean isDirty(File xmlFile, File xsltFile) {
            return this.xmlLastModified < xmlFile.lastModified( )
                    || this.xsltLastModified < xsltFile.lastModified( );
        }
    }
}

The key to this class is its transform( ) method. This method takes filenames of an XML file and XSLT stylesheet as arguments and returns the transformation result as a String. If any error occurs, a TransformerException is thrown:

public static synchronized String transform(String xmlFileName,
        String xsltFileName) throws TransformerException {

The cache is implemented using a java.util.Map data structure, which requires key/value pairs of data. The MapKey helper class is used as the key:

MapKey key = new MapKey(xmlFileName, xsltFileName);

File xmlFile = new File(xmlFileName);
File xsltFile = new File(xsltFileName);

Next, the value is retrieved from the cache. Another helper class, MapValue, keeps track of the transformation result and when each file was last modified. If this is the first request, the value will be null. Otherwise, the isDirty( ) method determines if either file has been updated:

    MapValue value = (MapValue) cache.get(key);
    if (value == null || value.isDirty(xmlFile, xsltFile)) {
        // this step performs the transformation
        value = new MapValue(xmlFile, xsltFile);
        cache.put(key, value);
    }

    return value.result;
}

As the comment indicates, constructing a new MapValue causes the XSLT transformation to occur. Unless exceptions are thrown, the result of the transformation is returned to the caller.

When compared to the results shown earlier in Table 9-4, this approach to caching is much faster. In fact, the average response time is less than a millisecond once the initial transformation has been performed.

This approach is quite easy to implement for applications based on a collection of static files but is significantly more difficult for database-driven applications. Since more dynamic applications may generate new XML with each invocation, a generic utility class cannot simply cache the result of the transformation. Stale data is the biggest problem with dynamic caching. When the result of an XSLT transformation is stored in memory and the underlying database changes, the cache must be refreshed for users to see the correct data.

Let's suppose that we want to add result caching to the discussion forum application presented in Chapter 7, "Discussion Forum". Since messages cannot be modified once they have been posted, this should be fairly easy to implement for the View Message page. One easy approach is to keep a cache of a fixed number of messages. Whenever a user views a message, the generated web page is added to the cache. If the cache exceeds a specified number of messages, the oldest entries can be flushed.

For more dynamic pages, such as the Month View page, the database must be queried to determine when the most recent message was posted for that particular message board. If the most recently posted message is newer than the cached web page, the transformation must be performed again using the updated data. As you might guess, this sort of caching must be done on a case-by-case basis, because it is very tightly coupled to the database design.

WARNING: Web applications relying on URL rewriting for session tracking may not be able to cache transformation results. This is because, as outlined in Chapter 8, "Additional Techniques", every URL must be dynamically encoded with the jsessionid when cookies are disabled.

As with any other type of optimization, the benefits of caching must be carefully weighed against the costs of added complexity. The best approach is to analyze log files to see which pages are requested most often and to focus optimization efforts there.

9.3.4. Writing Effective XSLT

A big performance hit can be incurred during the XSLT transformation process. For large XML documents in particular, try to avoid situations where large portions of the tree must be processed repeatedly. The // operator can be particularly dangerous:

<xsl:apply-templates select="/.//name"/>

In this statement, the entire document is recursively searched for all <name> elements, beginning at the root. The XSLT processor has no way of knowing where <name> might appear, so it must check every node in the document. If the specific path is known, a more efficient approach is:

<xsl:apply-templates select="/company/employee/name"/>

Variables can also be used to improve performance. For example, key pieces of data may be copied to the result tree several times. Do not do this each time the company owner's name must be displayed:

<xsl:value-of select="/company/owner/name/last"/>
<xsl:text> </xsl:text>
<xsl:value-of select="/company/owner/name/first"/>

Instead, assign the name to a variable once and reuse that variable throughout the stylesheet. This has the added benefit of making the XSLT more readable:

<!-- output the value of the companyOwner variable -->
<xsl:value-of select="$companyOwner"/>

Another common tip is to write inline code wherever possible. Instead of using <xsl:apply-templates> to recursively process XML data, use <xsl:value-of> to directly output the current node. This approach may result in duplicate code, however; a key reason to use templates is to modularize a stylesheet into reusable pieces of functionality. This is a good example of the tradeoff between code maintenance and raw performance that developers are often faced with.

Sorting within the XSLT may introduce performance problems, primarily because the entire node set must be sorted before any content can be output to the result tree. If it is easy to pre-sort the XML content using Java code, the XSLT processor may be able to transform the document using less memory by outputting some of the result tree before the entire document is processed.

Finally, writing smaller XSLT stylesheets is a great way to improve performance. Cascading style sheets (CSS) should be used whenever possible, because the CSS style instructions can be stored in a separate file, thus keeping the XSLT and result tree much smaller. JavaScript functions can also be placed in a separate file, eliminating the need to embed the JavaScript code within the stylesheet.

CSS was used in this manner back in Chapter 7, "Discussion Forum" for the discussion forum example. It is worth mentioning again that CSS is used only for defining styles, such as font colors, indentation, alignment, and colors. Many of these styles can also be defined directly in HTML, for instance:

<h1 align="center">Some Heading</h1>

By defining the alignment in a separate CSS file, however, the HTML is reduced to:

<h1>Some Heading</h1>

Because the HTML is now simplified, the XSLT stylesheet is also simplified. This is why CSS complements XSLT so nicely and should be used whenever possible.

9.3.5. Interacting with EJB

Enterprise JavaBeans (EJB) objects are server-side components that encapsulate business logic and access to data. Because EJBs execute inside of application servers, they are typically accessed remotely using Java Remote Method Invocation (RMI) interfaces. This implies that method calls to EJB components occur over a network connection; they are much slower than local method calls within the same VM. For this reason, care must be taken when sending data to and from the application server.

9.3.5.1. Sending XML from EJBs

From the perspective of Java and XSLT, the critical issue is determining where to produce XML. There are basically two options available. The first is to produce the XML within the EJB container, thus providing a pure XML interface to any client wishing to use the beans. For instance, a bean may have the following additional methods that know how to produce XML:

public String getLeaderXML( ) throws RemoteException;
public String getTeamMembersXML( ) throws RemoteException;
public String getProjectInformation( ) throws RemoteException;

Each of these methods simply returns a String that contains XML content.Figure 9-9 illustrates how this model works.

Figure 9-9. Generating XML in the EJB tier

NOTE: Another variation on this theme is to use a helper class whose sole responsibility is to produce XML from a bean, rather than embedding the XML production directly in the bean.

The EJB component is responsible for generating XML before sending a response to the client. Although DOM is shown in the figure, JDOM or any other XML generation API may be used. The client, whether a servlet container or a standalone Java client, sees XML only from the server.

At one extreme, each of these XML strings is a well-formed XML document. Another option, perhaps more useful, is returning fragments of XML. The client can then assemble many of these XML fragments into more complex XML documents. While this does put slightly more burden on the client to assemble the pieces properly, it does offer significantly more flexibility.

This approach may seem like it offers the cleanest interface to the EJB tier. As long as the structure of the XML remains the same, both client and server can change their internal object models without affecting the other.

WARNING: Many DOM implementations are not comprised of Serializable Java objects. Furthermore, the client may not use the same DOM implementation that the server uses. For these reasons, it is rarely a good idea to try sending DOM trees directly from an EJB to a client.

Producing XML on the EJB tier has significant drawbacks, however. First, the size of XML text is typically quite large. Compression of the text is essential, particularly as the size of the XML gets larger. While compression reduces bandwidth requirements, it increases the processor workload on both client and server for compression and decompression. This is supported by java.util.zip.GZIPInputStream and java.util.zip.GZIPOutputStream.

The second drawback is that 100 percent of an application's business logic can rarely be placed inside of EJB components. Although this is an admirable goal, it means that the client must make a remote network call to the server for every operation. By simply returning XML to the client, it is much harder for some of the business logic to be offloaded to the client machine.

9.3.5.2. Sending objects from EJBs

The second option is to leave XML out of the EJB components altogether. Instead, each bean method returns instances of helper classes such as Employee and ProjectInfo. With this approach, the client can perform some business logic by invoking methods locally on the returned objects. Figure 9-10 illustrates where XML production occurs in this model.

Figure 9-10. Generating XML on the client tier

As shown, the Employee object is serialized, and the serialized object is sent from the EJB container to the client, rather than a big text string of XML. On the client machine, the DOM API is used to convert the Employee into an XML representation, which is fed directly into an XSLT processor. By using DOM on the client, the XML never has to be completely converted to text, which tends to be slightly faster for transformations.

NOTE: While custom XML production using DOM is common today, more automated XML data-binding technologies are becoming increasingly popular. Sun's Java Architecture for XML Data Binding (JAXB) provides a standard Java API for mapping to and from XML documents and Java objects.

Although the bandwidth requirements for serialized objects are comparable to compressed XML, more finely grained requests are easier when returning objects instead of XML text. Consider an online benefit processing application. In this type of application, an employee may have a spouse, children, beneficiaries, and benefit elections. As the user moves from page to page in this application, the Employee object can be cached in the HttpSession. As new pages require additional data, they merely call methods on this cached Employee object:

// somewhere in a servlet...
if (employee.isMarried( )) {
    // request the Spouse, which will make a call to the EJB tier
    // unless the spouse was requested earlier and is cached
    Person spouse = employee.getSpouse( );
    // generate XML for the spouse...
} else {
    // simply generate XML for the employee; do not call the EJB tier
}

As the code fragment shows, when an employee does not have a spouse, no additional call to the EJB tier is required. If the EJB tier returns pure XML, then additional calls to the EJB tier are almost certainly required for each web page that is displayed. This is because the web tier sees only big blocks of XML text from the EJB tier, making it much more difficult to add any business logic to the web tier.

The biggest advantage of returning objects instead of XML is the ability to keep presentation logic out of the EJB tier. Most web applications combine data from several EJBs onto each web page. End user requirements for the user interface tend to change much more frequently than data requirements. It makes sense to keep the EJB tier stable while changing the XML generation code in the web tier more frequently.

Previous | Home | Next
9.2. Testing and Debugging | Book Index | 10. Wireless Applications
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Wireless Applications (Java and XSLT)

 Java and XSLTSearch this book

Chapter 10. Wireless Applications

Contents:

Wireless Technologies
The Wireless Architecture
Java, XSLT, and WML
The Future of Wireless

Cellular phones, personal digital assistants (PDAs), and various other forms of wireless devices are taking the world by storm, opening new avenues for development as well as a whole host of challenges for developers. These devices are tiny compared to PCs, both in terms of physical size and processing power. This means that traditional HTML web pages are unacceptable, opening the door for new markup languages based on XML to take center stage.

This chapter introduces key concepts behind wireless technologies with particular emphasis on Wireless Markup Language (WML), a concise alternative to HTML that is custom made for wireless devices. The role of XSLT and servlets will also be explored though an example application that allows wireless users to browse movie theaters and showtimes.

10.1. Wireless Technologies

The family of technologies used by wireless devices is in its infancy, just a few years old. Because of this, there are a few competing markup languages in use today. In Japan, the i-mode protocol is the market leader. i-mode is defined by a company named NTT DoCoMo, utilizing a subset of HTML known as Compact HTML (cHTML). This markup language shares much of the same syntax as HTML but is essentially proprietary and is not based on XML.

In Europe and the United States, Wireless Access Protocol (WAP) is more popular. WAP currently utilizes WML, which is quite different from HTML and cHTML. In coming years, it is likely that most vendors will consolidate around XHTML Basic, although existing markup languages will need to be supported for quite some time. WML and cHTML will be seen as transitional technologies that allowed us to start experimenting with wireless services. At any rate, XSLT offers an excellent way to minimize risk as vendors work through their differences and technology progresses.

Phone Simulators

A variety of cell phone simulators are illustrated throughout this chapter. All of these tools run on a PC, allowing developers to test various devices without purchasing actual phones and subscribing to wireless Internet access services. Here are some products and URLs to start with:

  • Ericsson WapIDE (http://www.ericsson.com)

  • Motorola Mobile Application Development Kit (http://www.motorola.com)

  • Nokia WAP Toolkit (http://www.nokia.com)

  • Openwave Software Development Kit (http://developer.openwave.com)

10.1.1. Why Not HTML?

HTML is used by nearly every web site and can theoretically be used by wireless devices as well. All that is needed is a web browser, a big display, lots of processing power, and sufficient bandwidth. Maybe not!

10.1.1.1. Problems with HTML

Consider this web page:

<boDY>
 <h1 align=center>Welcome to our home page!</h1
 <ul>
   <LI>first item
   <LI>second item
   <Li>third item

Believe it or not, Netscape 4.x, Netscape 6, and IE 5.5 all display this page perfectly, without a single warning or error. Browsers are incredibly forgiving of errors in HTML, because vendors and end users want these browsers to display all web pages properly. Since only a tiny fraction of web sites are written using well-formed HTML, browsers must be written to silently recover from a wide range of errors. A browser that fails to display poorly written HTML is not acceptable in the marketplace because it fails on so many pages.

Nonprogrammers may view this as a benefit, because it allows them to create web sites without too much concern for capitalization, quotes, or properly nested tags. What they do not realize is that browsers are much more complex than they really need to be. Writing HTML parsers to handle every conceivable form of poorly written HTML results in huge, complex browsers that require far too many resources to execute on a handheld device.

Until all web pages are written using well-formed HTML or XHTML, browsers must support poorly written HTML. Until browsers are more stringent, however, web authors will continue writing sloppy code.

10.1.1.2. Limitations of current devices

Another blow against HTML is the simple fact that current wireless devices have tiny displays, small amounts of memory, and very slow network connections. Devices with five line displays simply cannot display HTML tables, framesets, and large fonts. Instead, these devices demand entirely new markup languages that are far smaller and make more efficient use of available resources.

Previous | Home | Next
9.3. Performance Techniques | Book Index | 10.2. The Wireless Architecture
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Wireless Architecture (Java and XSLT)

 Java and XSLTSearch this book

10.2. The Wireless Architecture

Although wireless users want to access the internet, they cannot simply dial a phone number and expect to browse web sites. This is because wireless devices use protocols based on WAP, rather than traditional web-based protocols such as HTTP. Figure 10-1 shows how a typical web server fits into the wireless architecture.

Figure 10-1. WAP gateways

As shown, a new type of server called a WAP gateway is introduced into the equation. This is a server that converts HTTP traffic into WAP traffic, using protocols appropriate to the given wireless service. The details of this are well beyond the scope of this chapter and are generally unimportant to most developers. This is because cell phone companies and other wireless portals provide these gateways, allowing their customers to access the entire Internet via their wireless devices. Typical web application developers can deploy servlets and WML content onto normal web servers, and the content will be available to wireless users automatically.

Previous | Home | Next
10. Wireless Applications | Book Index | 10.3. Java, XSLT, and WML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Java, XSLT, and WML (Java and XSLT)

 Java and XSLTSearch this book

10.3. Java, XSLT, and WML

Unless a web application is limited to a corporate intranet, it must be designed to support a wide variety of device types. As the upcoming examples will demonstrate, wireless devices are far less consistent than web browsers. This amplifies the need for the clean separation between data and presentation that XML and XSLT offer, because many different presentation styles may be needed to take advantage of specific devices. Java servlets are used to tie everything together, detecting the type of client device and driving the XSLT transformation process.

10.3.1. A WML Example

WML is a relatively new XML-based markup language specifically designed for wireless devices. As such, it is compact, easy to parse, and optimized for small displays. WML is the product of the WAP Forum, an association consisting of over 500 member companies that defines specifications for wireless devices. You can learn more about WML by downloading the specification from http://www.wapforum.org or by reading Learning WML and WMLScript by Martin Frost (O'Reilly).

First of all, WML is an XML-based markup language.[54] This means that, unlike HTML, all WML documents must be well-formed and valid. For instance, all tags must be lowercase and nested properly, and attribute values must be quoted. Example 10-1 lists a WML document.

[54] WML documents are XML documents that conform to one of the WML DTDs.

Example 10-1. A very simple WML page

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
        "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
  <card id="home" title="Name Entry">
    <p>Enter your first name:
      <input name="firstName"/>
    </p>
    <p>Enter your age:
      <input name="age" format="*N"/>
    </p>
    <do type="accept">
      <go href="#hello"/>
    </do>
  </card>
  <card id="hello" title="Hello">
    <p>
    Hello there, $(firstName:e)!
    </p>
    <p>
    You claim to be <em>$(age:e)</em> years
    old...can this possibly be true?
    </p>
    <p>Click <a href="#home">here</a> to
       change your answer.</p>
  </card>
</wml>

This particular WML document adheres to Version 1.1 of the WML specification, as indicated by the document type declaration. Unlike HTML, the root element is <wml> , which is commonly known as a deck. The WML deck contains one or more <card> elements, each of which represents a single screen that is displayed on the device. Grouping cards into decks increases performance because the wireless device can make fewer requests to the server as the user navigates from card to card.

The first card in this example prompts the user to enter his first name and age. Unlike HTML form techniques, these values are stored in the WML variables firstName and age. This is much better than posting a form to the server because of the limited bandwidth available to wireless devices. The age field illustrates another interesting feature of WML that does not exist in HTML:

<input name="age" format="*N"/>

The format attribute shown here indicates that the user can enter any number of numeric digits or decimal points.[55] Formats for date entry, telephone numbers, and other known patterns can also be configured using simple pattern strings defined by the WML specification. This is a big advantage over traditional web techniques that require scripting language support to perform client-side validation. Table 10-1 lists all of the format patterns supported by WML 1.1.

[55] Not all devices support this feature.

Table 10-1. WML format strings

Format

Displays

A

Uppercase letters, symbols, and punctuation characters; not numbers.

a

Lowercase letters, symbols, and punctuation characters; not numbers.

N

Any number.

n

Any number, symbol, or punctuation character.

X

Same as A, but includes numbers.

x

Same as a, but includes numbers.

M

Any character, but the device should try to default to uppercase entry.

m

Any character, but the device should try to default to lowercase entry.

*f

Any number of the specified characters; f is one of the format codes shown in this table. Must appear at the end of the format string.

nf

n is a number from 1 to 9, specifying the number of characters that can be entered. f is one of the format codes shown in this table. Must appear at the end of the format string.

\c

Displays a specific character in the field. For instance, NNN\-NN\-NNNN specifies a data format for U.S. social security numbers, allowing the user to enter a number such as 333-22-4444.

The first card in Example 10-1 finishes with a <do> tag:

<do type="accept">
  <go href="#hello"/>
</do>

This causes a button to appear, which acts like a hyperlink to the #hello URL when the user clicks on it. This is a reference to the second card in the deck. Therefore, the server is not contacted when this particular button is clicked. Figure 10-2 shows how one particular device displays the first and second cards.

Figure 10-2. Cell phone simulator

As you can see in the picture, the second card displays the values that were entered in the first card.[56] Here is some of the code again:

[56] Notice that this particular device does not honor the <em> tag when the age is displayed.

Hello there, $(firstName:e)!

This demonstrates how to utilize WML variables, something that is not possible with HTML. The :e at the end of the variable name is optional and instructs the device to perform URL escaping of the text before displaying it. This is useful if the user enters spaces and other characters such as < that may cause problems with the WML.

Hyperlinks in WML look just like hyperlinks in HTML:

<p>Click <a href="#home">here</a> to

The trickiest part about WML is the wide variety of devices that may be in use. Figure 10-3 shows these same two cards on a cellular phone that has a smaller display.

Figure 10-3. Another cell phone simulator

As you can see, the first card does not fit on the display, so the user has to scroll. On the second card, the phone honors the emphasis (<em>) tag when displaying the age, while the first browser in Figure 10-2 does not. While differences like these should diminish as vendors have more time to implement the complete WML specification, there are no guarantees as to how buttons and <input> fields will be displayed. In many cases, <do> tags are mapped to physical buttons on the cell phone keypad rather than displayed as buttons on the screen.

10.3.1.1. WMLScript and WBMP

Because of bandwidth constraints, scripting capability is critical for wireless devices. WAP defines a scripting language called WMLScript, which is tightly integrated with WML and features a syntax that is very similar to JavaScript. With WMLScript, form validation and lightweight processing can be performed on the client device, greatly reducing the number of requests to a server.

Wireless Bitmap (WBMP) is a very simple image format that, as expected, is optimized for small devices. WBMP files are black and white and can be created with a number of free tools. There is even a free web site at http://www.teraflops.com/wbmp that will convert GIF, JPEG, and BMP images to WBMP using a browser-based interface.

Again, refer to Learning WML & WMLScript for the complete story on these technologies.

10.3.2. Servlets and WML

Servlets are important to wireless developers because they can detect the type of client device. Different XSLT stylesheets can then be selected for regular web browsers, sophisticated PDAs, and simple cell phones.

10.3.2.1. Identifying the client

Detecting the type of client is the most important role of the servlet. There are two HTTP header values that are typically used for this purpose: User-Agent and Accept. The text in Example 10-2 shows what an HTTP header looks like for the Ericsson R520m cell phone simulator.

Example 10-2. Example HTTP header

GET / HTTP/1.1
Host: 25.12.44.22
Accept: application/vnd.wap.wmlc, application/vnd.wap.wbxml,
application/vnd.wap.wmlscriptc, */*, text/vnd.wap.wml, application/xml, text/xml,
text/vnd.wap.wmlscript
User-Agent: EricssonR520/R1A
Accept-Charset: *

The HTTP header is text, and each line after the first consists of a name:value pair. The Accept header indicates the MIME content types that this device knows how to display, so searching for text/vnd.wap.wml is a simple way to detect if the client device supports WML. If the client accepts this MIME type, it could be a wireless device.

WARNING: Some browsers may also know how to display text/vnd.wap.wml. The Accept header is not a completely reliable way to determine the client type.

The User-Agent header definitively identifies the device. However, vendors do not consistently follow standards. Table 10-2 lists several user agents reported by various cell phone simulators.

Table 10-2. Sample user agents

Simulator type | User-Agent
Ericsson R320s | EricssonR320/R1A
Ericsson R380s | R380 2.1 WAP1.1
Ericsson R520m | EricssonR520/R1A
Motorola | Motorola VoxGateway/2.0
Nokia | Nokia-WAP-Toolkit/2.1
Openwave | OWG1 UP/4.1.20a UP.Browser/4.1.20a-XXXX UP.Link/4.1.HTTP-DIRECT

In general, a model number follows the vendor name. However, the Ericsson R380s does not follow this convention. As mentioned in Chapter 8, "Additional Techniques", almost every web browser reports a User-Agent that begins with the text "Mozilla," which can be used to identify a web browser rather than a wireless device.

From the servlet, it is quite easy to get to these HTTP headers:

protected void doGet(HttpServletRequest req, HttpServletResponse res)
        throws IOException, ServletException {
    String userAgent = req.getHeader("User-Agent");
    String accept = req.getHeader("Accept");

    if (userAgent != null) {
        ...

A more complete example is presented in the section "Movie Theater Example."

10.3.2.2. Setting the content type

Once the client type has been identified as either a web browser or a specific type of wireless device, the response must be sent back. Table 10-3 lists the three most common content types a servlet will encounter.

Table 10-3. MIME content types

MIME type | Extension | Description
text/vnd.wap.wml | .wml | WML source code
text/vnd.wap.wmlscript | .wmls | WMLScript source code
image/vnd.wap.wbmp | .wmlc | Wireless Bitmaps

This simply means that before sending a WML response back to the client device, the following code must be present in the servlet:

public void doGet(HttpServletRequest req, HttpServletResponse res) ... {
    res.setContentType("text/vnd.wap.wml");
    // now obtain a PrintWriter or OutputStream and perform
    // the XSLT transformation...

For dynamically generated pages, this is all that must be done. If a web application also consists of static resources such as WMLScript files and WBMP images, the web application deployment descriptor should also be updated. Example 10-3 lists some additional content that should be added to the deployment descriptor.

Example 10-3. Deployment descriptor MIME mappings

  <mime-mapping>
    <extension>.wml</extension>
    <mime-type>text/vnd.wap.wml</mime-type>
  </mime-mapping>
  <mime-mapping>
    <extension>.wmls</extension>
    <mime-type>text/vnd.wap.wmlscript</mime-type>
  </mime-mapping>
  <mime-mapping>
    <extension>.wmlc</extension>
    <mime-type>image/vnd.wap.wbmp</mime-type>
  </mime-mapping>

This effectively tells the web server to use the specified MIME type whenever the client requests files with the listed extensions.

10.3.3. Movie Theater Example

Admittedly, this is a crash course introduction to WML; hopefully a more complete example will clarify some of the concepts.

10.3.3.1. Storyboard

This example consists of three WML decks and several cards. Through this interface, users can select their city, select a particular movie theater within that city, and finally view showtimes for that theater. The diagram in Figure 10-4 contains the storyboard for this application, showing how each screen links to the next.

Figure 10-4. Storyboard

As the illustration indicates, the first deck contains a splash screen that displays for 1.5 seconds. This takes advantage of a WML timer, automatically displaying the city selection page after the timer expires. From this page, the user can select from a list of cities.

The second deck consists of a single card, which shows a list of theaters for the current city. Once the user clicks on a particular city, the third deck is displayed. This deck may have many cards, depending on how many movies are showing in that particular theater. The user can browse from movie to movie without requesting additional data from the server.

For the servlet to dynamically build the appropriate decks and cards, each page requires certain parameters. These parameters are passed along to the XSLT stylesheet so it can select the appropriate data from the XML file. Table 10-4 lists the required parameters for each deck. These will appear in each of the WML files, as well as in the servlet and XSLT stylesheets. If any parameter is invalid or missing, the application merely returns the user to the home page.

Table 10-4. Required parameters

Deck | Parameters | Notes
1 | none | Shows all cities
2 | action=theaters city=city_id | Shows theaters for a single city
3 | action=showtimes city=city_id theater=theater_id | Shows all movies for a specific theater in the given city

10.3.3.2. XML data

To keep things simple for the theater owners, this application produces all pages from a single XML datafile on the server. The DTD for this file is shown in Example 10-4.

Example 10-4. Movie theater DTD

<!ELEMENT movies (moviedef+, city+)>
<!ELEMENT moviedef (shortName, longName)>
<!ELEMENT city (name, theater+)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT shortName (#PCDATA)>
<!ELEMENT longName (#PCDATA)>
<!ELEMENT theater (name, movie+)>
<!ELEMENT movie (times)>
<!ELEMENT times (#PCDATA)>
<!ATTLIST city
  id ID #REQUIRED
>
<!ATTLIST movie
  ref IDREF #REQUIRED
>
<!ATTLIST moviedef
  id ID #REQUIRED
>
<!ATTLIST theater
  id ID #REQUIRED
>

It is worth pointing out the difference between a <moviedef> and <movie> element. Basically, a <moviedef> defines a short and long description for a movie in a single place. Since the same movie is likely to be listed in many different theaters, it makes sense to define the <moviedef> once, and then refer to it from other parts of the document using <movie> elements.

Example 10-5 contains a portion of an example XML datafile that adheres to this DTD. This is the data displayed in the upcoming screen shots.

Example 10-5. Movie theater XML datafile

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/wml/showtimes.xslt"?>
<movies>
  <!-- all movies -->
  <moviedef id="star_wars1">
    <shortName>Star Wars Ep 1</shortName>
    <longName>Star Wars Episode I: The Phantom Menace</longName>
  </moviedef>
  <moviedef id="star_wars4">
    <shortName>Star Wars</shortName>
    <longName>Star Wars: A New Hope</longName>
  </moviedef>
  <moviedef id="star_wars5">
    <shortName>Emp Strikes Back</shortName>
    <longName>The Empire Strikes Back</longName>
  </moviedef>
  ...additional moviedef elements

  <city id="stl">
    <name>St. Louis</name>
    <theater id="westolive16">
      <name>West Olive 16</name>
      <movie ref="star_wars1">
        <times>10:15a, 3:30, 12:30, 5:45, 7:15, 10:30</times>
      </movie>
      <movie ref="star_wars4">
        <times>1:30, 4:00, 6:00</times>
      </movie>
      <movie ref="star_wars5">
        <times>2:30, 4:10, 6:20</times>
      </movie>
      <movie ref="back2future3">
        <times>4:00, 6:00, 8:00, 10:00</times>
      </movie>
    </theater>
    <theater id="stcharles18">
      <name>St. Charles 18</name>
      <movie ref="star_wars4">
        <times>10:15a, 3:30, 12:30, 5:45, 7:15, 10:30</times>
      </movie>
      <movie ref="star_wars5">
        <times>1:30, 4:00, 6:00</times>
      </movie>
      <movie ref="back2future2">
        <times>4:00, 6:00, 8:00, 10:00</times>
      </movie>
    </theater>

    ... additional theater elements
  </city>

  ... additional city elements

</movies>

As you can see in the XML, nothing in the data indicates that the output must be WML. In fact, this application can support both XHTML and WML output via different XSLT stylesheets. Of course, WML support is the primary goal of this application. Therefore, <shortName> is included to support wireless devices. If this were targeted towards only web browsers, this element would not be required.

10.3.3.3. WML prototypes and screen shots

When using XSLT to produce XHTML or WML, it is a good idea to start with prototypes. This is because XSLT adds a level of indirection that makes it hard to visualize the result. It is much easier to simply create static WML first, test it using a simulator, and then develop the XSLT stylesheets once everything is working.

Example 10-6 lists the first WML deck used in this example. As mentioned earlier, this deck contains two cards, the first of which is a splash screen that displays for 1.5 seconds.

Example 10-6. Home page WML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
  <card ontimer="#home" title="ABC Theaters" id="splash">
    <timer value="15"/>
    <p align="center">
      <big>Welcome to ABC Theaters</big>
    </p>
    <p>Mmmm...Popcorn...</p>
    <do type="accept">
      <go href="#home"/>
    </do>
  </card>
  <card newcontext="true" title="Home" id="home">
    <p align="center">Please select your city:
      <select multiple="false" name="city">
        <option value="chi">Chicago</option>
        <option value="stl">St. Louis</option>
        <option value="seb">Sebastopol</option>
      </select>
    </p>
    <p>
      <em>
        <a href="movieguide?action=theaters&amp;city=$(city)">Show Theaters...</a>
      </em>
    </p>
  </card>
</wml>

The ontimer attribute of the first card indicates the URL to load when the <timer> element expires. The timer value is 15, meaning 15 tenths of a second, or 1.5 seconds. This first card also contains a <do> element, allowing the user to click on a button to jump to the home page if she does not want to wait for the timer to expire. Like XHTML, the <p> element indicates a paragraph of text, causing text to appear on the next line of the display.

The next card contains a <select> element, allowing the user to select from a list of cities. The value of the selection is assigned to the city variable, making it easy to submit the information to the server with the <a> tag:

<a href="movieguide?action=theaters&amp;city=$(city)">Show Theaters...</a>

This is actually the final URL used by the finished application, rather than a prototype URL. During the prototyping phase, the following link is more appropriate:

<a href="theaters.wml">Show Theaters...</a>

By using URLs to static WML files, it is at least possible to navigate from page to page before the servlet is written. Figure 10-5 shows how these first two pages look on a cell phone simulator.

Figure 10-5. Home page output

The image to the left shows the splash screen, which is replaced by the image on the right after 1.5 seconds. On this particular phone, the user navigates with the up and down arrows, making selections by clicking on the telephone's YES button.

The next WML page, shown in Example 10-7, shows a list of theaters for the current city. In this example, the list uses a series of hyperlinks. This can also be done using a <select> tag, as shown in the previous example. However, the user can see the entire list when hyperlinks and <br/> tags are used. Of course, on smaller displays the user will typically have to scroll down to see all items.

Example 10-7. Theater listing WML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
    "http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
  <card title="Theaters" id="theaters">
    <p>
      <big>St. Louis</big>
    </p>
    <p>Select a theater:</p>
    <p>
      <a href="movieguide?action=showtimes&amp;city=stl&amp;theater=westolive16">
            West Olive 16</a>
      <br/>
      <a href="movieguide?action=showtimes&amp;city=stl&amp;theater=stcharles18">
            St. Charles 18</a>
      <br/>
      <a href="movieguide?action=showtimes&amp;city=stl&amp;theater=ofallon">
            O'Fallon Cine</a>
      <br/>
    </p>
    <p>
      <em>
        <a href="movieguide">Change city...</a>
      </em>
    </p>
  </card>
</wml>

This WML file is shown on the left side of Figure 10-6 using a different cell phone simulator. On the right side of this figure, an XHTML representation of the same data is shown in a web browser. These images were generated using the same servlet and XML datafile but different XSLT stylesheets.

Figure 10-6. Theater listing output

The final deck is shown in Example 10-8. As mentioned earlier, this consists of several cards, one per movie.

Example 10-8. Showtimes WML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_
1.1.xml">
<wml>
  <template>
    <do name="common_prev" label="Back" type="prev">
      <prev/>
    </do>
  </template>
  <card title="Movies" id="movies">
    <do name="common_prev" type="prev">
      <noop/>
    </do>
    <p>
      <big>O'Fallon Cine</big>
    </p>
    <p>Select a movie:</p>
    <p>
      <a href="#jones1">Raiders Lost Ark</a>
      <br/>
      <a href="#jones2">Temple of Doom</a>
      <br/>
      <a href="#back2future2">Back 2 Future 2</a>
      <br/>
    </p>
    <p>
      <em>
        <a href="movieguide?action=theaters&amp;city=stl">Change theater...</a>
      </em>
    </p>
  </card>
  <card title="Showtimes" id="jones1">
    <p>
      <em>Raiders of the Lost Ark</em>
    </p>
    <p>10:15a, 3:30, 12:30, 5:45, 7:15, 10:30</p>
  </card>
  <card title="Showtimes" id="jones2">
    <p>
      <em>Indiana Jones and The Temple of Doom</em>
    </p>
    <p>1:30, 4:00, 6:00</p>
  </card>
  <card title="Showtimes" id="back2future2">
    <p>
      <em>Back to the Future 2</em>
    </p>
    <p>4:00, 6:00, 8:00, 10:00</p>
  </card>
</wml>

This WML file illustrates how to define and use a <template>, which is a piece of reusable markup that can be shared by all cards in the deck. This particular template defines a Back button displayed on each instance of the Showtimes card, allowing the user to easily return to the list of movies.

Since the Back button should not appear on the movie list card, it is shadowed as follows:

<do name="common_prev" type="prev">
  <noop/>
</do>

The <noop/> element stands for "No Operation" and effectively removes the <do> element defined by the common_prev template. When cards define elements with the same names as templates, the card elements take precedence. The card can choose to modify the behavior of the template or simply suppress it with the <noop/> tag as shown here.

The screen shot shown in Figure 10-7 illustrates how these cards look in a cell phone. As shown, the Back button does not appear in the list of movies but does appear in the Showtimes card.

Figure 10-7. Showtimes WML output

The final screen shot, shown in Figure 10-8, shows how a web browser takes advantage of its large display area by displaying all of the information in a single table. Once again, this is accomplished with a different XSLT stylesheet that converts the XML to XHTML instead of WML.

Figure 10-8. Showtimes XHTML output

Although WML does define a <table> element, it has almost no chance of fitting on a cell phone display and is not widely supported by currently available devices.

10.3.3.4. Servlet implementation

This application uses a single servlet, listed in Example 10-9. This servlet has three primary functions:

  • Parse request parameters and determine which page to display next.

  • Identify the client type.

  • Perform the appropriate XSLT transformation.

Example 10-9. MovieServlet.java

package chap10;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

/**
 * A servlet that shows schedules for movie theaters in various
 * cities. Supports normal web browser clients as well as WAP-enabled
 * PDAs and cell phones.
 */
public class MovieServlet extends HttpServlet {

    // currently supports two types of clients; could be expanded later
    private static final int XHTML_CLIENT_TYPE = 1;
    private static final int WML_CLIENT_TYPE = 2;

    // three pages in this web app
    private static final int HOME_PAGE = 100;
    private static final int THEATERS_PAGE = 101;
    private static final int SHOWTIMES_PAGE = 102;

    /**
     * This servlet supports GET and POST.
     */
    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        doPost(req, res);
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        try {
            String action = req.getParameter("action");
            String city = req.getParameter("city");
            String theater = req.getParameter("theater");

            // default to the home page
            int pageToShow = HOME_PAGE;

            if ("theaters".equals(action) && city != null) {
                // city is a required parameter for a theater list
                pageToShow = THEATERS_PAGE;
            } else if ("showtimes".equals(action) && city != null
                    && theater != null) {
                // city and theater are required parameters for showtimes
                pageToShow = SHOWTIMES_PAGE;
            }

            // set the content type of the response
            int clientType = determineClientType(req);
            switch (clientType) {
            case XHTML_CLIENT_TYPE:
                res.setContentType("text/html");
                break;
            case WML_CLIENT_TYPE:
                res.setContentType("text/vnd.wap.wml");
                break;
            default:
                res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }

            File xsltFile = locateStylesheet(req, clientType, pageToShow);

            // prepare for the transformation using JAXP
            TransformerFactory transFact = TransformerFactory.newInstance( );
            Transformer trans = transFact.newTransformer(
                    new StreamSource(xsltFile));

            // pass parameters to the XSLT stylesheet
            if (city != null) {
                trans.setParameter("city_id", city);
            }
            if (theater != null) {
                trans.setParameter("theater_id", theater);
            }

            // all pages, both WML and XHTML, share the exact same
            // XML data file
            InputStream xmlIn = getServletContext( ).getResourceAsStream(
                    "/WEB-INF/xml/movies.xml");

            // do the transformation
            trans.transform(new StreamSource(xmlIn),
                    new StreamResult(res.getOutputStream( )));
        } catch (TransformerException te) {
            throw new ServletException(te);
        }
    }

    /**
     * @param clientType one of the constants defined in this class, either
     *        WML_CLIENT_TYPE or XHTML_CLIENT_TYPE.
     * @param pageToShow one of the _PAGE constants defined by this class.
     * @return a file representing the appropriate XSLT stylesheet.
     */
    private File locateStylesheet(HttpServletRequest req,
            int clientType, int pageToShow) {
        String xsltDir = null;
        switch (clientType) {
        case WML_CLIENT_TYPE:
            xsltDir = "wml";
            break;
        case XHTML_CLIENT_TYPE:
            xsltDir = "xhtml";
            break;
        default:
            throw new IllegalArgumentException("Illegal clientType: "
                    + clientType);
        }

        String xsltName = null;
        switch (pageToShow) {
        case HOME_PAGE:
            xsltName = "home.xslt";
            break;
        case THEATERS_PAGE:
            xsltName = "theaters.xslt";
            break;
        case SHOWTIMES_PAGE:
            xsltName = "showtimes.xslt";
            break;
        default:
            throw new IllegalArgumentException("Illegal pageToShow: "
                    + pageToShow);
        }

        // locate a platform-dependent path
        String fullPath = getServletContext( ).getRealPath(
                "/WEB-INF/xslt/" + xsltDir + "/" + xsltName);
        return new File(fullPath);
    }

    /**
     * Determines the type of user agent.
     *
     * @return either XHTML_CLIENT_TYPE or WML_CLIENT_TYPE.
     */
    private int determineClientType(HttpServletRequest req) {
        // first check for normal web browsers that claim to be
        // mozilla-compliant
        String userAgent = req.getHeader("User-Agent");
        if (userAgent != null
                && userAgent.toLowerCase( ).startsWith("mozilla")) {
            return XHTML_CLIENT_TYPE;
        }

        // if the client accepts wml, it must be a WAP-compatible device
        String accept = req.getHeader("Accept");
        if (accept != null && accept.indexOf("text/vnd.wap.wml") > -1) {
            return WML_CLIENT_TYPE;
        }

        // otherwise, default to XHTML
        return XHTML_CLIENT_TYPE;
    }
}

This servlet determines the client type by looking at the HTTP User-Agent and Accept headers. This logic is encapsulated in the determineClientType( ) method, which first checks the User-Agent for Mozilla-compatible browsers such as Microsoft Internet Explorer and Netscape Navigator. If the client is not one of these browsers, it then checks the Accept header for text/vnd.wap.wml. If both tests fail, the servlet defaults to XHTML because the device did not claim to accept the WML content type.

Once the client browser type is identified, the HTTP Content-Type response header is set to the appropriate MIME type:

switch (clientType) {
case XHTML_CLIENT_TYPE:
    res.setContentType("text/html");
    break;
case WML_CLIENT_TYPE:
    res.setContentType("text/vnd.wap.wml");
    break;
default:
    res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    return;
}

The default case will occur only if the servlet has a bug. Therefore, it simply causes the code to fail with an internal server error. A helper method called locateStylesheet( ) is then used to locate the appropriate XSLT stylesheet:

File xsltFile = locateStylesheet(req, clientType, pageToShow);

In this application, there are two sets of XSLT stylesheets. One resides in a directory named wml, and another resides in a directory named xhtml. Just like examples shown in previous chapters, ServletContext is utilized to locate these files in a portable manner:

String fullPath = getServletContext( ).getRealPath(
        "/WEB-INF/xslt/" + xsltDir + "/" + xsltName);

Last but not least, the XSLT transformation is performed using the JAXP API.

10.3.3.5. XSLT stylesheets

This application consists of six XSLT stylesheets. Three of these stylesheets are listed here and are used to generate the three WML decks. The other three are used to generate XHTML and can be downloaded along with the rest of the examples in this book. The first stylesheet, shown in Example 10-10, is responsible for creating the home deck.

Example 10-10. Home page XSLT

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************************
** Produces the home page for WML-enabled devices.
********************************************************************-->
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml"
  version="1.0" encoding="UTF-8" indent="yes"
   doctype-public="-//WAPFORUM//DTD WML 1.1//EN"
   doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"/>
  <!--
  *********************************************************************
  ** The main template; creates the deck and the home card.
  ******************************************************************-->
  <xsl:template match="/movies">
    <wml>

      <!-- call a template to produce the splash screen -->
      <xsl:call-template name="createSplashCard"/>

      <card id="home" title="Home" newcontext="true">
        <p align="center">
          Please select your city:
          <select name="city" multiple="false">
            <xsl:apply-templates select="city"/>
          </select>
        </p>

        <p>
          <em>
            <a href="movieguide?action=theaters&amp;city=$(city)"
                >Show Theaters...</a>
          </em>
        </p>
      </card>
    </wml>
  </xsl:template>

  <!--
  *********************************************************************
  ** Produce a single <option> element for a city
  ******************************************************************-->
  <xsl:template match="city">
    <option value="{@id}">
      <xsl:value-of select="name"/>
    </option>
  </xsl:template>

  <!--
  *********************************************************************
  ** Create the splash screen.
  ******************************************************************-->
  <xsl:template name="createSplashCard">
    <card id="splash" title="ABC Theaters" ontimer="#home">
      <timer value="15"/>
      <p align="center">
        <big>Welcome to ABC Theaters</big>
      </p>
      <p>Mmmm...Popcorn...</p>
      <do type="accept">
        <go href="#home"/>
      </do>
    </card>
  </xsl:template>

</xsl:stylesheet>

This is actually a very simple stylesheet. The critical feature is the <xsl:output> element, which specifies the XML output method and the WML DTD. This application adheres to Version 1.1 of WML for maximum compatibility with existing cell phones, although newer versions of WML are available. For these versions, use the newer DTDs found at http://www.wapforum.org.

The only marginally difficult part of the stylesheet is the following line:

<a href="movieguide?action=theaters&amp;city=$(city)">
    Show Theaters...</a>

This creates the hyperlink to the next deck, passing parameters for the action and city. The ampersand character (&) must be written as &amp; for the XML parser to handle this attribute correctly. Although the $(city) syntax looks a lot like an XSLT Attribute Value Template, it is actually a WML variable.[57] This is how the selected city is sent to the servlet when the user clicks on the hyperlink. With ordinary XHTML, this can only be accomplished using a form or a scripting language.

[57] Recall from Chapter 2, "XSLT Part 1 -- The Basics" that XSLT AVTs are written like {$var}.

The stylesheet shown in Example 10-11 is responsible for creating a list of theaters in a city.

Example 10-11. Movie listing XSLT

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************************
** Produces a list of theaters for WML-enabled devices.
********************************************************************-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="city_id" select="'stl'"/>

  <xsl:output method="xml"
  version="1.0" encoding="UTF-8" indent="yes"
   doctype-public="-//WAPFORUM//DTD WML 1.1//EN"
   doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"/>

  <!--
  *********************************************************************
  ** The main template; creates the deck and the theaters card.
  ******************************************************************-->
  <xsl:template match="/movies">
    <wml>
      <card id="theaters" title="Theaters">
        <!-- select the appropriate city -->
        <xsl:apply-templates select="city[@id=$city_id]"/>
        <p>
          <em>
            <a href="movieguide">Change city...</a>
          </em>
        </p>
      </card>
    </wml>
  </xsl:template>

  <!--
  *********************************************************************
  ** Show details for a city.
  ******************************************************************-->
  <xsl:template match="city">
    <p>
      <big><xsl:value-of select="name"/></big>
    </p>
    <p>Select a theater:</p>
    <p>
      <!-- show a list of all theaters in this city -->
      <xsl:apply-templates select="theater"/>
    </p>
  </xsl:template>

  <!--
  *********************************************************************
  ** Create a link for an individual theater.
  ******************************************************************-->
  <xsl:template match="theater">
      <a href="movieguide?action=showtimes&amp;city={
             $city_id}&amp;theater={@id}">
        <xsl:value-of select="name"/>
      </a>
      <br/>
  </xsl:template>
</xsl:stylesheet>

Unlike the first stylesheet, this one requires a parameter for the city:

<xsl:param name="city_id" select="'stl'"/>

For testing purposes, the parameter defaults to stl, but in the real application it should always be passed from the servlet. This is necessary because one big XML file contains data for all cities. This parameter allows the stylesheet to extract information from this file for a single city. For example, the first <xsl:apply-templates> uses a predicate to select the city whose id attribute matches the city_id stylesheet parameter:

<xsl:apply-templates select="city[@id=$city_id]"/>

The remainder of this stylesheet is very basic, simply outputting a list of theaters in the city. The final stylesheet, shown in Example 10-12, creates a list of showtimes for a movie theater. This is the most complex stylesheet merely because it produces multiple cards.

Example 10-12. Showtimes XSLT

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************************
** Produces a list of showtimes for WML-enabled devices.
********************************************************************-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="city_id" select="'stl'"/>
  <xsl:param name="theater_id" select="'ofallon'"/>

  <xsl:output method="xml"
  version="1.0" encoding="UTF-8" indent="yes"
   doctype-public="-//WAPFORUM//DTD WML 1.1//EN"
   doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"/>

  <!--
  *********************************************************************
  ** The main template; creates the deck and the movies card.
  ******************************************************************-->
  <xsl:template match="/movies">
    <wml>
      <!-- generate the WML template -->
      <template>
        <do type="prev" label="Back" name="common_prev">
          <prev/>
        </do>
      </template>

      <card id="movies" title="Movies">
        <!-- shadow the template in this card -->
        <do type="prev" name="common_prev">
          <noop/>
        </do>

        <!-- select the theater that matches the city_id and
             theater_id stylesheet parameters -->
        <xsl:apply-templates
          select="city[@id=$city_id]/theater[@id=$theater_id]"/>
      </card>

      <!-- generate more cards, one per movie -->
      <xsl:apply-templates
          select="city[@id=$city_id]/theater[@id=$theater_id]/movie"
            mode="createCard"/>

    </wml>
  </xsl:template>

  <!--
  *********************************************************************
  ** Show more information about a theater.
  ******************************************************************-->
  <xsl:template match="theater">
    <p>
      <big>
        <xsl:value-of select="name"/>
      </big>
    </p>
    <p>Select a movie:</p>
    <p>
      <xsl:apply-templates select="movie"/>
    </p>

    <p>
      <em>
        <a href="movieguide?action=theaters&amp;city={$city_id}">
                Change theater...</a>
      </em>
    </p>
  </xsl:template>

  <!--
  *********************************************************************
  ** Show more information about a movie in the main card.
  ******************************************************************-->
   <xsl:template match="movie">
    <xsl:variable  name="curId" select="@ref"/>
    <!-- the hyperlink text is the shortName from the <moviedef> -->
    <a href="#{$curId}">
      <xsl:value-of select="/movies/moviedef[@id=$curId]/shortName"/>
    </a>
    <br/>
  </xsl:template>

  <!--
  *********************************************************************
  ** Create a card for a movie that lists showtimes.
  ******************************************************************-->
  <xsl:template match="movie" mode="createCard">
    <xsl:variable  name="curId" select="@ref"/>

    <card id="{$curId}" title="Showtimes">
      <p>
        <em>
          <xsl:value-of select="/movies/moviedef[@id=$curId]/longName"/>
        </em>
      </p>
      <p>
        <xsl:value-of select="times"/>
      </p>
    </card>
  </xsl:template>
</xsl:stylesheet>

As described earlier in this chapter, this deck creates a template that defines a Back button visible on all but the first card. The template is produced just before the first card, which also happens to be the one card that shadows the template with a <noop/> element.

The following <xsl:apply-templates> element selects the correct city and theater based on the stylesheet parameters city_id and theater_id:

<xsl:apply-templates
   select="city[@id=$city_id]/theater[@id=$theater_id]"/>

Although this syntax was covered in detail back in Chapter 2, "XSLT Part 1 -- The Basics", here is a quick review of how it works:

  1. Select all <city> children of the <movies> element.

  2. Use the predicate [@id=$city_id] to narrow this list down to the correct city.

  3. Select all <theater> children of the <city>.

  4. Use the predicate [@id=$theater_id] to narrow this list down to a single <theater>.

After the home card is created, <xsl:apply-templates> is used to create one card per movie:

<xsl:apply-templates
   select="city[@id=$city_id]/theater[@id=$theater_id]/movie"
   mode="createCard"/>

This uses template modes, a technique covered in Chapter 3, "XSLT Part 2 -- Beyond the Basics". It causes the following template to be instantiated, since it has a matching mode:

<xsl:template match="movie" mode="createCard">
  ...produce a card containing showtimes for a movie
</xsl:template>

Previous | Home | Next
10.2. The Wireless Architecture | Book Index | 10.4. The Future of Wireless
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Future of Wireless (Java and XSLT)

 Java and XSLTSearch this book

10.4. The Future of Wireless

Looking toward the future, two things are inevitable. First, device capabilities will improve. Color displays, increased screen resolution, increased memory, and faster processors can be expected. Second, bandwidth will improve. Migration to Third Generation (3G) wireless networks is already underway in many countries, offering bandwidth anywhere from 128Kbps to 2Mbps.

Partly because of these two advancements, the WAP Forum and NTT DoCoMo have agreed to migrate towards XHTML Basic for future devices. XHTML Basic uses a subset of XHTML. Therefore, it is far more familiar to web developers than WML. Existing web browsers can already display XHTML Basic, which is not the case with WML or cHTML. XHTML Basic also utilizes CSS to define colors, fonts, and other presentation styles. Newer, more capable devices should be able to take advantage of far more style instructions than currently available devices.

The migration to XHTML Basic will not eliminate the need for technologies such as XSLT, however. Even though normal web browsers can display the same XHTML Basic web pages that wireless devices use, devices such as cellular phones and pagers will always have tiny displays. Most web applications will still need to maintain a simplified interface for these devices to minimize the amount of scrolling the user has to do. Therefore, XSLT will continue to be important.

Previous | Home | Next
10.3. Java, XSLT, and WML | Book Index | A. Discussion Forum Code
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Discussion Forum Code (Java and XSLT)

 Java and XSLTSearch this book

Appendix A. Discussion Forum Code

This appendix contains all of the remaining code from the discussion forum example presented in Chapter 7, "Discussion Forum". These are the "simple" files that did not merit a lot of explanation in the text. All of the source code can be downloaded from this book's companion web site at http://www.oreilly.com/catalog/javaxslt.

BoardSummaryImpl.java(1) (shown in Example A-1) provides a default implementation of the BoardSummary interface.

Example A-1. BoardSummaryImpl.java(1)

package com.oreilly.forum.domain;

import com.oreilly.forum.domain.*;
import java.util.*;

/**
 * An implementation of the BoardSummary interface.
 */
public class BoardSummaryImpl implements BoardSummary {
    private long id;
    private String name;
    private String description;
    private List monthsWithMessages;

    /**
     * @param monthsWithMessages a list of MonthYear objects.
     */
    public BoardSummaryImpl(long id, String name, String description,
            List monthsWithMessages) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.monthsWithMessages = monthsWithMessages;

    }

    public long getID( ) {
        return this.id;
    }

    public String getName( ) {
        return this.name;
    }

    public String getDescription( ) {
        return this.description;
    }

    /**
     * @return an iterator of <code>MonthYear</code> objects.
     */
    public Iterator getMonthsWithMessages( ) {
        return this.monthsWithMessages.iterator( );
    }
}

BoardSummaryImpl.java(2) (shown in Example A-2) is an alternate implementation of the BoardSummary interface. This class is used by the fake data implementation, which is useful for testing purposes when a database is not available.

Example A-2. BoardSummaryImpl.java(2)

package com.oreilly.forum.fakeimpl;

import com.oreilly.forum.domain.*;

import java.util.*;

public class BoardSummaryImpl implements BoardSummary {
    private long id;
    private String name;
    private String description;
    // a list of MonthYear objects
    private List monthsWithMessages;

    public BoardSummaryImpl(long id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.monthsWithMessages = new ArrayList( );
    }

    public void messageAdded(Message msg) {
        DayMonthYear createDate = msg.getCreateDate( );

        // update the monthsWithMessages list
        Iterator iter = this.monthsWithMessages.iterator( );
        while (iter.hasNext( )) {
            MonthYear curMonth = (MonthYear) iter.next( );
            if (createDate.getMonth() == curMonth.getMonth( )
                    && createDate.getYear() == curMonth.getYear( )) {
                return;
            }
        }

        this.monthsWithMessages.add(createDate);
    }

    public long getID( ) {
        return this.id;
    }

    public String getName( ) {
        return this.name;
    }

    public String getDescription( ) {
        return this.description;
    }

    public Iterator getMonthsWithMessages( ) {
        return this.monthsWithMessages.iterator( );
    }
}

DataException.java (shown in Example A-3) is a generic exception that occurs when something goes wrong with the underlying database. This prevents database-specific code from creeping into the application, making it possible to migrate to other data sources in the future.

Example A-3. DataException.java

package com.oreilly.forum.adapter;

/**
 * An exception that indicates some operation with the back-end
 * data source failed.
 */
public class DataException extends Exception {
    private Throwable rootCause;

    /**
     * Wrap a DataException around another throwable.
     */
    public DataException(Throwable rootCause) {
        super(rootCause.getMessage( ));
        this.rootCause = rootCause;
    }

    /**
     * Construct an exception with the specified detail message.
     */
    public DataException(String message) {
        super(message);
    }

    /**
     * @return a reference to the root exception or null.
     */
    public Throwable getRootCause( ) {
        return this.rootCause;
    }
}

DataUtil.java (shown in Example A-4) is a simple utility method that deals with dates.

Example A-4. DateUtil.java

package com.oreilly.forum.domain;

import java.util.*;

/**
 * Misc utility functions for dates. Methods are synchronized because
 * the same Calendar instance is shared.
 */
public final class DateUtil {

    private static Calendar cal = Calendar.getInstance( );

    /**
     * @return the day of the month for a given date.
     */
    public synchronized static int getDayOfMonth(Date date) {
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * @return the month number for a given date.
     */
    public synchronized static int getMonth(Date date) {
        cal.setTime(date);
        return cal.get(Calendar.MONTH);
    }

    /**
     * @return the year number for the given date.
     */
    public synchronized static int getYear(Date date) {
        cal.setTime(date);
        return cal.get(Calendar.YEAR);
    }

    private DateUtil( ) {
    }
}

DayMonthYear.java (shown in Example A-5) is a helper class that groups a day, month, and year together. It also supports comparisons for sorting purposes.

Example A-5. DayMonthYear.java

package com.oreilly.forum.domain;

import java.util.Date;

/**
 * Represents a day, month, and year.
 */
public class DayMonthYear extends MonthYear {
    private int day;

    public DayMonthYear( ) {
        this(new Date( ));
    }

    public DayMonthYear(Date date) {
        super(date);
        this.day = DateUtil.getDayOfMonth(date);
    }

    public DayMonthYear(int day, int month, int year) {
        super(month, year);
        this.day = day;
    }

    public int getDay( ) {
        return this.day;
    }

    public boolean equals(Object obj) {
        if (obj instanceof DayMonthYear) {
            DayMonthYear rhs = (DayMonthYear) obj;
            return super.equals(obj) && this.day == rhs.day;
        }
        return false;
    }

    public int hashCode( ) {
        return super.hashCode( ) ^ this.day;
    }

    public int compareTo(Object obj) {
        DayMonthYear rhs = (DayMonthYear) obj;
        int comparison = super.compareTo(obj);
        if (comparison == 0) {
            if (this.day < rhs.day) {
                return -1;
            } else if (this.day > rhs.day) {
                return 1;
            }
        }
        return comparison;
    }

    public String toString( ) {
        return getMonth() + "/" + getDay() + "/" + getYear( );
    }
}

FakeDataAdapter.java (shown in Example A-6) allows the discussion forum to be executed without any database. This class was written before the database was implemented, and is useful for testing purposes only.

Example A-6. FakeDataAdapter.java

package com.oreilly.forum.fakeimpl;

import com.oreilly.forum.*;
import com.oreilly.forum.adapter.*;
import com.oreilly.forum.domain.*;
import java.util.*;

public class FakeDataAdapter extends DataAdapter {
    // a list of BoardSummary objects
    private List allBoards;
    private static long nextMessageID = 0;
    private Map messageMap = new HashMap( );

    public FakeDataAdapter( ) throws DataException {
        this.allBoards = new ArrayList( );

        BoardSummary bs0 = new BoardSummaryImpl(0L,
                "Java Programming",
                "General programming questions about Java.");
        BoardSummary bs1 = new BoardSummaryImpl(1L,
                "XSLT Stylesheet Techniques",
                "Writing effective XSLT stylesheets.");
        this.allBoards.add(bs0);
        this.allBoards.add(bs1);

        this.postNewMessage(0L, "First subject in Java Prog",
                "burke_e@yahoo.com", "Sample message text");

    }

    /**
     * @param msgID must be a valid message identifier.
     * @return the message with the specified id.
     * @throws DataException if msgID does not exist or a database
     * error occurs.
     */
    public Message getMessage(long msgID) throws DataException {
        Message msg = (Message) this.messageMap.get(new Long(msgID));
        if (msg != null) {
            return msg;
        }
        throw new DataException("Invalid msgID");
    }

    /**
     * If no messages exist for the specified board and month, return
     * an empty iterator.
     * @return an iterator of <code>MessageSummary</code> objects.
     * @throws DataException if the boardID is illegal or a database
     * error occurs.
     */
    public Iterator getAllMessages(long boardID, MonthYear month)
            throws DataException {
        // this is slow, but works fine for a fake implementation
        List msgs = new ArrayList( );
        Iterator iter = this.messageMap.values().iterator( );
        while (iter.hasNext( )) {
            MessageSummary curMsg = (MessageSummary) iter.next( );
            if (curMsg.getBoard().getID( ) == boardID
                    && month.containsInMonth(curMsg.getCreateDate( ))) {
                msgs.add(curMsg);
            }
        }
        return msgs.iterator( );
    }

    /**
     * Add a reply to an existing message.
     *
     * @throws DataException if a database error occurs, or if any
     * parameter is illegal.
     */
    public Message replyToMessage(long origMsgID, String msgSubject,
            String authorEmail, String msgText) throws DataException {
        MessageSummary origMsg = getMessage(origMsgID);
        long msgID = getNextMessageID( );

        Message msg = new MessageImpl(msgID, new DayMonthYear( ), origMsg.getBoard( ),
                msgSubject, authorEmail, msgText, origMsgID);

        this.messageMap.put(new Long(msg.getID( )), msg);
        return msg;
    }

    /**
     * Post a new message.
     *
     * @return the newly created message.
     * @throws DataException if a database error occurs, or if any
     * parameter is illegal.
     */
    public Message postNewMessage(long boardID, String msgSubject,
            String authorEmail, String msgText) throws DataException {
        BoardSummary boardSum = getBoardSummary(boardID);
        long msgID = getNextMessageID( );

        Message msg = new MessageImpl(msgID, new DayMonthYear( ), boardSum,
                msgSubject, authorEmail, msgText, -1);
        this.messageMap.put(new Long(msg.getID( )), msg);

        ((BoardSummaryImpl) boardSum).messageAdded(msg);

        return msg;
    }

    /**
     * @return an iterator of <code>BoardSummary</code> objects.
     */
    public Iterator getAllBoards( ) throws DataException {
        return this.allBoards.iterator( );
    }

    public BoardSummary getBoardSummary(long boardID)
            throws DataException {
        Iterator iter = getAllBoards( );
        while (iter.hasNext( )) {
            BoardSummary curBoard = (BoardSummary) iter.next( );
            if (curBoard.getID( ) == boardID) {
                return curBoard;
            }
        }
        throw new DataException("Illegal boardID: " + boardID);
    }

    private synchronized static long getNextMessageID( ) {
        nextMessageID++;
        return nextMessageID;
    }
}

MessageImpl.java (shown in Example A-7) is an implementation of the Message interface.

Example A-7. MessageImpl.java

package com.oreilly.forum.domain;

import java.util.*;

/**
 * An implementation of the Message interface.
 */
public class MessageImpl extends MessageSummaryImpl implements Message {
    private String text;

    /**
     * Construct a new instance of this class.
     */
    public MessageImpl(long id, DayMonthYear createDate,
            BoardSummary board, String subject, String authorEmail,
            String text, long inReplyTo) {
        super(id, createDate, board, subject, authorEmail, inReplyTo);
        this.text = text;
    }

    /**
     * @return the text of this message.
     */
    public String getText( ) {
        return this.text;
    }
}

MessageSummaryImpl.java (shown in Example A-8) is an implementation of the MessageSummary interface.

Example A-8. MessageSummaryImpl.java

package com.oreilly.forum.domain;

import java.util.*;

/**
 * Implementation of the MessageSummary interface.
 */
public class MessageSummaryImpl implements MessageSummary {
    private long id;
    private BoardSummary board;
    private String subject;
    private String authorEmail;
    private DayMonthYear createDate;
    private long inReplyTo;

    public MessageSummaryImpl(long id, DayMonthYear createDate,
            BoardSummary board, String subject, String authorEmail,
            long inReplyTo) {
        this.id = id;
        this.createDate = createDate;
        this.board = board;
        this.subject = subject;
        this.authorEmail = authorEmail;
        this.inReplyTo = inReplyTo;
    }

    public long getInReplyTo( ) {
        return this.inReplyTo;
    }

    public long getID( ) {
        return this.id;
    }

    public DayMonthYear getCreateDate( ) {
        return this.createDate;
    }

    public BoardSummary getBoard( ) {
        return this.board;
    }

    public String getSubject( ) {
        return this.subject;
    }

    public String getAuthorEmail( ) {
        return this.authorEmail;
    }

    public boolean equals(Object obj) {
        if (obj instanceof MessageSummaryImpl) {
            MessageSummaryImpl rhs = (MessageSummaryImpl) obj;
            return this.id == rhs.id;
        }
        return false;
    }

    public int hashCode( ) {
        return (int) this.id;
    }

    /**
     * Sorts by create date followed by message subject.
     */
    public int compareTo(Object obj) {
        if (this == obj) {
            return 0;
        }
        MessageSummaryImpl rhs = (MessageSummaryImpl) obj;

        int comparison = this.createDate.compareTo(rhs.createDate);
        if (comparison != 0) {
            return comparison;
        }

        comparison = this.subject.compareTo(rhs.subject);
        if (comparison != 0) {
            return comparison;
        }

        return 0;
    }
}

MonthYear.java (shown in Example A-9) groups a month and year together. It also supports sorting.

Example A-9. MonthYear.java

package com.oreilly.forum.domain;

import java.io.Serializable;
import java.util.*;

/**
 * Represents a month and a year.
 */
public class MonthYear implements Comparable, Serializable {
    private int month;
    private int year;

    /**
     * Construct a new object representing the current instant in time.
     */
    public MonthYear( ) {
        this(new Date( ));
    }

    /**
     * Construct a new object with the given date.
     */
    public MonthYear(Date date) {
        this(DateUtil.getMonth(date), DateUtil.getYear(date));
    }

    /**
     * Construct a new object with the given month and year.
     * @param month a zero-based month, just like java.util.Calendar.
     */
    public MonthYear(int month, int year) {
        this.month = month;
        this.year = year;
    }

    /**
     * Compare this MonthYear object to another.
     */
    public int compareTo(Object obj) {
        MonthYear rhs = (MonthYear) obj;
        // first compare year
        if (this.year < rhs.year) {
            return -1;
        } else if (this.year > rhs.year) {
            return 1;
        }
        // then month
        if (this.month < rhs.month) {
            return -1;
        } else if (this.month > rhs.month) {
            return 1;
        }

        return 0;
    }

    /**
     * @return true if the specified date occurs sometime during this month.
     */
    public boolean containsInMonth(DayMonthYear date) {
        return date.getMonth( ) == this.month
                && date.getYear( ) == this.year;
    }

    /**
     * @return the month number, starting with 0 for January.
     */
    public int getMonth( ) {
        return this.month;
    }

    /**
     * @return the year number.
     */
    public int getYear( ) {
        return this.year;
    }

    public boolean equals(Object obj) {
        if (obj instanceof MonthYear) {
            MonthYear rhs = (MonthYear) obj;
            return this.month == rhs.month
                    && this.year == rhs.year;
        }
        return false;
    }

    public int hashCode( ) {
        return this.month ^ this.year;
    }
}

The viewMsg.xslt XSLT stylesheet (shown in Example A-10) displays a web page for a single message.

Example A-10. viewMsg.xslt

<?xml version="1.0" encoding="UTF-8"?>
<!--
***********************************************************
** viewMsg.xslt
**
** Shows details for a specific message.
***********************************************************
-->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="utils.xslt"/>
  <xsl:param name="rootDir" select="'../docroot/'"/>
  <xsl:output method="xml" version="1.0" encoding="UTF-8"
    indent="yes"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>

   <!--
  **********************************************************
  ** Create the XHTML web page
  *******************************************************-->
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>View Message</title>
        <link href="{$rootDir}forum.css"
            rel="stylesheet" type="text/css"/>
      </head>
      <body>
        <div class="box1">
          <h1>View Message</h1>
          <div>
            <xsl:value-of select="message/board/name"/>
          </div>
        </div>
        <!-- ===== Quick Actions ====== -->
        <h3>Quick Actions</h3>
        <ul>
          <li>Return to
                           <!-- long line wrapped -->
          <a href="viewMonth?boardID={message/board/@id}&amp;month={
               message/@month}&amp;year={message/@year}">
              <xsl:call-template name="utils.printLongMonthName">
                <xsl:with-param name="monthNumber" select="message/@month"/>
              </xsl:call-template>,
          <xsl:value-of select="message/@year"/>
            </a> messages for <xsl:value-of select="message/board/name"/>
          </li>
          <li>Return to the <a href="home">home page</a>
          </li>
          <li>
            <a href="postMsg?mode=replyToMsg&amp;origMsgID={message/@id}">Reply</a>
              to this message</li>
        </ul>
        <h3>Message</h3>
        <div class="box2">
          <xsl:apply-templates select="message"/>
        </div>
      </body>
    </html>
  </xsl:template>

  <!--
  **********************************************************
  ** Show details for the <message> element
  *******************************************************-->
  <xsl:template match="message">
    <div>
      <div style="font-weight: bold;">
        <xsl:value-of select="subject"/>
      </div>
      <xsl:text> posted by </xsl:text>
      <a href="mailto:{authorEmail}">
        <xsl:value-of select="authorEmail"/>
      </a>
      <xsl:text> on </xsl:text>
      <xsl:call-template name="utils.printShortMonthName">
        <xsl:with-param name="monthNumber" select="@month"/>
      </xsl:call-template>
      <xsl:text> </xsl:text>
      <xsl:value-of select="@day"/>
      <xsl:text>, </xsl:text>
      <xsl:value-of select="@year"/>
      <xsl:apply-templates select="inResponseTo"/>
    </div>
    <pre>
      <xsl:value-of select="text"/>
    </pre>
  </xsl:template>

  <!--
  **********************************************************
  ** Show a link to the message that this one is in
  ** response to.
  *******************************************************-->
  <xsl:template match="inResponseTo">
    <div style="text-indent: 2em;">
      <xsl:text>In response to </xsl:text>
      <a href="viewMsg?msgID={@id}">
        <xsl:value-of select="subject"/>
      </a>
    </div>
  </xsl:template>
</xsl:stylesheet>
Previous | Home | Next
10.4. The Future of Wireless | Book Index | B. JAXP API Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


JAXP API Reference (Java and XSLT)

 Java and XSLTSearch this book

Appendix B. JAXP API Reference

This appendix summarizes Version 1.1 of the Java API for XML Processing (JAXP).[58] JAXP provides a standard way for Java programs to interact with XML parsers and XSLT processors and is freely available from http://java.sun.com/xml. JAXP also includes classes and interfaces for DOM and SAX; these are not listed here.

[58] Before transformation support was added, JAXP stood for "Java API for XML Parsing."

The biggest changes from JAXP 1.0 to JAXP 1.1 are support for level 2 of DOM and SAX, as well as an entirely new plugability layer for performing transformations. JAXP 1.1 also refines the algorithm used for locating implementation classes for the three supported plugability layers. This algorithm is discussed in Chapter 5, "XSLT Processing with Java", in the "Introduction to JAXP 1.1" section.

Package: javax.xml.parsers

The classes in this package support parsing using Simple API for XML (SAX) 2 and Document Object Model (DOM) Level 2. These classes do not perform the actual parsing work; instead, they delegate to plugable parser implementations such as Apache's Crimson or Xerces.

DocumentBuilder

Instances of this class define an API for parsing XML from a variety of input sources, as well as for creating new DOM Document objects from scratch. The DocumentBuilder instance should be obtained from the DocumentBuilderFactory instance. Once an instance has been obtained, the newDocument( ) method can be used to construct new DOM Document objects without resorting to the implementation of specific code.

public abstract class DocumentBuilder {
    protected DocumentBuilder( );
    public abstract DOMImplementation getDOMImplementation( );
    public abstract boolean isNamespaceAware( );
    public abstract boolean isValidating( );
    public abstract Document newDocument( );
    public Document parse(InputStream is, String systemId)
        throws SAXException, IOException;
    public Document parse(String uri)
        throws SAXException, IOException;
    public Document parse(File f)
        throws SAXException, IOException;
    public abstract Document parse(InputSource is)
        throws SAXException, IOException;
    public Document parse(InputStream is)
        throws SAXException, IOException;
    public abstract void setEntityResolver(EntityResolver er);
    public abstract void setErrorHandler(ErrorHandler eh);
}

DocumentBuilderFactory

This class allows instances of DocumentBuilder to be constructed using a factory pattern, insulating application code from specific DOM implementations. Various methods in this class allow programs to specify which features the parser will support. If these features are not available, the newDocumentBuilder( ) throws a ParserConfigurationException. The various accessor methods, such as isNamespaceAware( ), do not indicate whether the underlying parser actually supports a given feature. Instead, these methods indicate whether the application configured those features on this DocumentBuilderFactory instance. Before using this class, call the newInstance( ) method to create an instance of it. This object is then used to construct an instance of DocumentBuilder using the newDocumentBuilder( ) method.

public abstract class DocumentBuilderFactory {
    protected DocumentBuilderFactory( );
    public abstract Object getAttribute(String name)
        throws IllegalArgumentException;
    public boolean isCoalescing( );
    public boolean isExpandEntityReferences( );
    public boolean isIgnoringComments( );
    public boolean isIgnoringElementContentWhitespace( );
    public boolean isNamespaceAware( );
    public boolean isValidating( );
    public abstract DocumentBuilder newDocumentBuilder( )
        throws ParserConfigurationException;
    public static DocumentBuilderFactory newInstance( );
    public abstract void setAttribute(String name, Object value)
        throws IllegalArgumentException;
    public void setCoalescing(boolean coalescing);
    public void setExpandEntityReferences(boolean expandEntityRef);
    public void setIgnoringComments(boolean ignoreComments);
    public void setIgnoringElementContentWhitespace(boolean whitespace);
    public void setNamespaceAware(boolean awareness);
    public void setValidating(boolean validating);
}

FactoryConfigurationError

This indicates that the class for a parser factory could not be located or instantiated. If this error occurs, something is not installed correctly on the system. Refer to Chapter 5, "XSLT Processing with Java" for information on the algorithm that JAXP uses to locate parser implementations.

public class FactoryConfigurationError
        extends Error {
    public FactoryConfigurationError(String msg);
    public FactoryConfigurationError(Exception e);
    public FactoryConfigurationError(Exception e, String msg);
    public FactoryConfigurationError( );
    public Exception getException( );
    public String getMessage( );
}

ParserConfigurationException

According to the API specification, represents "a serious configuration error." Generally, this means that the factory cannot provide a parser with the requested features. For instance, a programmer may ask for a namespace-aware parser, but the only parser available does not support namespaces.[59]

[59] XSLT processing requires namespace-aware XML parsers.

public class ParserConfigurationException
        extends Exception {
    public ParserConfigurationException(String msg);
    public ParserConfigurationException( );
}

SAXParser

This class defines a wrapper around an underlying SAX parser implementation. It was part of JAXP 1.0 and supports both SAX 1 and SAX 2 features. If possible, programmers should avoid methods that use HandlerBase, because this is a deprecated SAX 1 interface. Instead, use the methods that deal with DefaultHandler.

public abstract class SAXParser {
    protected SAXParser( );
    public abstract Parser getParser( )
        throws SAXException;
    public abstract Object getProperty(String name)
        throws SAXNotRecognizedException, SAXNotSupportedException;
    public abstract XMLReader getXMLReader( )
        throws SAXException;
    public abstract boolean isNamespaceAware( );
    public abstract boolean isValidating( );
    public void parse(InputStream is, DefaultHandler dh, String systemId)
        throws SAXException, IOException;
    public void parse(InputStream is, DefaultHandler p1)
        throws SAXException, IOException;
    public void parse(InputStream is, HandlerBase hb, String systemId)
        throws SAXException, IOException;
    public void parse(File f, HandlerBase hb)
        throws SAXException, IOException;
    public void parse(InputStream is, HandlerBase hb)
        throws SAXException, IOException;
    public void parse(String uri, HandlerBase hb)
        throws SAXException, IOException;
    public void parse(InputSource is, HandlerBase hb)
        throws SAXException, IOException;
    public void parse(InputSource is, DefaultHandler dh)
        throws SAXException, IOException;
    public void parse(String systemId, DefaultHandler dh)
        throws SAXException, IOException;
    public void parse(File f, DefaultHandler dh)
        throws SAXException, IOException;
    public abstract void setProperty(String name, Object value)
        throws SAXNotRecognizedException, SAXNotSupportedException;
}

SAXParserFactory

This class defines a factory for creating instances of SAX parsers. Before creating these instances, use the setFeature( ) method to define which parsing features are required of the parser to be created. See http://www.megginson.com/SAX/Java/features.html for a list of core SAX 2 features.

public abstract class SAXParserFactory {
    protected SAXParserFactory( );
    public abstract boolean getFeature(String name)
        throws ParserConfigurationException, SAXNotRecognizedException,
        SAXNotSupportedException;
    public boolean isNamespaceAware( );
    public boolean isValidating( );
    public static SAXParserFactory newInstance( );
    public abstract SAXParser newSAXParser( )
        throws ParserConfigurationException, SAXException;
    public abstract void setFeature(String name, boolean value)
        throws ParserConfigurationException, SAXNotRecognizedException,
        SAXNotSupportedException;
    public void setNamespaceAware(boolean awareness);
    public void setValidating(boolean validating);
}
Package: javax.xml.transform

This package defines an API for performing transformations. Although these are common XSLT transformations, the API is flexible enough to support other transformation technologies. Like the javax.xml.parsers package, the classes and interfaces in this package hide vendor-specific implementation code. JAXP 1.1 ships with Xalan as its reference implementation for transformations; different processors can be plugged in.

ErrorListener

This interface allows applications to implement custom error handling. If an error listener is not registered, errors are written to System.err. More details on this interface can be found in Chapter 9, "Development Environment, Testing, and Performance".

public interface ErrorListener {
    void error(TransformerException exception)
        throws TransformerException;
    void fatalError(TransformerException exception)
        throws TransformerException;
    void warning(TransformerException exception)
        throws TransformerException;
}

OutputKeys

These are constant definitions for legal output property settings on the Transformer interface. They map directly to the legal attributes for the <xsl:output> element. Programmatically specified output properties take priority over output properties specified in the XSLT stylesheet.

public class OutputKeys {
    public static final String CDATA_SECTION_ELEMENTS = "cdata-section-elements";
    public static final String DOCTYPE_PUBLIC = "doctype-public";
    public static final String DOCTYPE_SYSTEM = "doctype-system";
    public static final String ENCODING = "encoding";
    public static final String INDENT = "indent";
    public static final String MEDIA_TYPE = "media-type";
    public static final String METHOD = "method";
    public static final String OMIT_XML_DECLARATION = "omit-xml-declaration";
    public static final String STANDALONE = "standalone";
    public static final String VERSION = "version";
}

Result

This is a common interface for classes that produce a transformation result. DOMResult, SAXResult, and StreamResult are implementing classes. The two constants in this interface are used when specifying whether output escaping is performed, as discussed in section 16.4 of the XSLT specification at http://www.w3.org/TR/xslt. The system id is optional but can be helpful when displaying error messages or warnings.

public interface Result {
    public static final String PI_DISABLE_OUTPUT_ESCAPING =
        "javax.xml.transform.disable-output-escaping";
    public static final String PI_ENABLE_OUTPUT_ESCAPING =
       "javax.xml.transform.enable-output-escaping";
    String getSystemId( );
    void setSystemId(String systemId);
}

Source

This is a generic interface implemented by DOMSource, SAXSource, and StreamSource. The system id is particularly important for Source because it allows the processor to resolve relative URI references within the XML and XSLT inputs.[60]

[60] URI references are found in elements such as <xsl:import> and <xsl:include>.

public interface Source {
    String getSystemId( );
    void setSystemId(String systemId);
}

SourceLocator

Instances of this interface are useful when reporting locations of error messages and warnings. Application programmers retrieve SourceLocator instances from TransformerException's getLocator( ) method.

public interface SourceLocator {
    int getColumnNumber( );
    int getLineNumber( );
    String getPublicId( );
    String getSystemId( );
}

Templates

These instances represent "compiled" transformation instructions. Whether a particular XSLT processor actually compiles stylesheets is implementation-dependent. However, Templates objects are guaranteed to be thread-safe. This makes them ideal for servlet environments, where it is desirable to parse an XSLT stylesheet once then cache it in memory as a Templates object. The output properties are a read-only representation of the <xsl:output> stylesheet element.

public interface Templates {
    Properties getOutputProperties( );
    Transformer newTransformer( )
        throws TransformerConfigurationException;
}

Transformer

Instances of this class perform one or more transformations. Although Transformer objects can be reused, they are not thread-safe and therefore cannot be used concurrently. Output property names are defined by the OutputKeys class and map to the <xsl:output> stylesheet element. Parameters, on the other hand, are stylesheet parameters and map to top-level <xsl:param> elements. The getParameter() method returns only parameters that have been programmatically set.

public abstract class Transformer {
    protected Transformer( );
    public abstract void clearParameters( );
    public abstract ErrorListener getErrorListener( );
    public abstract Properties getOutputProperties( );
    public abstract String getOutputProperty(String name)
        throws IllegalArgumentException;
    public abstract Object getParameter(String name);
    public abstract URIResolver getURIResolver( );
    public abstract void setErrorListener(ErrorListener listener)
        throws IllegalArgumentException;
    public abstract void setOutputProperties(Properties oformat)
        throws IllegalArgumentException;
    public abstract void setOutputProperty(String name, String value)
        throws IllegalArgumentException;
    public abstract void setParameter(String name, Object value);
    public abstract void setURIResolver(URIResolver resolver);
    public abstract void transform(Source xmlSource, Result outputTarget)
        throws TransformerException;
}

TransformerConfigurationException

This exception indicates a serious problem and may occur when an XSLT stylesheet has syntax errors that prevent instantiation of a Transformer instance. This class can wrap around other exceptions. For example, an underlying parser exception may be wrapped by an instance of this class.

public class TransformerConfigurationException
        extends TransformerException {
    public TransformerConfigurationException(String msg);
    public TransformerConfigurationException(Throwable e);
    public TransformerConfigurationException(String msg, Throwable e);
    public TransformerConfigurationException(String msg, SourceLocator locator);
    public TransformerConfigurationException(String msg, SourceLocator locator,
        Throwable e);
    public TransformerConfigurationException( );
}

TransformerException

This is a general-purpose exception that occurs during transformation. If an ErrorListener is registered, the processor should try to report exceptions there first. Otherwise, exceptions are written to System.err. The quality of error messages varies widely across different XSLT processors. This class can wrap around other exceptions.

public class TransformerException
        extends Exception {
    public TransformerException(String msg, Throwable e);
    public TransformerException(String msg, SourceLocator locator);
    public TransformerException(Throwable e);
    public TransformerException(String msg);
    public TransformerException(String msg, SourceLocator locator, Throwable e);
    public Throwable getCause( );
    public Throwable getException( );
    public String getLocationAsString( );
    public SourceLocator getLocator( );
    public String getMessageAndLocation( );
    public synchronized Throwable initCause(Throwable cause);
    public void printStackTrace(PrintStream ps);
    public void printStackTrace(PrintWriter pw);
    public void printStackTrace( );
    public void setLocator(SourceLocator locator);
}

TransformerFactory

This defines a portable way to access different TransformerFactory instances and is the key abstraction that masks differences between XSLT processors from different vendors.

public abstract class TransformerFactory {
    protected TransformerFactory( );
    public abstract Source getAssociatedStylesheet(Source source, String media,
        String title, String charset) throws TransformerConfigurationException;
    public abstract Object getAttribute(String name)
        throws IllegalArgumentException;
    public abstract ErrorListener getErrorListener( );
    public abstract boolean getFeature(String name);
    public abstract URIResolver getURIResolver( );
    public static TransformerFactory newInstance( )
        throws TransformerFactoryConfigurationError;
    public abstract Templates newTemplates(Source source)
        throws TransformerConfigurationException;
    public abstract Transformer newTransformer(Source source)
        throws TransformerConfigurationException;
    public abstract Transformer newTransformer( )
        throws TransformerConfigurationException;
    public abstract void setAttribute(String name, Object value)
        throws IllegalArgumentException;
    public abstract void setErrorListener(ErrorListener listener)
        throws IllegalArgumentException;
    public abstract void setURIResolver(URIResolver resolver);
}

TransformerFactoryConfigurationError

This error is typically seen when a transformer factory class cannot be instantiated. This is a good indicator of CLASSPATH problems.

public class TransformerFactoryConfigurationError
        extends Error {
    public TransformerFactoryConfigurationError(String msg);
    public TransformerFactoryConfigurationError(Exception e);
    public TransformerFactoryConfigurationError(Exception e, String msg);
    public TransformerFactoryConfigurationError( );
    public Exception getException( );
    public String getMessage( );
}

URIResolver

In most cases, the JAXP provides a URIResolver instance. By creating a custom implementation, however, applications can define how relative URI references in XSLT stylesheets are resolved. For instance, the URIResolver defines how <xsl:include href="header.xslt"/> locates header.xslt.

public interface URIResolver {
    Source resolve(String href, String base)
        throws TransformerException;
}
Package: javax.xml.transform.dom

This package defines how to perform transformations using DOM.

DOMLocator

This interface allows applications to locate the DOM node where an error occurs. Since TransformerException returns instances of SourceLocator, applications must downcast to obtain DOMLocator objects.

public interface DOMLocator
        extends SourceLocator {
    Node getOriginatingNode( );
}

DOMResult

This class allows transformation results to be stored in a DOM tree. If the default constructor is used, the XSLT processor creates a DOM Document node. Otherwise, applications can specify a DOM Document, DocumentFragment, or Element node as the constructor parameter.

The FEATURE constant is used with TransformerFactory.getFeature( ) to determine if the factory supports DOMResult.

public class DOMResult
        implements Result {
    public static final String FEATURE =
        "http://javax.xml.transform.dom.DOMResult/feature";
    public DOMResult(Node node);
    public DOMResult(Node node, String systemId);
    public DOMResult( );
    public Node getNode( );
    public String getSystemId( );
    public void setNode(Node node);
    public void setSystemId(String systemId);
}

DOMSource

This class allows a DOM tree to be used as an input source. In practice, the node parameter is usually an instance of a DOM Document. However, XSLT processors may also support any other type of DOM Node. The system id is still important for resolving relative URI references.

public class DOMSource
        implements Source {
    public static final String FEATURE =
        "http://javax.xml.transform.dom.DOMSource/feature";
    public DOMSource(Node node);
    public DOMSource(Node node, String systemId);
    public DOMSource( );
    public Node getNode( );
    public String getSystemId( );
    public void setNode(Node node);
    public void setSystemId(String systemId);
}
Package: javax.xml.transform.sax

This package defines how to perform transformations using SAX. Example usages can be found in Chapter 5, "XSLT Processing with Java".

SAXResult

This class makes it possible to emit SAX events as the result of a transformation. The ContentHandler parameter receives these events.

public class SAXResult
        implements Result {
    public static final String FEATURE =
        "http://javax.xml.transform.sax.SAXResult/feature";
    public SAXResult(ContentHandler handler);
    public SAXResult( );
    public ContentHandler getHandler( );
    public LexicalHandler getLexicalHandler( );
    public String getSystemId( );
    public void setHandler(ContentHandler handler);
    public void setLexicalHandler(LexicalHandler handler);
    public void setSystemId(String systemId);
}

SAXSource

This allows output from a SAX parser to be fed into an XSLT processor for transformation. It is also used to build Templates or Transformer objects using TransformerFactory.

public class SAXSource
        implements Source {
    public static final String FEATURE =
        "http://javax.xml.transform.sax.SAXSource/feature";
    public SAXSource(XMLReader reader, InputSource inputSource);
    public SAXSource(InputSource inputSource);
    public SAXSource( );
    public InputSource getInputSource( );
    public String getSystemId( );
    public XMLReader getXMLReader( );
    public void setInputSource(InputSource inputSource);
    public void setSystemId(String systemId);
    public void setXMLReader(XMLReader reader);
    public static InputSource sourceToInputSource(Source source);
}

SAXTransformerFactory

This is a subclass of TransformerFactory that adds SAX-specific methods. To create an instance of this class, create a TransformerFactory instance and downcast if transFact.getFeature(SAXTransformerFactory.FEATURE) returns true.

public abstract class SAXTransformerFactory
        extends TransformerFactory {
    public static final String FEATURE =
        "http://javax.xml.transform.sax.SAXTransformerFactory/feature";
    public static final String FEATURE_XMLFILTER =
        "http://javax.xml.transform.sax.SAXTransformerFactory/feature/xmlfilter";
    protected SAXTransformerFactory( );
    public abstract TemplatesHandler newTemplatesHandler( )
        throws TransformerConfigurationException;
    public abstract TransformerHandler newTransformerHandler(Templates templates)
        throws TransformerConfigurationException;
    public abstract TransformerHandler newTransformerHandler( )
        throws TransformerConfigurationException;
    public abstract TransformerHandler newTransformerHandler(Source src)
        throws TransformerConfigurationException;
    public abstract XMLFilter newXMLFilter(Templates templates)
        throws TransformerConfigurationException;
    public abstract XMLFilter newXMLFilter(Source src)
        throws TransformerConfigurationException;
}

TemplatesHandler

This acts as a SAX 2 ContentHandler, which receives SAX events as a document is parsed. Once parsing is complete, it returns a Templates object. Instances are constructed using SAXTransformerFactory.

public interface TemplatesHandler
        extends ContentHandler {
    String getSystemId( );
    Templates getTemplates( );
    void setSystemId(String systemId);
}

TransformerHandler

Instances of this interface receive SAX events and produce Transformer objects once parsing is complete. Instances are constructed using SAXTransformerFactory.

public interface TransformerHandler
        extends ContentHandler, LexicalHandler, DTDHandler {
    String getSystemId( );
    Transformer getTransformer( );
    void setResult(Result result)
        throws IllegalArgumentException;
    void setSystemId(String systemId);
}
Package: javax.xml.transform.stream

This package defines how to perform transformations using Java I/O streams.

StreamResult

This allows transformation results to be sent to a File, Writer, or OutputStream.

public class StreamResult
        implements Result {
    public static final String FEATURE =
        "http://javax.xml.transform.stream.StreamResult/feature";
    public StreamResult(OutputStream outputStream);
    public StreamResult(Writer writer);
    public StreamResult(String systemId);
    public StreamResult(File f);
    public StreamResult( );
    public OutputStream getOutputStream( );
    public String getSystemId( );
    public Writer getWriter( );
    public void setOutputStream(OutputStream outputStream);
    public void setSystemId(File f);
    public void setSystemId(String systemId);
    public void setWriter(Writer writer);
}

StreamSource

This supports input from a URL, File, Reader, or InputStream. The system id is used to resolve relative URLs in the XML and XSLT.

public class StreamSource
        implements Source {
    public static final String FEATURE =
        "http://javax.xml.transform.stream.StreamSource/feature";
    public StreamSource(InputStream inputStream);
    public StreamSource(InputStream inputStream, String systemId);
    public StreamSource(Reader reader);
    public StreamSource(Reader reader, String systemId);
    public StreamSource(String systemId);
    public StreamSource(File f);
    public StreamSource( );
    public InputStream getInputStream( );
    public String getPublicId( );
    public Reader getReader( );
    public String getSystemId( );
    public void setInputStream(InputStream inputStream);
    public void setPublicId(String systemId);
    public void setReader(Reader reader);
    public void setSystemId(File f);
    public void setSystemId(String systemId);
}
Previous | Home | Next
A. Discussion Forum Code | Book Index | C. XSLT Quick Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Quick Reference (Java and XSLT)

 Java and XSLTSearch this book

Appendix C. XSLT Quick Reference

This appendix provides a quick reference to the XSLT markup language. Each element is listed in alphabetical order, along with a reference to the appropriate section in Version 1.0 of the XSLT specification available at http://www.w3.org/TR/xslt.

Attributes are shown along with their allowable values, and square brackets indicate optional attributes. Values enclosed in curly braces are treated as attribute value templates, and quoted values are literals. XML-style comments indicate which elements allow content and the allowable type of that content.

<xsl:apply-imports>
<xsl:apply-imports/>

See XSLT specification section 5.6: "Overriding Template Rules." http://www.w3.org/TR/xslt#apply-imports

<xsl:apply-templates>
<xsl:apply-templates
    [select = node-set-expression]
    [mode = qname]>
  <!-- Content: Any number of <xsl:sort> or <xsl:with-param> -->
</xsl:apply-templates>

See XSLT specification section 5.4: "Applying Template Rules." http://www.w3.org/TR/xslt#section-Applying-Template-Rules

<xsl:attribute>
<xsl:attribute
    name = {qname}
    [namespace = {uri-reference}]>
  <!-- Content: template -->
</xsl:attribute>

See XSLT specification section 7.1.3: "Creating Attributes with <xsl:attribute>." http://www.w3.org/TR/xslt#creating-attributes

<xsl:attribute-set>
<xsl:attribute-set
    name = qname
    [use-attribute-sets = qnames]>
  <!-- Content: Any number of <xsl:attribute> -->
</xsl:attribute-set>

See XSLT specification section 7.1.4: "Named Attribute Sets." http://www.w3.org/TR/xslt#attribute-sets

<xsl:call-template>
<xsl:call-template
    name = qname>
  <!-- Content: Any number of <xsl:with-param> -->
</xsl:call-template>

See XSLT specification section 6: "Named Templates." http://www.w3.org/TR/xslt#named-templates

<xsl:choose>
<xsl:choose>
 <!-- Content: One or more <xsl:when>, followed by an optional <xsl:otherwise> -->
</xsl:choose>

See XSLT specification section 9.2: "Conditional Processing with <xsl:choose>." http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:choose

<xsl:comment>
<xsl:comment>
  <!-- Content: template -->
</xsl:comment>

See XSLT specification section 7.4: "Creating Comments." http://www.w3.org/TR/xslt#section-Creating-Comments

<xsl:copy>
<xsl:copy
    [use-attribute-sets = qnames]>
  <!-- Content: template -->
</xsl:copy>

See XSLT specification section 7.5: "Copying." http://www.w3.org/TR/xslt#copying

<xsl:copy-of>
<xsl:copy-of
    select = expression/>

See XSLT specification section 11.3: "Using Values of Variables and Parameters with <xsl:copy-of>." http://www.w3.org/TR/xslt#copy-of

<xsl:decimal-format>
<xsl:decimal-format
    [name = qname]
    [decimal-separator = char]
    [grouping-separator = char]
    [infinity = string]
    [minus-sign = char]
    [NaN = string]
    [percent = char]
    [per-mille = char]
    [zero-digit = char]
    [digit = char]
    [pattern-separator = char]/>

See XSLT specification section 12.3: "Number Formatting." http://www.w3.org/TR/xslt#format-number

<xsl:element>
<xsl:element
    name = {qname}
    [namespace = {uri-reference}]
    [use-attribute-sets = qnames]>
  <!-- Content: template -->
</xsl:element>

See XSLT specification section 7.1.2: "Creating Elements with <xsl:element>." http://www.w3.org/TR/xslt#section-Creating-Elements-with-xsl:element

<xsl:fallback>
<xsl:fallback>
  <!-- Content: template -->
</xsl:fallback>

See XSLT specification section 15: "Fallback." http://www.w3.org/TR/xslt#fallback

<xsl:for-each>
<xsl:for-each
    select = node-set-expression>
  <!-- Content: Any number of <xsl:sort>, followed by template -->
</xsl:for-each>

See XSLT specification section 8: "Repetition." http://www.w3.org/TR/xslt#for-each

<xsl:if>
<xsl:if
    test = boolean-expression>
  <!-- Content: template -->
</xsl:if>

See XSLT specification section 9.1: "Conditional Processing with <xsl:if>." http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:if

<xsl:import>
<xsl:import
    href = uri-reference/>

See XSLT specification section 2.6.2: "Stylesheet Import." http://www.w3.org/TR/xslt#import

<xsl:include>
<xsl:include
    href = uri-reference/>

See XSLT specification section 2.6.1: "Stylesheet Inclusion." http://www.w3.org/TR/xslt#include

<xsl:key>
<xsl:key
    name = qname
    match = pattern
    use = expression/>

See XSLT specification section 12.2: "Keys." http://www.w3.org/TR/xslt#key

<xsl:message>
<xsl:message
    [terminate = "yes" or "no"]>
  <!-- Content: template -->
</xsl:message>

See XSLT specification section 13: "Messages." http://www.w3.org/TR/xslt#message

<xsl:namespace-alias>
<xsl:namespace-alias
    stylesheet-prefix = prefix or "#default"
    result-prefix = prefix or "#default"/>

See XSLT specification section 7.1.1: "Literal Result Elements." http://www.w3.org/TR/xslt#literal-result-element

<xsl:number>
<xsl:number
    [level = "single" or "multiple" or "any"]
    [count = pattern]
    [from = pattern]
    [value = number-expression]
    [format = {string}]
    [lang = {nmtoken}]
    [letter-value = {"alphabetic" or "traditional"}]
    [grouping-separator = {char}]
    [grouping-size = {number}]/>

See XSLT specification section 7.7: "Numbering." http://www.w3.org/TR/xslt#number

<xsl:otherwise>
<xsl:otherwise>
  <!-- Content: template -->
</xsl:otherwise>

See XSLT specification section 9.2: "Conditional Processing with <xsl:choose>." http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:choose

<xsl:output>
<xsl:output
    [method = "xml" or "html" or "text" or qname-but-not-ncname]
    [version = nmtoken]
    [encoding = string]
    [omit-xml-declaration = "yes" or "no"]
    [standalone = "yes" or "no"]
    [doctype-public = string]
    [doctype-system = string]
    [cdata-section-elements = qnames]
    [indent = "yes" or "no"]
    [media-type = string]/>

See XSLT specification section 16: "Output." http://www.w3.org/TR/xslt#output

<xsl:param>
<xsl:param
    name = qname
    [select = expression]>
  <!-- Content: template -->
</xsl:param>

See XSLT specification section 11: "Variables and Parameters." http://www.w3.org/TR/xslt#variables

<xsl:preserve-space>
<xsl:preserve-space
    elements = tokens/>

See XSLT specification section 3.4: "Whitespace Stripping." http://www.w3.org/TR/xslt#strip

<xsl:processing-instruction>
<xsl:processing-instruction
    name = {ncname}>
  <!-- Content: template -->
</xsl:processing-instruction>

See XSLT specification section 7.3: "Creating Processing Instructions." http://www.w3.org/TR/xslt#section-Creating-Processing-Instructions

<xsl:sort>
<xsl:sort
    [select = string-expression]
    [lang = {nmtoken}]
    [data-type = {"text" or "number" or qname-but-not-ncname}]
    [order = {"ascending" or "descending"}]
    [case-order = {"upper-first" or "lower-first"}]/>

See XSLT specification section 10: "Sorting." http://www.w3.org/TR/xslt#sorting

<xsl:strip-space>
<xsl:strip-space
    elements = tokens/>

See XSLT specification section 3.4: "Whitespace Stripping." http://www.w3.org/TR/xslt#strip

<xsl:stylesheet>
<xsl:stylesheet
    version = number
    [id = id]
    [extension-element-prefixes = tokens]
    [exclude-result-prefixes = tokens]>
  <!-- Content: Any number of <xsl:import>, followed by top-level-elements -->
</xsl:stylesheet>

See XSLT specification section 2.2: "Stylesheet Element." http://www.w3.org/TR/xslt#stylesheet-element

<xsl:template>
<xsl:template
    [match = pattern]
    [name = qname]
    [priority = number]
    [mode = qname]>
  <!-- Content: Any number of <xsl:param>, followed by template -->
</xsl:template>

See XSLT specification section 5.3: "Defining Template Rules." http://www.w3.org/TR/xslt#section-Defining-Template-Rules

<xsl:text>
<xsl:text
    [disable-output-escaping = "yes" or "no"]>
  <!-- Content: #PCDATA -->
</xsl:text>

See XSLT specification section 7.2: "Creating Text." http://www.w3.org/TR/xslt#section-Creating-Text

<xsl:transform>
<xsl:transform
    version = number
    [id = id]
    [extension-element-prefixes = tokens]
    [exclude-result-prefixes = tokens]>
  <!-- Content: Any number of <xsl:import>, followed by top-level-elements -->
</xsl:transform>

See XSLT specification section 2.2: "Stylesheet Element." http://www.w3.org/TR/xslt#stylesheet-element

<xsl:value-of>
<xsl:value-of
    select = string-expression
    [disable-ouput-escaping = "yes" or "no"]/>

See XSLT specification section 7.6.1: "Generating Text with <xsl:value-of>." http://www.w3.org/TR/xslt#value-of

<xsl:variable>
<xsl:variable
    name = qname
    [select = expression]>
  <!-- Content: template -->
</xsl:variable>

See XSLT specification section 11: "Variables and Parameters." http://www.w3.org/TR/xslt#variables

<xsl:when>
<xsl:when
    test = boolean-expression>
  <!-- Content: template -->
</xsl:when>

See XSLT specification section 9.2: "Conditional Processing with xsl:choose." http://www.w3.org/TR/xslt#section-Conditional-Processing-with-xsl:choose

<xsl:with-param>
<xsl:with-param
    name = qname
    [select = expression]>
  <!-- Content: template -->
</xsl:with-param>

See XSLT specification section 11.6: "Passing Parameters to Templates." http://www.w3.org/TR/xslt#section-Passing-Parameters-to-Templates

Previous | Home | Next
B. JAXP API Reference | Book Index | Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Colophon (Java and XSLT)

 Java and XSLTSearch this book
Previous

Colophon

Our look is the result of reader comments, our own experimentation, and feedback from distribution channels. Distinctive covers complement our distinctive approach to technical topics, breathing personality and life into potentially dry subjects.

The animals on the cover of Java and XSLT are ermines, also known as stoats or short-tailed weasels. Mustela erminea are found around the world between the Arctic Circle and approximately 40 degrees north latitude. Their body length, not including tail, ranges from 7 to 12 inches, with proportionately sized tails from 3 to 6 inches long. They weigh between 1.5 and 11.5 ounces. Males are, on average, larger than females.

Ermines can both swim and climb trees, and though they live in such diverse habitats as grasslands, tundra, and deep forests, they tend to prefer rocky or brushy areas. They are carnivores and eat mostly rodents, but they also enjoy small rabbits and birds, fish, bugs, and eggs. (Whether they take their eggs sunny-side-up or over-easy is still a matter of great debate.)

Female ermines give birth to only one litter per year. The mating season is in the late spring, but after fertilization, the ermine embryos stop developing and do not implant in the uterus for several months. Once implantation occurs, gestation takes only about a month, and the babies are born in the spring of the year following fertilization.

Ermines can live up to eight years, but their lifespan in the wild tends to be only a year or less. Their main killer is starvation, dependent as they are on fluctuating rodent populations, but they are also preyed upon by hawks, owls, and humans.

Ermine fur, which is harvested from both M. erminea and its cousin, M. frenata, the long-tailed weasel, is specifically the fur of an ermine in winter. At this time of year, their fur is stark white except for a small black tip on the tail, leading to the black-flecked, white-fur robes favored by stylish monarchs all over the world.

Matt Hutchinson was the production editor and copyeditor for Java and XSLT. Susan Carlson Greene proofread the book, and Leanne Soylemez and Emily Quill provided quality control. John Bickelhaupt wrote the index. James Carter provided production assistance.

Ellie Volckhausen designed the cover of this book, based on a series design by Edie Freedman. The cover image is a 19th-century engraving from the Dover Pictorial Archive. Emma Colby produced the cover layout with QuarkXPress 4.1 using Adobe's ITC Garamond font.

David Futato designed the interior layout, based on a series design by Nancy Priest. Neil Walls converted the files from Microsoft Word to FrameMaker 5.5.6 using tools created by Mike Sierra. The heading font is Bitstream Bodoni, the text font is ITC New Baskerville, and the code font is Constant Willison. The illustrations that appear in the book were produced by Robert Romano and Jessamyn Read using Macromedia FreeHand 9 and Adobe Photoshop 6. This colophon was written by Leanne Soylemez.

Previous | Home
Index | Book Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Java & XSLT: Index

Mastering Perl/TkSearch this book

Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Symbols & Numbers

* (asterisk): 2.4.3. Location Steps
@ (attribute selection): 2.1.1. An XSLT Example
{} (curly braces): 2.1.1. An XSLT Example
2.6.2. Attribute Value Templates
$ (dollar-sign): 3.2.1.2. Using variables
== (equality operator): 3.1.1. <xsl:if>
= (equals sign): 3.1.1. <xsl:if>
| (pipe) operator: 3.2.3. <xsl:param>and <xsl:with-param>
3.6.2. Stylesheet Functionality
& (ampersand), &amp; entity: 7.2.3. XSLT Stylesheets
&lt (encoding, left angle-bracket): 1.2.3. Validation
< (left angle-bracket): 1.2.3. Validation
/(forward slash): 2.1.1. An XSLT Example
2.2.4. A Skeleton Stylesheet
6.2.1. WAR Files
//(double forward slash): 9.3.4. Writing Effective XSLT

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

Accept HTTP header: 10.3.2.1. Identifying the client
Accept-Language HTTP header: 8.6.3. Putting It All Together
access synchronization, service method: 6.5.2. Thread Safety Tips
actions: 4.2.1. Web Frameworks
addElem( ): 6.3.3. Source Code
AddressDO.xml: 8.5. XSLT as a Code Generator
Address.java: 8.5. XSLT as a Code Generator
Altova XML Spy: 2.1.2. Trying It Out
Ant: 3. XSLT Part 2 -- Beyond the Basics
3.6. Ant Documentation Stylesheet
9.1. Development Environment
9.1.2. Ant
Ant documentation stylesheet: 3.6. Ant Documentation Stylesheet
build file structure: 3.6.1. Ant Basics
build files: 9.1.2.2. A typical Ant build file
CLASSPATH: 9.1.2.2. A typical Ant build file
global properties list: 3.6.2. Stylesheet Functionality
installing: 9.1.2.1. Installing Ant
JAR file conflicts, avoiding: 9.1.2.1. Installing Ant
make, as alternative to: 9.1.2. Ant
online resources: 3.6. Ant Documentation Stylesheet
output, specification: 3.6.3.1. Specifying XHTML output
style attributes: 9.1.2.3. Transforming using Ant's style task
transformations using: 9.1.2.3. Transforming using Ant's style task
version control: 3.6.1. Ant Basics
Antdoc.xslt: 3.6.3. The Complete Example
Apache Ant (see Ant)
Apache Tomcat (see Tomcat)
Apache Turbine (see Turbine)
Apache Xalan (see Xalan)
attribute value template (see AVT)
attributes: 1.2.2. XML Syntax
AVT (attribute value templates): 2.6.2. Attribute Value Templates
variables and HTML: 3.2.1.2. Using variables
axes: 2.4.2. Axes
abbreviated syntax: 2.4.5. Abbreviated Syntax

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

BoardSummary interface: 7.3.1. Domain Classes
A. Discussion Forum Code
BoardSummaryJDOM interface: 7.3.3. JDOM XML Production
boolean expressions: 3.1. Conditional Processing
Borland JBuilder (see JBuilder)
BrowserDetector class: 8.3. Identifying the Browser
BrowserID.java: 8.3. Identifying the Browser
browsers
fonts: 8.6.3. Putting It All Together
identifying: 8.3. Identifying the Browser
XSLT support: 1.5. Web Browser Support for XSLT
BufferedHttpResponseWrapper: 8.4.2. XSLT Transformation Filter
BufferedHttpResponseWrapper.java: 8.4.2. XSLT Transformation Filter
BufferedServletOutputStream.java: 8.4.2. XSLT Transformation Filter
build files: 3.6.1. Ant Basics
ByteArrayOutputStream class: 8.4.2. XSLT Transformation Filter

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

caching stylesheets: 5.4.2. A Stylesheet Cache
6.4.1. Integration with the Stylesheet Cache
8.1. XSLT Page Layout Templates
dynamic loading: 6.4. Stylesheet Caching Revisited
removing entries: 5.4.2. A Stylesheet Cache
CGI (Common Gateway Interface): 4.1.1. CGI
chaining filters: 8.4.1. Filter Overview
chap6.war: 6.3.5. How to Compile, Deploy, and Run
Tomcat, deploying to: 6.3.5. How to Compile, Deploy, and Run
character encoding: 8.6.2. Encodings
transparency: 8.6.3. Putting It All Together
characters( ): 1.2.4.1. SAX
child axis: 2.4.2. Axes
cHTML (Compact HTML): 10.1. Wireless Technologies
classes
BrowserDetector: 8.3. Identifying the Browser
ByteArrayOutputStream: 8.4.2. XSLT Transformation Filter
ClassNotFoundException: 9.1.1.3. Other exceptions and errors
DataAdapter: 7.3.2. Data Adapter Layer
DefaultHandler: 1.2.4.1. SAX
DocumentBuilder: B. JAXP API Reference
DocumentBuilderFactory: B. JAXP API Reference
DOMOutputter: 1.2.4.4. JDOM and DOM interoperability
DOMResult: B. JAXP API Reference
DOMSource: B. JAXP API Reference
FactoryConfigurationError: 9.1.1.3. Other exceptions and errors
B. JAXP API Reference
File: 5.3. Input and Output
FilterChain: 8.4.1. Filter Overview
JdbcDataAdapter: 7.3.2. Data Adapter Layer
MessageTree: 7.3.1. Domain Classes
OutputKeys: B. JAXP API Reference
ParserConfigurationException: B. JAXP API Reference
SAXParser: B. JAXP API Reference
SAXParserFactory: B. JAXP API Reference
SAXResult: B. JAXP API Reference
SAXSource: B. JAXP API Reference
SAXTransformerFactory: B. JAXP API Reference
ServletConfig: 6.2.4. Servlet API Highlights
ServletContext: 6.2.4. Servlet API Highlights
6.3.3. Source Code
StreamResult: B. JAXP API Reference
StreamSource: B. JAXP API Reference
StylesheetCache: 5.4.2. A Stylesheet Cache
6.4. Stylesheet Caching Revisited
Transformer: B. JAXP API Reference
TransformerConfigurationException: 5.2.4. JAXP XSLT Design
B. JAXP API Reference
TransformerException: B. JAXP API Reference
TransformerFactory: B. JAXP API Reference
TransformerFactoryConfigurationError: 5.2.4. JAXP XSLT Design
B. JAXP API Reference
ClassNotFoundException class: 9.1.1.3. Other exceptions and errors
CLASSPATH
common problems: 5.1. A Simple Example
JAR files, listing of: 6.3.5. How to Compile, Deploy, and Run
JAR files, version problems: 9.1.1. CLASSPATH Issues
loading of classes: 9.1.1.4. Java optional packages
source code directory, listing of: 6.3.5. How to Compile, Deploy, and Run
cleanup( ): 6.3.3. Source Code
code beautifiers: 8.5. XSLT as a Code Generator
Compact HTML (cHTML): 10.1. Wireless Technologies
compiling XSLT stylesheets: 5.4. Stylesheet Compilation
concurrency: 1.3.1. Web Development Challenges
conditional logic: 3.1. Conditional Processing
content: 1.2.2. XML Syntax
ContentHandler interface: 1.2.4.1. SAX
controller: 4.2.2. Model-View-Controller
cookies: 8.2.1. Servlet Session-Tracking API
enabled or disabled, checking: 8.2.2. Session Tracking with Java and XSLT
CORBA IDL (Common Object Request Broker Architecture Interface Definition Language): 1.2.4.3. JDOM
count( ): 3.1.1. <xsl:if>
createTopNavbar template: 8.1. XSLT Page Layout Templates
Crimson, online resources: 0.2. Software and Versions
CSS (Cascading Style Sheets), contrasted with XSLT stylesheets: 2.1. XSLT Introduction
curName variable: 3.6.3.2. Creating the dependency graph
6.3.3. Source Code
currencies, outputting: 3.4.1. Number Formatting
current node: 2.1.1. An XSLT Example
2.2.1. XML Tree Data Structure
current node list: 2.2.1. XML Tree Data Structure

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

data driven pattern matching: 2.5. Looping and Sorting
DataAdapter class: 7.3.2. Data Adapter Layer
DBUtil class: 7.3.2. Data Adapter Layer
debugging: 9.2. Testing and Debugging
JAXP 1.1 error listeners: 9.2.2. JAXP 1.1 Error Listeners
JUunit: 9.2.1. JUnit
XSLT transformer GUI: 9.2.3. A Custom XSLT Transformer GUI
deck: 10.3.1. A WML Example
DefaultHandler class: 1.2.4.1. SAX
dependent objects: 8.5. XSLT as a Code Generator
dependentObject.xslt: 8.5. XSLT as a Code Generator
deployment: 4.1.2. Servlets as CGI Replacements
deployment descriptors: 4.1.2. Servlets as CGI Replacements
6.2.2. Deployment Descriptor
filters: 8.4.2. XSLT Transformation Filter
MIME mappings, wireless devices: 10.3.2.2. Setting the content type
page layout templates: 8.1. XSLT Page Layout Templates
PersonalDataServlet class, expanded with: 6.3.4. Deployment
servlet version numbers: 6.2.2. Deployment Descriptor
destroy( ): 5.4.2. A Stylesheet Cache
development environments: 9. Development Environment, Testing, and Performance
development tools: 9.1. Development Environment
phone simulators: 10.1. Wireless Technologies
directory_en.xslt: 8.6.1. XSLT Stylesheet Design
directory_es.xslt: 8.6.1. XSLT Stylesheet Design
directory.xml: 8.6.1. XSLT Stylesheet Design
discussion forum web application: 2.1.1. An XSLT Example
7. Discussion Forum
BoardSummaryJDOM class: 7.3.3. JDOM XML Production
CSS: 7.2.1. XHTML Prototypes
deployment descriptor: 7.5.1. Deployment
development process: 7.1. Overall Process
enhancements: 7.5.2. Ideas for Enhancements
error messages: 7.4. Servlet Implementation
ErrorRenderer class: 7.4. Servlet Implementation
ForumServlet: 7.4. Servlet Implementation
home page, source code: 7.2.1. XHTML Prototypes
HomeJDOM class: 7.3.3. JDOM XML Production
HomeRenderer class: 7.4. Servlet Implementation
Java packages required: 7. Discussion Forum
PostMessageJDOM class: 7.3.3. JDOM XML Production
PostMsgRenderer class: 7.4. Servlet Implementation
PostMsgReqHandler class: 7.4. Servlet Implementation
Renderer class: 7.4. Servlet Implementation
ReqHandler class: 7.4. Servlet Implementation
ReqhandlerRegistry class: 7.4. Servlet Implementation
requirements: 7.1.1. Requirements
screen flow: 7.1.2. Screen Flow
security: 7.4. Servlet Implementation
servlet implementation: 7.4. Servlet Implementation
flow of control: 7.4. Servlet Implementation
View Message page: 7.2.2. XML Samples
ViewMessageJDOM class: 7.3.3. JDOM XML Production
ViewMonthJDOM class: 7.3.3. JDOM XML Production
ViewMonthRenderer class: 7.4. Servlet Implementation
ViewMonthReqHandler class: 7.4. Servlet Implementation
ViewMsgRenderer class: 7.4. Servlet Implementation
ViewMsgReqHandler class: 7.4. Servlet Implementation
WAR file: 7.5.1. Deployment
web pages: 7.1.2. Screen Flow
XHTML prototyping: 7.2.1. XHTML Prototypes
XML production using JDOM: 7.3.3. JDOM XML Production
XML sample data: 7.2.2. XML Samples
XSLT stylesheets: 7.2.3. XSLT Stylesheets
7.2.3. XSLT Stylesheets
home page: 7.2.3. XSLT Stylesheets
postMsg.xslt: 7.2.3. XSLT Stylesheets
viewMonth.xslt: 7.2.3. XSLT Stylesheets
XSLTRenderHelper class: 7.4. Servlet Implementation
DOCTYPE: 6.2.2. Deployment Descriptor
Document interface: 1.2.4.2. DOM
5.3.4. JAXP DOM I/O
document root element: 1.2.4.2. DOM
2.1.1. An XSLT Example
document type declarations: 1.2.3. Validation
DocumentBuilder class: B. JAXP API Reference
DocumentBuilderFactory class: B. JAXP API Reference
doFilter( ): 8.4.1. Filter Overview
doGet( ): 6.1.1. Splash Screen Servlet Example
6.1.1. Splash Screen Servlet Example
6.3.1. Design
DOM (Document Object Model): 1.2.4.2. DOM
CLASSPATH issues: 5.1. A Simple Example
factory methods, reliance on: 1.2.4.2. DOM
interfaces: 1.2.4.2. DOM
JAXP DOM I/O: 5.3.4. JAXP DOM I/O
JDOM, interoperability with: 1.2.4.4. JDOM and DOM interoperability
JDOM output, feeding into JAXP: 5.3.6.2. JDOM to DOM approach
non serializable implementations: 9.3.5.1. Sending XML from EJBs
XML elements, adding text to: 1.2.4.2. DOM
DOM tree: 1.2.4.2. DOM
domain classes: 7.3.1. Domain Classes
DOMLocator interface: B. JAXP API Reference
DOMOutputter class: 1.2.4.4. JDOM and DOM interoperability
5.3.6. Feeding JDOM Output into JAXP
DOMResult class: B. JAXP API Reference
DOMSource class: 5.3.4. JAXP DOM I/O
B. JAXP API Reference
doPost( ): 6.1.1. Splash Screen Servlet Example
6.3.1. Design
download optional packages: 9.1.1.4. Java optional packages
DTD (Document Type Definition): 1.2.3. Validation

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

editPersonalData.xslt: 6.3.1. Design
6.3.2. XML and Stylesheets
6.3.2. XML and Stylesheets
EJB (Enterprise JavaBeans)
address class: 8.5. XSLT as a Code Generator
dependent objects: 8.5. XSLT as a Code Generator
XSLT, and: 4.3. XSLT and EJB
Element interface: 1.2.4.2. DOM
elements: 1.2. XML Review
2.1.1. An XSLT Example
2.1.1. An XSLT Example
2.2.2. Recursive Processing with Templates
2.2.2. Recursive Processing with Templates
2.2.2. Recursive Processing with Templates
2.2.4. A Skeleton Stylesheet
2.5.1. Looping with <xsl:for-each>
2.6.1. <xsl:attribute>
2.6.3. <xsl:attribute-set>
2.6.3. <xsl:attribute-set>
3.1. Conditional Processing
3.1.1. <xsl:if>
3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise>
3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise>
3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise>
3.2.1. <xsl:variable>
3.2.2. <xsl:call-template> and Named Templates
3.2.3. <xsl:param>and <xsl:with-param>
3.2.3. <xsl:param>and <xsl:with-param>
3.3.1. <xsl:include>
3.3.2. <xsl:import>
3.5.2. The Identity Transformation
3.5.2. The Identity Transformation
3.5.3. Transforming Elements and Attributes
3.6.1. Ant Basics
5.2.3. The Transformer Class
6.3.2. XML and Stylesheets
8.5. XSLT as a Code Generator
10.3.1. A WML Example
10.3.1. A WML Example
10.3.3.3. WML prototypes and screen shots
content and attributes: 1.2.2. XML Syntax
[elements timer\\>: 10.3.3.3. WML prototypes and screen shots
top level elements: 2.6.3. <xsl:attribute-set>
empty elements: 1.2.2. XML Syntax
empty templates: 3.5.3. Transforming Elements and Attributes
encodeURL( ): 8.2.1. Servlet Session-Tracking API
endDocument( ): 1.2.4.1. SAX
endElement( ): 1.2.4.1. SAX
Ericsson WapIDE: 10.1. Wireless Technologies
ErrorListener interface: B. JAXP API Reference
ErrorRenderer.java: 7.4. Servlet Implementation
example code
BoardSummaryImpl.java(2): A. Discussion Forum Code
DataException.java: A. Discussion Forum Code
DataUtil.java: A. Discussion Forum Code
DayMonthYear.java: A. Discussion Forum Code
FakeDataAdapter.java: A. Discussion Forum Code
MessageImpl.java: A. Discussion Forum Code
MessageSummaryImpl.java: A. Discussion Forum Code
MonthYear.java: A. Discussion Forum Code
viewMsg.xslt XSLT stylesheet: A. Discussion Forum Code
expanded deployment descriptor: 6.3.4. Deployment

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

FactoryConfigurationError class: 9.1.1.3. Other exceptions and errors
B. JAXP API Reference
File class: 5.3. Input and Output
filename extensions, XSLT stylesheets: 2.3. Another XSLT Example, Using XHTML
filenames (platform-specific), converting to system IDs: 5.3.1. System Identifiers, Files, and URLs
Filter deployment descriptor: 8.4.2. XSLT Transformation Filter
Filter interface: 8.4.1. Filter Overview
FilterChain class: 8.4.1. Filter Overview
filters: 8.4. Servlet Filters
drawbacks: 8.4.3. Closing Thoughts on Filters
uses: 8.4.1. Filter Overview
XSLT transformation: 8.4.2. XSLT Transformation Filter
flushAll( ): 5.4.2. A Stylesheet Cache
format attribute, WML: 10.3.1. A WML Example
format-number( ): 3.4.1. Number Formatting
forum.css: 7.2.1. XHTML Prototypes
ForumServlet: 7.4. Servlet Implementation
ForumServlet.java: 7.4. Servlet Implementation
forward slash: 2.1.1. An XSLT Example
6.2.1. WAR Files
pattern template: 2.2.4. A Skeleton Stylesheet

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

GET requests: 6.1.1. Splash Screen Servlet Example
getOutputProperties( ): 5.4.1. Templates API
getOutputStream( ): 6.1.1. Splash Screen Servlet Example
8.4.2. XSLT Transformation Filter
ServletResponse interface: 8.4.1. Filter Overview
ServletResponseWrapper class: 8.4.1. Filter Overview
getParameter( ): 6.1.1. Splash Screen Servlet Example
getPersonalData( ): 6.3.3. Source Code
getResource( ): 6.2.4. Servlet API Highlights
getResourceAsStream( ): 6.2.4. Servlet API Highlights
getServletInfo( ): 6.1.1. Splash Screen Servlet Example
getTodaysShows( ): 4.1.2. Servlets as CGI Replacements
getWriter( ): 6.1.1. Splash Screen Servlet Example
8.4.2. XSLT Transformation Filter
global. naming convention: 7.2.3. XSLT Stylesheets
global properties list, Ant buildfiles: 3.6.2. Stylesheet Functionality
global variables: 3.2.1. <xsl:variable>

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

Home page WML: 10.3.3.3. WML prototypes and screen shots
Home page XSLT: 10.3.3.5. XSLT stylesheets
HomeJDOM.java: 7.3.3. JDOM XML Production
HomeRenderer.java: 7.4. Servlet Implementation
home.xml: 8.1. XSLT Page Layout Templates
HTML (Hypertext Markup Language)
problems with: 1.1.1. What's Wrong with HTML?
web development, limitations for: 1.3.1. Web Development Challenges
wireless technologies, incompatibilities with: 10.1.1. Why Not HTML?
HTTP (Hypertext Transfer Protocol): 1.3.1. Web Development Challenges
headers: 10.3.2.1. Identifying the client
statelessness: 4.2.2. Model-View-Controller
HttpServlet class: 6.1.1. Splash Screen Servlet Example
HttpServletRequest interface: 6.1.1. Splash Screen Servlet Example
HttpServletRequestWrapper class: 8.4.1. Filter Overview
HttpServletResponse interface: 6.1.1. Splash Screen Servlet Example
6.1.1. Splash Screen Servlet Example
HttpServletResponseWrapper class: 8.4.1. Filter Overview
HttpSession interface: 8.2.1. Servlet Session-Tracking API

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

i-mode protocol: 10.1. Wireless Technologies
IBM Alphaworks LotusXSL: 1.4.1.2. LotusXSL
identification of browsers: 8.3. Identifying the Browser
identity transformations: 3.5.2. The Identity Transformation
result file: 3.5.4. The Result File
if/else syntax, XSLT: 3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise>
il8n (internationalization): 8.6. Internationalization with XSLT
troubleshooting: 8.6.4. I18n Troubleshooting Checklist
XSLT stylesheet design: 8.6.1. XSLT Stylesheet Design
immutable objects, thread safety immutable objects: 6.5.2.2. Tip 2: Immutable objects are thread-safe
i18n.html: 8.6.3.3. Web page and servlet
init( ): 6.3.3. Source Code
stylesheets, location of: 6.3.6. Locating Stylesheets with Initialization Parameters
TemplateServlet.java: 8.1. XSLT Page Layout Templates
initialization parameters
stylesheets, location with: 6.3.6. Locating Stylesheets with Initialization Parameters
input/output, XSLT processors: 5.3. Input and Output
JAXP DOM I/O: 5.3.4. JAXP DOM I/O
JAXP I/O design: 5.3.2. JAXP I/O Design
JAXP SAX I/O: 5.3.5. JAXP SAX I/O
JAXP stream I/O: 5.3.3. JAXP Stream I/O
JDOM output, feeding into JAXP: 5.3.6. Feeding JDOM Output into JAXP
system identifiers, files, and URLs: 5.3.1. System Identifiers, Files, and URLs
installed optional package: 9.1.1.4. Java optional packages
integrated development environments, problems with: 9.1.1.5. Java IDEs
interfaces
BoardSummary: 7.3.1. Domain Classes
A. Discussion Forum Code
ContentHandler: 1.2.4.1. SAX
Document: 1.2.4.2. DOM
5.3.4. JAXP DOM I/O
DOMLocator: B. JAXP API Reference
Element: 1.2.4.2. DOM
ErrorListener: B. JAXP API Reference
Filter: 8.4.1. Filter Overview
Message: 7.3.1. Domain Classes
A. Discussion Forum Code
MessageSummary: 7.3.1. Domain Classes
A. Discussion Forum Code
Result: B. JAXP API Reference
Source: 5.3.1. System Identifiers, Files, and URLs
B. JAXP API Reference
SourceLocator: B. JAXP API Reference
Templates: B. JAXP API Reference
TemplatesHandler: B. JAXP API Reference
TransformerHandler: B. JAXP API Reference
URIResolver: B. JAXP API Reference
internationalization (see il8n)
Internet Explorer (see under Microsoft)
interoperability between DOM, JDOM: 1.2.4.4. JDOM and DOM interoperability
interpreters: 5.4. Stylesheet Compilation
ISO/IEC 10646: 8.6.2. Encodings
isRequestedSessionIdFromCookie( ): 8.2.2. Session Tracking with Java and XSLT
iterative design: 7.2.2. XML Samples

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

jar command, creating WAR files with: 6.2.1. WAR Files
JAR file issues: 9.1.1.1. JAR hell?
Ant, incompatibility with JAXP 1.1: 9.1.2.1. Installing Ant
sealed JAR files: 9.1.1.2. Sealing violations
versioning: 6.3.5. How to Compile, Deploy, and Run
9.1.1.1. JAR hell?
Java
Ant (see Ant)
APIs for XML: 1.2.4. Java and XML
integrated development environments: 9.1.1.5. Java IDEs
servlets: 1.3. Beyond Dynamic Web Pages
4.1.2. Servlets as CGI Replacements
switch statement, XSLT equivalent to: 3.1.2. <xsl:choose>, <xsl:when>, and <xsl:otherwise>
webpage presentation, and: 1.1.3. The XSLT Solution
XSLT, and, program example: 5.1. A Simple Example
XSLT processors, used for: 0. Preface
java.util.Map: 5.4.2. A Stylesheet Cache
javax.xml.transform.dom: B. JAXP API Reference
javax.xml.transform.OutputKeys, constants: 5.2.3. The Transformer Class
javax.xml.transform.sax: B. JAXP API Reference
javax.xml.transform.stream: B. JAXP API Reference
javax.xm..transform: B. JAXP API Reference
javaz.xml.parsers: B. JAXP API Reference
JAXP 1.1, backwards compatibility: 9.1.1.3. Other exceptions and errors
JAXP (Java API for XML Processing): 1.4.1.4. JAXP
configuration exceptions: 9.1.1.3. Other exceptions and errors
error handlers: 5.2.4. JAXP XSLT Design
factory, locating: 5.2.2. XSLT Plugability Layer
input and output: 5.3. Input and Output
JDOM output, feeding into: 5.3.6. Feeding JDOM Output into JAXP
online resources: 0.2. Software and Versions
role in parsing: 1.2.4.2. DOM
Version 1.1: 5.2. Introduction to JAXP 1.1
B. JAXP API Reference
online resources: B. JAXP API Reference
JBuilder: 9.1.1.5. Java IDEs
JdbcDataAdapter class: 7.3.2. Data Adapter Layer
JDK 1.2, online resources: 0.2. Software and Versions
JDOM (Java Document Object Model): 1.2.4.3. JDOM
beta 6, online resources: 0.2. Software and Versions
DOM, interoperability with: 1.2.4.4. JDOM and DOM interoperability
online resources: 1.2.4.3. JDOM
output, feeding into JAXP: 5.3.6. Feeding JDOM Output into JAXP
JDOM to text approach: 5.3.6.3. JDOM to text approach
producer classes: 7.3.3. JDOM XML Production
Sun JSR, and: 1.2.4.3. JDOM
XML generation with: 1.2.4.3. JDOM
XML production using: 7.3.3. JDOM XML Production
XSLT processors, support by: 1.2.4.4. JDOM and DOM interoperability
JIndent: 8.5. XSLT as a Code Generator
jsessionid: 8.2.1. Servlet Session-Tracking API
8.2.1. Servlet Session-Tracking API
JSP (JavaServer Pages): 4.1.3. JSP
embedded HTML, and: 4.1.3. JSP
error handling: 4.1.3. JSP
namespace prefixes: 4.1.3. JSP
RequestDispatcher class: 4.1.5. The Hybrid Approach
tag handlers: 4.1.3. JSP
validation: 4.1.3. JSP
JUnit: 9.1. Development Environment
v. 3.7., online resources: 0.2. Software and Versions
JVM (Java Virtual Machine)
CGI programming, unsuitability for: 4.1.1. CGI
classes, loading of: 9.1.1.4. Java optional packages
XSLT processor, as: 1.4.1. Java XSLT Processor Choices
JVMPI (Java Virtual Machine Profiling Interface): 9.3.2.2. JVMPI

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

LanguageDemo.java: 8.6.3.3. Web page and servlet
last( ): 3.1.1. <xsl:if>
list mode: 3.2.5. Template Modes
locale-specific content: 8.6.1. XSLT Stylesheet Design
location paths: 2.4.4. Location Paths
location step: 2.4.3. Location Steps
log( ): 6.3.3. Source Code
logging file: 3.5.1. An Example XML File
looping and sorting, code example: 2.5.3. Looping and Sorting Examples
loops: 3.1.1. <xsl:if>
LotusXSL: 1.4.1.2. LotusXSL
Lynx User-Agent: 8.3. Identifying the Browser

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

main(): 1.2.4.2. DOM
MapEntry class: 5.4.2. A Stylesheet Cache
markup languages: 1.2. XML Review
1.2.1. SGML, XML, and Markup Languages
Message interface: 7.3.1. Domain Classes
A. Discussion Forum Code
MessageSummary interface: 7.3.1. Domain Classes
A. Discussion Forum Code
MessageTree class: 7.3.1. Domain Classes
META-INFÚMANIFEST.MF file: 9.1.1.1. JAR hell?
metalanguages: 1.2.1. SGML, XML, and Markup Languages
methods
access synchronization, drawbacks to: 6.5.1. Servlet Threading Model
addElem( ): 6.3.3. Source Code
characters( ): 1.2.4.1. SAX
cleanup( ): 6.3.3. Source Code
count( ): 3.1.1. <xsl:if>
destroy( ): 5.4.2. A Stylesheet Cache
doFilter( ): 8.4.1. Filter Overview
doGet( ): 6.1.1. Splash Screen Servlet Example
6.1.1. Splash Screen Servlet Example
6.3.1. Design
doPost( ): 6.1.1. Splash Screen Servlet Example
6.3.1. Design
encodeURL( ): 8.2.1. Servlet Session-Tracking API
endDocument( ): 1.2.4.1. SAX
endElement( ): 1.2.4.1. SAX
flushAll( ): 5.4.2. A Stylesheet Cache
format-number( ): 3.4.1. Number Formatting
getOutputProperties( ): 5.4.1. Templates API
getOutputStream( ): 6.1.1. Splash Screen Servlet Example
8.4.2. XSLT Transformation Filter
getParameter( ): 6.1.1. Splash Screen Servlet Example
getPersonalData( ): 6.3.3. Source Code
getResource( ): 6.2.4. Servlet API Highlights
getResourceAsStream( ): 6.2.4. Servlet API Highlights
getServletInfo( ): 6.1.1. Splash Screen Servlet Example
getWriter( ): 6.1.1. Splash Screen Servlet Example
8.4.2. XSLT Transformation Filter
init( ): 6.3.3. Source Code
stylesheets, location of: 6.3.6. Locating Stylesheets with Initialization Parameters
log( ): 6.3.3. Source Code
main(): 1.2.4.2. DOM
newTransformer( ): 5.4.1. Templates API
5.4.2. A Stylesheet Cache
service( ): 6.5.2. Thread Safety Tips
setSystemId( ): 6.3.3. Source Code
showErrorPage( ): 6.3.3. Source Code
showPage( ): 6.3.3. Source Code
startDocument( ): 1.2.4.1. SAX
startElement( ): 1.2.4.1. SAX
synchronization: 5.4.2. A Stylesheet Cache
toURL( ): 5.1.2. Xalan 1 Implementation
Transformer( ): 5.4.2. A Stylesheet Cache
Microsoft
Internet Explorer
font configuration, determining: 8.6.3. Putting It All Together
User-Agent: 8.3. Identifying the Browser
XSLT support: 1.5. Web Browser Support for XSLT
7.2.3. XSLT Stylesheets
MSXML parser: 1.5. Web Browser Support for XSLT
Windows, Unicode support: 8.6.2.1. Creating the XML and XSLT
model: 4.2.2. Model-View-Controller
modes: 3.2.5. Template Modes
Motorola Mobile Application Development Kit: 10.1. Wireless Technologies
Movie listing XSLT: 10.3.3.5. XSLT stylesheets
Movie theater XML datafile: 10.3.3.2. XML data
MSXML parser: 1.5. Web Browser Support for XSLT
MVC (model-view-controller): 4.2. The Universal Design
advantages: 4.2.2. Model-View-Controller
classes: 4.2.2. Model-View-Controller
controller, purpose: 4.2.2. Model-View-Controller
dependencies: 4.2.2. Model-View-Controller
scalability: 4.2.2. Model-View-Controller
MySQL dump: 7.3.2. Data Adapter Layer

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

named templates: 3.2.2. <xsl:call-template> and Named Templates
namespace prefixes (JSP): 4.1.3. JSP
namespaces: 2.1.1. An XSLT Example
and typos: 2.1.1. An XSLT Example
Netscape, XSLT support: 1.5. Web Browser Support for XSLT
Netscape 6
font configuration, determining: 8.6.3. Putting It All Together
HTTP request: 8.3. Identifying the Browser
Netscape Navigator User-Agent: 8.3. Identifying the Browser
newTransformer( ): 5.4.1. Templates API
5.4.2. A Stylesheet Cache
NJStar Chinese word processor: 8.6.2.1. Creating the XML and XSLT
node( ): 3.5.2. The Identity Transformation
Node interface: 1.2.4.2. DOM
node set functions: 3.1.1. <xsl:if>
nodes, as defined by XPath: 2.4.2. Axes
Nokia WAP Toolkit: 10.1. Wireless Technologies
NTT DoCoMo: 10.1. Wireless Technologies
number formatting: 3.4.1. Number Formatting
numbers_chinese.xslt: 8.6.3.2. XSLT stylesheets
numbers_english.xslt: 8.6.3.2. XSLT stylesheets
numbers_spanish.xslt: 8.6.3.2. XSLT stylesheets

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

online resources
Ant: 3.6. Ant Documentation Stylesheet
JAXP version 1.1: B. JAXP API Reference
JDOM: 1.2.4.3. JDOM
LotusXSL: 1.4.1.2. LotusXSL
NJStar Chinese word processor: 8.6.2.1. Creating the XML and XSLT
phone simulators: 10.1. Wireless Technologies
SAX (Simple API for XML): 1.2.4.1. SAX
SAXON: 1.4.1.3. SAXON
Tomcat: 6.1.1. Splash Screen Servlet Example
Turbine: 8.3. Identifying the Browser
Unicode: 8.6.2. Encodings
WML: 10.3.1. A WML Example
Xalan: 2.1.1. An XSLT Example
XML Schema: 1.2.3. Validation
XML Spy: 2.1.2. Trying It Out
Openwave Software Development Kit: 10.1. Wireless Technologies
Opera User-Agent: 8.3. Identifying the Browser
output
number formatting: 3.4.1. Number Formatting
text formatting: 3.4.2. Text Formatting
output streams
BufferedServletOutputStream class: 8.4.2. XSLT Transformation Filter
ByteArrayOutputStream class: 8.4.2. XSLT Transformation Filter
ServletOutputStream class: 8.4.1. Filter Overview
OutputKeys class: B. JAXP API Reference
OutputKeys class, constants: 5.2.3. The Transformer Class
OutputStreamWriter class
encoded text files, creation with: 8.6.2.1. Creating the XML and XSLT

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

packages
javax.xml.parsers: B. JAXP API Reference
javax.xml.transform: B. JAXP API Reference
javax.xml.transform.dom: B. JAXP API Reference
javax.xml.transform.sax: B. JAXP API Reference
javax.xml.transform.stream: B. JAXP API Reference
page layout templates: 8.1. XSLT Page Layout Templates
deployment descriptors: 8.1. XSLT Page Layout Templates
WAR file content: 8.1. XSLT Page Layout Templates
web.xml: 8.1. XSLT Page Layout Templates
parameters: 3.2.3. <xsl:param>and <xsl:with-param>
parent axis: 2.4.2. Axes
parsed character data: 1.2.3. Validation
ParserConfigurationException class: B. JAXP API Reference
parsers: 1.2.4. Java and XML
propretary differences: 1.2.4.2. DOM
pathnames of stylesheets, encoding of: 6.3.3. Source Code
pattern matching: 2.5. Looping and Sorting
XML data: 2.4.1. How XSLT Uses XPath
#PCDATA: 1.2.3. Validation
1.2.3. Validation
percentages, outputting: 3.4.1. Number Formatting
PersonalData class: 6.3.1. Design
6.3.3. Source Code
validation method: 6.3.3. Source Code
PersonalData.java: 6.3.3. Source Code
PersonalDataServlet: 6.3.1. Design
cache, integration with: 6.4.1. Integration with the Stylesheet Cache
PersonalDataServlet.java: 6.3.3. Source Code
PersonalDataXML helper class: 6.3.1. Design
PersonalDataXML.java: 6.3.3. Source Code
phone simulators: 10.1. Wireless Technologies
plugability layers: 5.2. Introduction to JAXP 1.1
position( ): 3.1.1. <xsl:if>
POST requests: 6.1.1. Splash Screen Servlet Example
PostMessageJDOM.java: 7.3.3. JDOM XML Production
PostMsgRenderer.java: 7.4. Servlet Implementation
PostMsgReqHandler.java: 7.4. Servlet Implementation
postMsg.xslt: 7.2.3. XSLT Stylesheets
principal node type: 2.4.3. Location Steps
println( ): 4.1.2. Servlets as CGI Replacements
problems, likely causes: 5.1. A Simple Example
processors for XSLT: 0. Preface
1.1. Java, XSLT, and the Web
projects: 3.6.1. Ant Basics

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

There are no index entries for this letter.

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

relative pathnames: 6.3.3. Source Code
Renderer.java: 7.4. Servlet Implementation
ReqHandler.java: 7.4. Servlet Implementation
ReqHandlerRegistry.java: 7.4. Servlet Implementation
RequestDispatcher class: 4.1.5. The Hybrid Approach
RequestHandler: 4.3. XSLT and EJB
reset( ): 8.4.2. XSLT Transformation Filter
Result interface: 5.2.1. JAXP 1.1 Implementation
5.3.2. JAXP I/O Design
B. JAXP API Reference
result tree: 2.1. XSLT Introduction
root element: 1.2.2. XML Syntax
WML: 10.3.1. A WML Example
root node
XML data tree: 2.1.1. An XSLT Example
2.2.1. XML Tree Data Structure

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

SAX (Simple API for XML): 1.2.4.1. SAX
events, piping from JDOM to JAXP: 5.3.6.1. JDOM to SAX approach
javax.xml.parsers: B. JAXP API Reference
JAXP SAX I/O: 5.3.5. JAXP SAX I/O
versions: 1.2.4.1. SAX
SAXON: 1.4.1.3. SAXON
SAXOutputter class: 5.3.6. Feeding JDOM Output into JAXP
5.3.6.1. JDOM to SAX approach
SAXParser class: B. JAXP API Reference
SAXParserFactory class: B. JAXP API Reference
SAXResult class: B. JAXP API Reference
SAXSource class: B. JAXP API Reference
SAXTransformerFactory class: B. JAXP API Reference
Schedule class: 4.1.2. Servlets as CGI Replacements
ScheduleJDOM.java: 4.2.3. XSLT Implementation
XML output: 4.2.3. XSLT Implementation
schedule.jsp: 4.1.3. JSP
ScheduleServlet.java: 4.1.2. Servlets as CGI Replacements
scheduling program: 2.3. Another XSLT Example, Using XHTML
schedule.xml: 2.3. Another XSLT Example, Using XHTML
schedule.xslt: 2.3. Another XSLT Example, Using XHTML
schema evolution: 2.1. XSLT Introduction
3.5. Schema Evolution
security, web applications: 7.4. Servlet Implementation
SecurityException: 9.1.1.2. Sealing violations
service( ): 6.5.2. Thread Safety Tips
servlet, threading safety tips: 6.5.2. Thread Safety Tips
servlet containers: 1.4. Getting Started
4.1.2. Servlets as CGI Replacements
ServletConfig class: 6.2.4. Servlet API Highlights
ServletContext class: 6.2.4. Servlet API Highlights
6.3.3. Source Code
ServletOutputStream class: 8.4.1. Filter Overview
ServletRequestWrapper class: 8.4.1. Filter Overview
ServletResponse interface: 8.4.1. Filter Overview
ServletResponseWrapper class: 8.4.1. Filter Overview
servlets: 1.3. Beyond Dynamic Web Pages
4.1.2. Servlets as CGI Replacements
API: 6.2.4. Servlet API Highlights
classes: 6.2.4. Servlet API Highlights
client identification: 10.3.2. Servlets and WML
definition: 6.2.2. Deployment Descriptor
deployment: 4.1.2. Servlets as CGI Replacements
design: 6.3.1. Design
DOM and XSLT, using: 6.3. Another Servlet Example
filters: 8.4. Servlet Filters
drawbacks: 8.4.3. Closing Thoughts on Filters
Version 2.3 wrapper classes: 8.4.1. Filter Overview
XSLT transformation: 8.4.2. XSLT Transformation Filter
internationalization: 8.6.3.3. Web page and servlet
LanguageDemo.java: 8.6.3.3. Web page and servlet
session tracking using: 8.2.1. Servlet Session-Tracking API
stylesheets, location: 6.3.6. Locating Stylesheets with Initialization Parameters
syntax: 6.1. Servlet Syntax
threading: 6.5. Servlet Threading Issues
SingleThreadModel interface: 6.5.1. Servlet Threading Model
User-Agent, ascertaining, code for: 8.3. Identifying the Browser
WML implementation: 10.3.3.4. Servlet implementation
session identifier: 8.2.1. Servlet Session-Tracking API
session tracking: 8.2. Session Tracking Without Cookies
without cookies: 8.2.1. Servlet Session-Tracking API
XSLT, using: 8.2.2. Session Tracking with Java and XSLT
Session-tracking code: 8.2.2. Session Tracking with Java and XSLT
Session-tracking XSLT stylesheet: 8.2.2. Session Tracking with Java and XSLT
setBufferSize( ): 8.4.2. XSLT Transformation Filter
setSystemId( ): 5.3.1. System Identifiers, Files, and URLs
5.3.3. JAXP Stream I/O
6.3.3. Source Code
SGML (Standard Generalized Markup Language): 1.2.1. SGML, XML, and Markup Languages
Show classes: 4.1.2. Servlets as CGI Replacements
showErrorPage( ): 6.3.3. Source Code
showPage( ): 6.3.3. Source Code
Showtimes XSLT: 10.3.3.5. XSLT stylesheets
SimpleJaxp.java: 5.2.1. JAXP 1.1 Implementation
SimpleSaxon.java: 5.1.3. SAXON Implementation
SimpleXalan1.java: 5.1.2. Xalan 1 Implementation
SingleThreadModel interface: 6.5.1. Servlet Threading Model
6.5.1. Servlet Threading Model
skeleton XSLT stylesheet: 2.2.4. A Skeleton Stylesheet
SOAP (Simple Object Access Protocol): 1.3.3.1. SOAP
software development, testing and debugging: 9.2. Testing and Debugging
software development environments: 9. Development Environment, Testing, and Performance
sorting: 2.5.2. Sorting
Source interface: 5.2.1. JAXP 1.1 Implementation
5.3.1. System Identifiers, Files, and URLs
5.3.2. JAXP I/O Design
B. JAXP API Reference
SourceLocator interface: B. JAXP API Reference
SplashScreenServlet.java: 6.1.1. Splash Screen Servlet Example
Tomcat, deploying to: 6.2.3. Deploying SplashScreenServlet to Tomcat
web.xml: 6.2.2. Deployment Descriptor
startDocument( ): 1.2.4.1. SAX
startElement( ): 1.2.4.1. SAX
state information: 1.3.1. Web Development Challenges
stateless protocols: 4.2.2. Model-View-Controller
storyboarding: 7.1.2. Screen Flow
StreamResult class: B. JAXP API Reference
StreamResult interface: 5.3.3. JAXP Stream I/O
streams, JAXP I/O: 5.3.3. JAXP Stream I/O
Streams class (example): 5.3.3. JAXP Stream I/O
StreamSource class: 5.2.1. JAXP 1.1 Implementation
5.3.3. JAXP Stream I/O
B. JAXP API Reference
StringReader class: 5.3.6.3. JDOM to text approach
StringWriter class: 5.3.6.3. JDOM to text approach
style class: 7.2.1. XHTML Prototypes
StylesheetCache class: 5.4.2. A Stylesheet Cache
6.4. Stylesheet Caching Revisited
stylesheets: 0.6. Acknowledgments
1.1. Java, XSLT, and the Web
compilation: 5.4. Stylesheet Compilation
examples: 2.1. XSLT Introduction
numberFormatting.xslt: 3.4.1. Number Formatting
Sun Solaris, internationalization support: 8.6.3. Putting It All Together
synchronized keyword: 6.5.1. Servlet Threading Model
system identifiers: 5.1.2. Xalan 1 Implementation
5.3.1. System Identifiers, Files, and URLs
StreamSource, providing for: 5.3.3. JAXP Stream I/O

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

table mode: 3.2.5. Template Modes
tag handlers (JSP): 4.1.3. JSP
tags: 1.2. XML Review
7.2.1. XHTML Prototypes
7.2.1. XHTML Prototypes
navigation areas, XHTML pages: 8.1. XSLT Page Layout Templates
XPath, locating using: 8.1. XSLT Page Layout Templates
targets: 3.6.1. Ant Basics
template driven pattern matching: 2.5. Looping and Sorting
template engines: 4.1.4. Template Engines
template modes: 3.2.5. Template Modes
template rules, XSLT processors: 2.2.3. Built-in Template Rules
templatePage.xslt: 8.1. XSLT Page Layout Templates
templates: 2.1.1. An XSLT Example
breaking stylesheets into: 2.2.4. A Skeleton Stylesheet
createTopNavbar: 8.1. XSLT Page Layout Templates
empty templates: 3.5.3. Transforming Elements and Attributes
identity transformations: 3.5.2. The Identity Transformation
result file: 3.5.4. The Result File
pattern matching, multiple templates: 3.5.3. Transforming Elements and Attributes
Templates interface: 5.2.1. JAXP 1.1 Implementation
B. JAXP API Reference
thread safety: 6.5.2.4. Tip 4: Understand the Templates interface
TemplateServlet.java: 8.1. XSLT Page Layout Templates
TemplatesHandler interface: B. JAXP API Reference
testing: 9.2. Testing and Debugging
text formatting: 3.4.2. Text Formatting
thread safety: 6.5.2. Thread Safety Tips
controlled access: 6.5.2.3. Tip 3: Provide a single point of entry
local variables: 6.5.2.1. Tip 1: Local variables are thread-safe
Templates interface: 6.5.2.4. Tip 4: Understand the Templates interface
threaded discussion forum: 7.1.1. Requirements
Tomcat: 1.4. Getting Started
9.1.3. Tomcat
online resources: 6.1.1. Splash Screen Servlet Example
root directory: 6.1.1. Splash Screen Servlet Example
v. 4.0, online resources: 0.2. Software and Versions
top level elements: 2.6.3. <xsl:attribute-set>
toURL( ): 5.1.2. Xalan 1 Implementation
transformations: 0.6. Acknowledgments
1. Introduction
1.1. Java, XSLT, and the Web
2.2. Transformation Process
AntÕs task: 9.1.2.3. Transforming using Ant's style task
client or server: 2.1. XSLT Introduction
client-side: 1.5. Web Browser Support for XSLT
Microsoft products, support within: 1.5. Web Browser Support for XSLT
CVS files, on: 5.3.5.4. Using the parser
DTDs, validating with: 9.2. Testing and Debugging
dynamic XML: 7.3. Making the XML Dynamic
filters and: 8.4.3. Closing Thoughts on Filters
javax.xml.transform package: B. JAXP API Reference
javax.xml.transform.dom package: B. JAXP API Reference
javax.xml.transform.stream: B. JAXP API Reference
javax.xml.transfrom.sax: B. JAXP API Reference
JAXP support: 1.4.1.4. JAXP
maximizing performance: 9.3. Performance Techniques
performance penalties: 9.3. Performance Techniques
process, recursion: 2.2.2. Recursive Processing with Templates
required components: 2.1.1. An XSLT Example
SAX-based transformations: 5.3.5.2. Detailed CSV to SAX design
task, Ant: 9.1.2.3. Transforming using Ant's style task
Templates interface, performance optimization with: 5.4.2. A Stylesheet Cache
TemplateServlet, using: 8.1. XSLT Page Layout Templates
testing and debugging: 9.2. Testing and Debugging
Transformer interface: 5.4.1. Templates API
validation: 9.2.1. JUnit
XML to XML: 3.5. Schema Evolution
XSLT, predominance for: 2. XSLT Part 1 -- The Basics
XSLT processors, independence from servlets: 4.2.4. Development and Maintenance Benefits of XSLT
XSLT stylesheet reuse: 3.5. Schema Evolution
XSLTRenderHelper class: 7.4. Servlet Implementation
Transformer( ): 5.4.2. A Stylesheet Cache
Transformer class: 5.2.3. The Transformer Class
5.4.1. Templates API
5.4.1. Templates API
B. JAXP API Reference
thread safety, and: 5.2.3. The Transformer Class
TransformerConfigurationException class: 5.2.4. JAXP XSLT Design
9.1.1.3. Other exceptions and errors
B. JAXP API Reference
TransformerException class: 5.2.1. JAXP 1.1 Implementation
B. JAXP API Reference
TransformerFactory class: 5.2.1. JAXP 1.1 Implementation
B. JAXP API Reference
TransformerFactoryConfigurationError class: 5.2.4. JAXP XSLT Design
B. JAXP API Reference
TransformerHandler interface: B. JAXP API Reference
tree data structure, XML documents: 2.2.1. XML Tree Data Structure
Turbine: 4.2.1. Web Frameworks
BrowserDetector class: 8.3. Identifying the Browser
online resources: 8.3. Identifying the Browser

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

UCS (Universal Character Set): 8.6.2. Encodings
UnavailableException: 6.3.3. Source Code
Unicode: 8.6.2. Encodings
URIResolver interface: B. JAXP API Reference
URIs (Uniform Resource Identifiers): 5.1.2. Xalan 1 Implementation
URL rewriting: 8.2.1. Servlet Session-Tracking API
URLs (Uniform Resource Locators): 5.1.2. Xalan 1 Implementation
file names, converting to system IDs: 5.3.1. System Identifiers, Files, and URLs
US-ASCII encoding: 8.6.2. Encodings
User-Agent HTTP header: 8.3. Identifying the Browser
10.3.2.1. Identifying the client
User-Agent values: 8.3. Identifying the Browser
user interface prototypes: 7.2.1. XHTML Prototypes
UTF-8: 8.6.2. Encodings
UTFs (UCS Transformation Formats): 8.6.2. Encodings
utils.xslt: 7.2.3. XSLT Stylesheets

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

valid XML documents: 1.2.3. Validation
variables: 3.2. Parameters and Variables
defining: 3.2.1.1. Defining variables
HTML attribute values, using within: 3.2.1.2. Using variables
incrementing: 3.2.4. Incrementing Variables
limitations: 3.2.1.2. Using variables
thread safety: 6.5.2.1. Tip 1: Local variables are thread-safe
using: 3.2.1.2. Using variables
view: 4.2.2. Model-View-Controller
ViewMessageJDOM.java: 7.3.3. JDOM XML Production
ViewMonthJDOM.java: 7.3.3. JDOM XML Production
ViewMonthRenderer.java: 7.4. Servlet Implementation
ViewMonthReqHandler.java: 7.4. Servlet Implementation
viewMonth.xslt: 7.2.3. XSLT Stylesheets
ViewMsgRenderer.java: 7.4. Servlet Implementation
ViewMsgReqHandler.java: 7.4. Servlet Implementation
ViewMsg.xml: 7.2.2. XML Samples

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

WAP gateways: 10.2. The Wireless Architecture
WAP (Wireless Access Protocol): 10.1. Wireless Technologies
WAP (Wireless Application Protocol): 1.3.4. Wireless
WAR (Web Application Archive): 4.1.2. Servlets as CGI Replacements
WAR (Web Application Archive) files: 6.2. WAR Files and Deployment
contents, viewing: 6.2.1. WAR Files
6.3.5. How to Compile, Deploy, and Run
deployment descriptors: 6.2.2. Deployment Descriptor
directory structure: 6.3.4. Deployment
jar command, creation using: 6.2.1. WAR Files
page layout templates: 8.1. XSLT Page Layout Templates
WEB-INF directory: 6.2.1. WAR Files
WBMP (Wireless Bitmap): 10.3.1.1. WMLScript and WBMP
image conversion: 10.3.1.1. WMLScript and WBMP
W3C (Worldwide Web Consortium)
XSL specification: 2. XSLT Part 1 -- The Basics
web application development: 4. Java-Based Web Technologies
design process: 7.2.2. XML Samples
summary of approaches: 4.4. Summary of Key Approaches
web applications: 4.1.2. Servlets as CGI Replacements
client-only applications, vs.: 6. Servlet Basics and XSLT
contrasted with web sites: 1.3.2. Web Applications
development: 1.3.1. Web Development Challenges
challenges: 1.3.1. Web Development Challenges
hyperlinks: 7.2.3. XSLT Stylesheets
nonbrowser clients: 1.3.3. Nonbrowser Clients
security: 7.4. Servlet Implementation
web applications development, tools phone simulators: 10.1. Wireless Technologies
web frameworks: 4.2.1. Web Frameworks
advantages: 4.2.1. Web Frameworks
Turbine: 4.2.1. Web Frameworks
WEB-INF directory: 6.2.1. WAR Files
web pages, navigation areas: 8.1. XSLT Page Layout Templates
web.xml: 6.2.2. Deployment Descriptor
page layout templates: 8.1. XSLT Page Layout Templates
well-formed XML documents: 1.2.2. XML Syntax
2.2.1. XML Tree Data Structure
root element: 1.2.2. XML Syntax
summary of rules: 1.2.2. XML Syntax
whitespace: 1.2.2. XML Syntax
Wireless Access Protocol (see WAP)
wireless applications: 10. Wireless Applications
content types: 10.3.2.2. Setting the content type
deployment descriptors: 10.3.2.2. Setting the content type
future developments: 10.4. The Future of Wireless
XML data files: 10.3.3.2. XML data
wireless architecture: 10.2. The Wireless Architecture
WML (Wireless Markup Language): 1.3.4. Wireless
10.3.1. A WML Example
example: 10.3.3.1. Storyboard
online resources: 10.3.1. A WML Example
prototyping: 10.3.3.3. WML prototypes and screen shots
root element: 10.3.1. A WML Example
servlets, implementation by: 10.3.3.4. Servlet implementation
XSLT stylesheets: 10.3.3.5. XSLT stylesheets
WML (Wireless Markup Lanuage): 10.1. Wireless Technologies
WMLScript: 10.3.1.1. WMLScript and WBMP
write( ) (ServletOutputStream): 8.4.2. XSLT Transformation Filter

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

Xalan: 0.2. Software and Versions
2.1.1. An XSLT Example
JAR files and CLASSPATH: 2.1.2. Trying It Out
Spanish stylesheet transformation: 8.6.1. XSLT Stylesheet Design
XHTML Basic: 1.3.4. Wireless
10.1. Wireless Technologies
XHTML (Extensible Hypertext Markup Language): 1.2.1. SGML, XML, and Markup Languages
1.2.1. SGML, XML, and Markup Languages
generation from XML with XSLT: 2.3. Another XSLT Example, Using XHTML
ouptput, specification: 3.6.3.1. Specifying XHTML output
XSLT, page layout with: 8.1. XSLT Page Layout Templates
XHTML Strict: 7.2.1. XHTML Prototypes
XML (Extensible Markup Language): 1.2. XML Review
case sensitivity: 1.2.2. XML Syntax
datafiles, internationalization: 8.6.3.1. XML data
design: 7.2.2. XML Samples
discussion form, sample data: 7.2.2. XML Samples
document template: 2.2.4. A Skeleton Stylesheet
documents, tree data structure: 2.2.1. XML Tree Data Structure
dynamic generation in web applications: 1.1. Java, XSLT, and the Web
empty elements: 1.2.2. XML Syntax
HTML, and: 1. Introduction
JDOM, generation using: 1.2.4.3. JDOM
JDOM, production using: 7.3.3. JDOM XML Production
logging file example: 3.5.1. An Example XML File
software tools, used with: 0.2. Software and Versions
stylesheets, structure for: 6.3.2. XML and Stylesheets
syntax: 1.2.2. XML Syntax
transformations through XSLT: 0. Preface
transforming XML types: 3.5. Schema Evolution
valid documents: 1.2.3. Validation
webpage presentation and: 1.1.3. The XSLT Solution
well-formed documents: 1.2.2. XML Syntax
1.2.2. XML Syntax
2.2.1. XML Tree Data Structure
whitespace: 1.2.2. XML Syntax
XML namespace declarations: 2.1.1. An XSLT Example
XPATH, matching patterns using: 2.4.1. How XSLT Uses XPath
XSLT, advantages of use with: 2.1.1. An XSLT Example
XML Schema: 1.2.3. Validation
XML Spy: 2.1.2. Trying It Out
7.2.3. XSLT Stylesheets
XMLOutputter: 5.3.6.3. JDOM to text approach
XMLOutputter class: 5.3.6. Feeding JDOM Output into JAXP
XPath: 2.4. XPath Basics
context nodes: 2.4.2. Axes
node set functions: 3.1.1. <xsl:if>
syntax: 2.4. XPath Basics
tags, locating: 8.1. XSLT Page Layout Templates
use by XSLT: 2.4.1. How XSLT Uses XPath
XSL (Extensible Stylesheet Language): 2. XSLT Part 1 -- The Basics
W3C specification: 2. XSLT Part 1 -- The Basics
XSL Formatting Objects: 2. XSLT Part 1 -- The Basics
2.1. XSLT Introduction
XSLT-compatible web browsers: 7.2.3. XSLT Stylesheets
XSLT (Extensible Stylesheet Language Transformations): 0. Preface
0.2. Software and Versions
1.1.3. The XSLT Solution
advantages, development and maintenance: 4.2.4. Development and Maintenance Benefits of XSLT
browser support for: 1.5. Web Browser Support for XSLT
code generation using: 8.5. XSLT as a Code Generator
whitespace: 8.5. XSLT as a Code Generator
conflict-resolution rules: 8.6.1. XSLT Stylesheet Design
design and purpose: 0. Preface
EJB, and: 4.3. XSLT and EJB
importance: 2.1. XSLT Introduction
internationalization: 8.6. Internationalization with XSLT
Java, and: 5.1. A Simple Example
MVC architecture, and: 4.2.3. XSLT Implementation
Netscape, support by: 1.5. Web Browser Support for XSLT
online resources: 2.1.1. An XSLT Example
page layout templates, used for: 8.1. XSLT Page Layout Templates
processors: 0. Preface
role on the web: 1.1. Java, XSLT, and the Web
servlets, and: 6. Servlet Basics and XSLT
session tracking: 8.2.2. Session Tracking with Java and XSLT
skeleton stylesheet: 2.2.4. A Skeleton Stylesheet
stylesheet parameters, declaration: 5.2.3. The Transformer Class
stylesheets: 1.1. Java, XSLT, and the Web
templates: 2.1.1. An XSLT Example
testing: 9.2. Testing and Debugging
WML implementation: 10.3.3.5. XSLT stylesheets
WML prototyping: 10.3.3.3. WML prototypes and screen shots
XML, advantages of use with: 2.1.1. An XSLT Example
XPath, uses of: 2.4.1. How XSLT Uses XPath
XSLT processors: 1.1. Java, XSLT, and the Web
1.4.1. Java XSLT Processor Choices
2.1. XSLT Introduction
built in template rules: 2.2.3. Built-in Template Rules
choosing: 1.4.1. Java XSLT Processor Choices
input and output: 5.3. Input and Output
invocation from Java: 5. XSLT Processing with Java
Java, advantages of: 1.4.1. Java XSLT Processor Choices
JAXP: 1.4.1.4. JAXP
JDOM support: 1.2.4.4. JDOM and DOM interoperability
LotusXSL: 1.4.1.2. LotusXSL
SAXON: 1.4.1.3. SAXON
XT: 1.4.1.1. XT
XSLT stylesheets: 2.1. XSLT Introduction
as XML documents: 2.1.1. An XSLT Example
caching: 6.4.1. Integration with the Stylesheet Cache
8.1. XSLT Page Layout Templates
combining multiple sheets: 3.3. Combining Multiple Stylesheets
compiling: 5.4. Stylesheet Compilation
caching: 5.4.2. A Stylesheet Cache
CSS, contrasted with: 2.1. XSLT Introduction
design considerations: 6.3.2. XML and Stylesheets
discussion forum application: 7.2.3. XSLT Stylesheets
discussion forum application, for: 7.2.3. XSLT Stylesheets
editPersonalData.xslt: 6.3.2. XML and Stylesheets
6.3.2. XML and Stylesheets
English: 8.6.1. XSLT Stylesheet Design
filename extensions: 2.3. Another XSLT Example, Using XHTML
for generationg XHTML output from XML documents: 2.3. Another XSLT Example, Using XHTML
importation: 8.6.1. XSLT Stylesheet Design
internationalization: 8.6.3.2. XSLT stylesheets
location via initialization parameters: 6.3.6. Locating Stylesheets with Initialization Parameters
navigation areas, defining with: 8.1. XSLT Page Layout Templates
pathnames, encoding of: 6.3.3. Source Code
session-tracking: 8.2.2. Session Tracking with Java and XSLT
Spanish: 8.6.1. XSLT Stylesheet Design
XML structure: 6.3.2. XML and Stylesheets
syntax: 3.2.6. <xsl:template> Syntax Summary
XSLTProcessor interface: 5.1.2. Xalan 1 Implementation
XSLTProcessorFactory class: 5.1.2. Xalan 1 Implementation
XSLTRenderHelper.java: 7.4. Servlet Implementation
XT: 1.4.1.1. XT

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Y

There are no index entries for this letter.

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Z

There are no index entries for this letter.

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Perl & XML

by Erik T. Ray and Jason McIntosh
ISBN 0-596-00205-X
First Edition, published April 2002.
(See the catalog page for this book.)

Table of Contents

Copyright Page
Preface
Chapter 1: Perl and XML
Chapter 2: An XML Recap
Chapter 3: XML Basics: Reading and Writing
Chapter 4: Event Streams
Chapter 5: SAX
Chapter 6: Tree Processing
Chapter 7: DOM
Chapter 8: Beyond Trees: XPath, XSLT, and More
Chapter 9: RSS, SOAP, and Other XML Applications
Chapter 10: Coding Strategies
Index
Colophon

Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Copyright (Perl and XML)

 Perl & XMLSearch this book

Copyright © 2002 O'Reilly & Associates, Inc. All rights reserved.

Printed in the United States of America.

Published by O'Reilly & Associates, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safari.oreilly.com). For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

The O'Reilly logo is a registered trademark of O'Reilly & Associates, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps. The association between the image of West African green monkeys and the topic of Perl and XML is a trademark of O'Reilly & Associates, Inc.

While every precaution has been taken in the preparation of this book, the publisher and the authors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Previous | Home | Next
Table of Contents | Book Index | 0. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Preface (Perl and XML)

 Perl & XMLSearch this book

Preface

This book marks the intersection of two essential technologies for the Web and information services. XML, the latest and best markup language for self-describing data, is becoming the generic data packaging format of choice. Perl, which web masters have long relied on to stitch up disparate components and generate dynamic content, is a natural choice for processing XML. The shrink-wrap of the Internet meets the duct tape of the Internet.

More powerful than HTML, yet less demanding than SGML, XML is a perfect solution for many developers. It has the flexibility to encode everything from web pages to legal contracts to books, and the precision to format data for services like SOAP and XML-RPC. It supports world-class standards like Unicode while being backwards-compatible with plain old ASCII. Yet for all its power, XML is surprisingly easy to work with, and many developers consider it a breeze to adapt to their programs.

As the Perl programming language was tailor-made for manipulating text, Perl and XML are perfectly suited for one another. The only question is, "What's the best way to pair them?" That's where this book comes in.

0.1. Assumptions

This book was written for programmers who are interested in using Perl to process XML documents. We assume that you already know Perl; if not, please pick up O'Reilly's Learning Perl (or its equivalent) before reading this book. It will save you much frustration and head scratching.

We do not assume that you have much experience with XML. However, it helps if you are familiar with markup languages such as HTML.

We assume that you have access to the Internet, and specifically to the Comprehensive Perl Archive Network (CPAN), as most of this book depends on your ability to download modules from CPAN.

Most of all, we assume that you've rolled up your sleeves and are ready to start programming with Perl and XML. There's a lot of ground to cover in this little book, and we're eager to get started.

Previous | Home | Next
Copyright Page | Book Index | 0.2. How This Book Is Organized
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How This Book Is Organized (Perl and XML)

 Perl & XMLSearch this book

0.2. How This Book Is Organized

This book is broken up into ten chapters, as follows:

Chapter 1, "Perl and XML" introduces our two heroes. We also give an XML::Simple example for the impatient reader.

Chapter 2, "An XML Recap" is for the readers who say they know XML but suspect they really don't. We give a quick summary of where XML came from and how it's structured. If you really do know XML, you are free to skip this chapter, but don't complain later that you don't know a namespace from an en-dash.

Chapter 3, "XML Basics: Reading and Writing" shows how to get information from an XML document and write it back in. Of course, all the interesting stuff happens in between these steps, but you still need to know how to read and write the stuff.

Chapter 4, "Event Streams" explains event streams, the efficient core of most XML processing.

Chapter 5, "SAX" introduces the Simple API for XML processing, a standard interface to event streams.

Chapter 6, "Tree Processing" is about . . . well, processing trees, the basic structure of all XML documents. We start with simple structures of built-in types and finish with advanced, object-oriented tree models.

Chapter 7, "DOM" covers the Document Object Model, another standard interface of importance. We give examples showing how DOM will make you nimble as a squirrel in any XML tree.

Chapter 8, "Beyond Trees: XPath, XSLT, and More" covers advanced tree processing, including event-tree hybrids and transformation scripts.

Chapter 9, "RSS, SOAP, and Other XML Applications " shows existing real-life applications using Perl and XML.

Chapter 10, "Coding Strategies" wraps everything up. Now that you are familiar with the modules, we'll tell you which to use, why to use them, and what gotchas to avoid.

Previous | Home | Next
0. Preface | Book Index | 0.3. Resources
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Resources (Perl and XML)

 Perl & XMLSearch this book

0.3. Resources

While this book aims to cover everything you'll need to start programming with Perl and XML, modules change, new standards emerge, and you may think of some oddball situation that we haven't anticipated. Here's are two other resources you can pursue.

0.3.1. The perl-xml Mailing List

The perl-xml mailing list is the first place to go for finding fellow programmers suffering from the same issues as you. In fact, if you plan to work with Perl and XML in any nontrivial way, you should first subscribe to this list. To subscribe to the list or browse archives of past discussions, visit http://aspn.activestate.com/ASPN/Mail/Browse/Threaded/perl-xml.

You might also want to check out http://www.xmlperl.com, a fairly new web site devoted to the Perl/XML community.

0.3.2. CPAN

Most modules discussed in this book are not distributed with Perl and need to be downloaded from CPAN.

If you've worked in Perl at all, you're familiar with CPAN and how to download and install modules. If you aren't, head over to http://www.cpan.org. Check out the FAQ first. Get the CPAN module if you don't already have it (it probably came with your standard Perl distribution).

Previous | Home | Next
0.2. How This Book Is Organized | Book Index | 0.4. Font Conventions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Font Conventions (Perl and XML)

 Perl & XMLSearch this book

0.4. Font Conventions

Italic is used for URLs, filenames, commands, hostnames, and emphasized words.

Constant width is used for function names, module names, and text that is typed literally.

Constant-width bold is used for user input.

Constant-width italic is used for replaceable text.

Previous | Home | Next
0.3. Resources | Book Index | 0.5. How to Contact Us
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How to Contact Us (Perl and XML)

 Perl & XMLSearch this book

0.5. How to Contact Us

Please address comments and questions concerning this book to the publisher:

O'Reilly & Associates, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
(800) 998-9938 (in the United States or Canada)
(707) 829-0515 (international or local)
(707) 829-0104 (fax)

There is a web page for this book, which lists errata, examples, or any additional information. You can access this page at:

http://www.oreilly.com/catalog/perlxml

To comment or ask technical questions about this book, send email to:

bookquestions@oreilly.com

For more information about books, conferences, Resource Centers, and the O'Reilly Network, see the O'Reilly web site at:

http://www.oreilly.com/

Previous | Home | Next
0.4. Font Conventions | Book Index | 0.6. Acknowledgments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Acknowledgments (Perl and XML)

 Perl & XMLSearch this book

0.6. Acknowledgments

Both authors are grateful for the expert guidance from Paula Ferguson, Andy Oram, Jon Orwant, Michel Rodriguez, Simon St.Laurent, Matt Sergeant, Ilya Sterin, Mike Stok, Nat Torkington, and their editor, Linda Mui.

Erik would like to thank his wife Jeannine; his family (Birgit, Helen, Ed, Elton, Al, Jon-Paul, John and Michelle, John and Dolores, Jim and Joanne, Gene and Margaret, Liane, Tim and Donna, Theresa, Christopher, Mary-Anne, Anna, Tony, Paul and Sherry, Lillian, Bob, Joe and Pam, Elaine and Steve, Jennifer, and Marion); his excellent friends Derrick Arnelle, Stacy Chandler, J. D. Curran, Sarah Demb, Ryan Frasier, Chris Gernon, John Grigsby, Andy Grosser, Lisa Musiker, Benn Salter, Caroline Senay, Greg Travis, and Barbara Young; and his coworkers Lenny, Mela, Neil, Mike, and Sheryl.

Jason would like to thank Julia for her encouragement throughout this project; Looney Labs games (http://www.looneylabs.com) and the Boston Warren for maintaining his sanity by reminding him to play; Josh and the Ottoman Empire for letting him escape reality every now and again; the Diesel Cafe in Somerville, Massachusetts and the 1369 Coffee House in Cambridge for unwittingly acting as his alternate offices; housemates Charles, Carla, and Film Series: The Cat; Apple Computer for its fine iBook and Mac OS X, upon which most writing/hacking was accomplished; and, of course, Larry Wall and all the strange and wonderful people who brought (and continue to bring) us Perl.

Previous | Home | Next
0.5. How to Contact Us | Book Index | 1. Perl and XML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Perl and XML (Perl and XML)

 Perl & XMLSearch this book

Chapter 1. Perl and XML

Contents:

Why Use Perl with XML?
XML Is Simple with XML::Simple
XML Processors
A Myriad of Modules
Keep in Mind...
XML Gotchas

Perl is a mature but eccentric programming language that is tailor-made for text manipulation. XML is a fiery young upstart of a text-based markup language used for web content, document processing, web services, or any situation in which you need to structure information flexibly. This book is the story of the first few years of their sometimes rocky (but ultimately happy) romance.

1.1. Why Use Perl with XML?

First and foremost, Perl is ideal for crunching text. It has filehandles, "here" docs, string manipulation, and regular expressions built into its syntax. Anyone who has ever written code to manipulate strings in a low-level language like C and then tried to do the same thing in Perl has no trouble telling you which environment is easier for text processing. XML is text at its core, so Perl is uniquely well suited to work with it.

Furthermore, starting with Version 5.6, Perl has been getting friendly with Unicode-flavored character encodings, especially UTF-8, which is important for XML processing. You'll read more about character encoding in Chapter 3, "XML Basics: Reading and Writing".

Second, the Comprehensive Perl Archive Network (CPAN) is a multimirrored heap of modules free for the taking. You could say that it takes a village to make a program; anyone who undertakes a programming project in Perl should check the public warehouse of packaged solutions and building blocks to save time and effort. Why write your own parser when CPAN has plenty of parsers to download, all tested and chock full of configurability? CPAN is wild and woolly, with contributions from many people and not much supervision. The good news is that when a new technology emerges, a module supporting it pops up on CPAN in short order. This feature complements XML nicely, since it's always changing and adding new accessory technologies.

Early on, modules sprouted up around XML like mushrooms after a rain. Each module brought with it a unique interface and style that was innovative and Perlish, but not interchangeable. Recently, there has been a trend toward creating a universal interface so modules can be interchangeable. If you don't like this SAX parser, you can plug in another one with no extra work. Thus, the CPAN community does work together and strive for internal coherence.

Third, Perl's flexible, object-oriented programming capabilities are very useful for dealing with XML. An XML document is a hierarchical structure made of a single basic atomic unit, the XML element, that can hold other elements as its children. Thus, the elements that make up a document can be represented by one class of objects that all have the same, simple interface. Furthermore, XML markup encapsulates content the way objects encapsulate code and data, so the two complement each other nicely. You'll also see that objects are useful for modularizing XML processors. These objects include parser objects, parser factories that serve up parser objects, and parsers that return objects. It all adds up to clean, portable code.

Fourth, the link between Perl and the Web is important. Java and JavaScript get all the glamour, but any web monkey knows that Perl lurks at the back end of most servers. Many web-munging libraries in Perl are easily adapted to XML. The developers who have worked in Perl for years building web sites are now turning their nimble fingers to the XML realm.

Ultimately, you'll choose the programming language that best suits your needs. Perl is ideal for working with XML, but you shouldn't just take our word for it. Give it a try.

Previous | Home | Next
0.6. Acknowledgments | Book Index | 1.2. XML Is Simple with XML::Simple
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Is Simple with XML::Simple (Perl and XML)

 Perl & XMLSearch this book

1.2. XML Is Simple with XML::Simple

Many people, understandably, think of XML as the invention of an evil genius bent on destroying humanity. The embedded markup, with its angle brackets and slashes, is not exactly a treat for the eyes. Add to that the business about nested elements, node types, and DTDs, and you might cower in the corner and whimper for nice, tab-delineated files and a split function.

Here's a little secret: writing programs to process XML is not hard. A whole spectrum of tools that handle the mundane details of parsing and building data structures for you is available, with convenient APIs that get you started in a few minutes. If you really need the complexity of a full-featured XML application, you can certainly get it, but you don't have to. XML scales nicely from simple to bafflingly complex, and if you deal with XML on the simple end of the continuum, you can pick simple tools to help you.

To prove our point, we'll look at a very basic module called XML::Simple, created by Grant McLean. With minimal effort up front, you can accomplish a surprising amount of useful work when processing XML.

A typical program reads in an XML document, makes some changes, and writes it back out to a file. XML::Simple was created to automate this process as much as possible. One subroutine call reads in an XML document and stores it in memory for you, using nested hashes to represent elements and data. After you make whatever changes you need to make, call another subroutine to print it out to a file.

Let's try it out. As with any module, you have to introduce XML::Simple to your program with a use pragma like this:

use XML::Simple;

When you do this, XML::Simple exports two subroutines into your namespace:

XMLin( )

This subroutine reads an XML document from a file or string and builds a data structure to contain the data and element structure. It returns a reference to a hash containing the structure.

XMLout( )

Given a reference to a hash containing an encoded document, this subroutine generates XML markup and returns it as a string of text.

If you like, you can build the document from scratch by simply creating the data structures from hashes, arrays, and strings. You'd have to do that if you wanted to create a file for the first time. Just be careful to avoid using circular references, or the module will not function properly.

For example, let's say your boss is going to send email to a group of people using the world-renowned mailing list management application, WarbleSoft SpamChucker. Among its features is the ability to import and export XML files representing mailing lists. The only problem is that the boss has trouble reading customers' names as they are displayed on the screen and would prefer that they all be in capital letters. Your assignment is to write a program that can edit the XML datafiles to convert just the names into all caps.

Accepting the challenge, you first examine the XML files to determine the style of markup. Example 1-1 shows such a document.

Example 1-1. SpamChucker datafile

 <?xml version="1.0"?>
 <spam-document version="3.5" timestamp="2002-05-13 15:33:45">
 <!-- Autogenerated by WarbleSoft Spam Version 3.5 -->
 <customer>
  <first-name>Joe</first-name>
  <surname>Wrigley</surname>
  <address>
    <street>17 Beable Ave.</street>
    <city>Meatball</city>
    <state>MI</state>
    <zip>82649</zip>
  </address>
  <email>joewrigley@jmac.org</email>
  <age>42</age>
 </customer>
 <customer>
  <first-name>Henrietta</first-name>
  <surname>Pussycat</surname>
   <address>
    <street>R.F.D. 2</street>
    <city>Flangerville</city>
    <state>NY</state>
    <zip>83642</zip>
   </address>
   <email>meow@263A.org</email>
   <age>37</age>
  </customer>
 </spam-document>

Having read the perldoc page describing XML::Simple, you might feel confident enough to craft a little script, shown in Example 1-2.

Example 1-2. A script to capitalize customer names

# This program capitalizes all the customer names in an XML document
# made by WarbleSoft SpamChucker.

# Turn on strict and warnings, for it is always wise to do so (usually)
use strict;
use warnings;

# Import the XML::Simple module
use XML::Simple;

# Turn the file into a hash reference, using XML::Simple's "XMLin"
# subroutine.
# We'll also turn on the 'forcearray' option, so that all elements
# contain arrayrefs.
my $cust_xml = XMLin('./customers.xml', forcearray=>1);

# Loop over each customer sub-hash, which are all stored as in an
# anonymous list under the 'customer' key
for my $customer (@{$cust_xml->{customer}}) {
  # Capitalize the contents of the 'first-name' and 'surname' elements
  # by running Perl's built-in uc( ) function on them
  foreach (qw(first-name surname)) {
    $customer->{$_}->[0] = uc($customer->{$_}->[0]);
  }
}

# print out the hash as an XML document again, with a trailing newline
# for good measure
print XMLout($cust_xml);
print "\n";

Running the program (a little trepidatious, perhaps, since the data belongs to your boss), you get this output:

<opt version="3.5" timestamp="2002-05-13 15:33:45">
  <customer>
    <address>
      <state>MI</state>
      <zip>82649</zip>
      <city>Meatball</city>
      <street>17 Beable Ave.</street>
    </address>
    <first-name>JOE</first-name>
    <email>i-like-cheese@jmac.org</email>
    <surname>WRIGLEY</surname>
    <age>42</age>
  </customer>
  <customer>
    <address>
      <state>NY</state>
      <zip>83642</zip>
      <city>Flangerville</city>
      <street>R.F.D. 2</street>
    </address>
    <first-name>HENRIETTA</first-name>
    <email>meowmeow@augh.org</email>
    <surname>PUSSYCAT</surname>
    <age>37</age>
  </customer>
</opt>

Congratulations! You've written an XML-processing program, and it worked perfectly. Well, almost perfectly. The output is a little different from what you expected. For one thing, the elements are in a different order, since hashes don't preserve the order of items they contain. Also, the spacing between elements may be off. Could this be a problem?

This scenario brings up an important point: there is a trade-off between simplicity and completeness. As the developer, you have to decide what's essential in your markup and what isn't. Sometimes the order of elements is vital, and then you might not be able to use a module like XML::Simple. Or, perhaps you want to be able to access processing instructions and keep them in the file. Again, this is something XML::Simple can't give you. Thus, it's vital that you understand what a module can or can't do before you commit to using it. Fortunately, you've checked with your boss and tested the SpamChucker program on the modified data, and everyone was happy. The new document is close enough to the original to fulfill the application's requirements.[1] Consider yourself initiated into processing XML with Perl!

[1]Some might say that, disregarding the changes we made on purpose, the two documents are semantically equivalent, but this is not strictly true. The order of elements changed, which is significant in XML. We can say for sure that the documents are close enough to satisfy all the requirements of the software for which they were intended and of the end user.

This is only the beginning of your journey. Most of the book still lies ahead of you, chock full of tips and techniques to wrestle with any kind of XML. Not every XML problem is as simple as the one we just showed you. Nevertheless, we hope we've made the point that there's nothing innately complex or scary about banging XML with your Perl hammer.

Previous | Home | Next
1. Perl and XML | Book Index | 1.3. XML Processors
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Processors (Perl and XML)

 Perl & XMLSearch this book

1.3. XML Processors

Now that you see the easy side of XML, we will expose some of XML's quirks. You need to consider these quirks when working with XML and Perl.

When we refer in this book to an XML processor (which we'll often refer to in shorthand as a processor, not to be confused with the central processing unit of a computer system that has the same nickname), we refer to software that can either read or generate XML documents. We use this term in the most general way -- what the program actually does with the content it might find in the XML it reads is not the concern of the processor itself, nor is it the processor's responsibility to determine the origin of the document or decide what to do with one that is generated.

As you might expect, a raw XML processor working alone isn't very interesting. For this reason, a computer program that actually does something cool or useful with XML uses a processor as just one component. It usually reads an XML file and, through the magic of parsing, turns it into in-memory structures that the rest of the program can do whatever it likes with.

In the Perl world, this behavior becomes possible through the use of Perl modules: typically, a program that needs to process XML embraces, through the use pragma, an existing package that makes a programmer interface available (usually an object-oriented one). This is why, before they get down to business, many XML-handling Perl programs start out with use XML::Parser; or something similar. With one little line, they're able to leave all the dirty work of XML parsing to another, previously written module, leaving their own code to decide what to do pre- and post-processing.

Previous | Home | Next
1.2. XML Is Simple with XML::Simple | Book Index | 1.4. A Myriad of Modules
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Myriad of Modules (Perl and XML)

 Perl & XMLSearch this book

1.4. A Myriad of Modules

One of Perl's strengths is that it's a community-driven language. When Perl programmers identify a need and write a module to handle it, they are encouraged to distribute it to the world at large via CPAN. The advantage of this is that if there's something you want to do in Perl and there's a possibility that someone else wanted to do it previously, a Perl module is probably already available on CPAN.

However, for a technology that's as young, popular, and creatively interpretable as XML, the community-driven model has a downside. When XML first caught on, many different Perl modules written by different programmers appeared on CPAN, seemingly all at once. Without a governing body, they all coexisted in inconsistent glee, with a variety of structures, interfaces, and goals.

Don't despair, though. In the time since the mist-enshrouded elder days of 1998, a movement towards some semblance of organization and standards has emerged from the Perl/XML community (which primarily manifests on ActiveState's perl-xml mailing list, as mentioned in the preface). The community built on these first modules to make tools that followed the same rules that other parts of the XML world were settling on, such as the SAX and DOM parsing standards, and implemented XML-related technologies such as XPath. Later, the field of basic, low-level parsers started to widen. Recently, some very interesting systems have emerged (such as XML::SAX) that bring truly Perlish levels of DWIMminess out of these same standards.[2]

[2]DWIM = "Do What I Mean," one of the fundamental philosophies governing Perl.

Of course, the goofy, quick-and-dirty tools are still there if you want to use them, and XML::Simple is among them. We will try to help you understand when to reach for the standards-using tools and when it's OK to just grab your XML and run giggling through the daffodils.

Previous | Home | Next
1.3. XML Processors | Book Index | 1.5. Keep in Mind...
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Keep in Mind... (Perl and XML)

 Perl & XMLSearch this book

1.5. Keep in Mind...

In many cases, you'll find that the XML modules on CPAN satisfy 90 percent of your needs. Of course, that final 10 percent is the difference between being an essential member of your company's staff and ending up slated for the next round of layoffs. We're going to give you your money's worth out of this book by showing you in gruesome detail how XML processing in Perl works at the lowest levels (relative to any other kind of specialized text munging you may perform with Perl). To start, let's go over some basic truths:

  • It doesn't matter where it comes from.

    By the time the XML parsing part of a program gets its hands on a document, it doesn't give a camel's hump where the thing came from. It could have been received over a network, constructed from a database, or read from disk. To the parser, it's good (or bad) XML, and that's all it knows.

    Mind you, the program as a whole might care a great deal. If we write a program that implements XML-RPC, for example, it better know exactly how to use TCP to fetch and send all that XML data over the Internet! We can have it do that fetching and sending however we like, as long as the end product is the same: a clean XML document fit to pass to the XML processor that lies at the program's core.

    We will get into some detailed examples of larger programs later in this book.

  • Structurally, all XML documents are similar.

    No matter why or how they were put together or to what purpose they'll be applied, all XML documents must follow the same basic rules of well-formedness: exactly one root element, no overlapping elements, all attributes quoted, and so on. Every XML processor's parser component will, at its core, need to do the same things as every other XML processor. This, in turn, means that all these processors can share a common base. Perl XML-processing programs usually observe this in their use of one of the many free parsing modules, rather than having to reimplement basic XML parsing procedures every time.

    Furthermore, the one-document, one-element nature of XML makes processing a pleasantly fractal experience, as any document invoked through an external entity by another document magically becomes "just another element" within the invoker, and the same code that crawled the first document can skitter into the meat of any reference (and anything to which the reference might refer) without batting an eye.

  • In meaning, all XML applications are different.

    XML applications are the raison d'être of any one XML document, the higher-level set of rules they follow with an aim for applicability to some useful purpose -- be it filling out a configuration file, preparing a network transmission, or describing a comic strip. XML applications exist to not only bless humble documents with a higher sense of purpose, but to require the documents to be written according to a given application specification.

    DTDs help enforce the consistency of this structure. However, you don't have to have a formal validation scheme to make an application. You may want to create some validation rules, though, if you need to make sure that your successors (including yourself, two weeks in the future) do not stray from the path you had in mind when they make changes to the program. You should also create a validation scheme if you want to allow others to write programs that generate the same flavor of XML.

Most of the XML hacking you'll accomplish will capitalize on this document/application duality. In most cases, your software will consist of parts that cover all three of these facts:

  • It will accept input in an appropriate way -- listening to a network socket, for example, or reading a file from disk. This behavior is very ordinary and Perlish: do whatever's necessary here to get that data.

  • It will pass captured input to some kind of XML processor. Dollars to doughnuts says you'll use one of the parsers that other people in the Perl community have already written and continue to maintain, such as XML::Simple, or the more sophisticated modules we'll discuss later.

  • Finally, it will Do Something with whatever that processor did to the XML. Maybe it will output more XML (or HTML), update a database, or send mail to your mom. This is the defining point of your XML application -- it takes the XML and does something meaningful with it. While we won't cover the infinite possibilities here, we will discuss the crucial ties between the XML processor and the rest of your program.

Previous | Home | Next
1.4. A Myriad of Modules | Book Index | 1.6. XML Gotchas
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Gotchas (Perl and XML)

 Perl & XMLSearch this book

1.6. XML Gotchas

This section introduces topics we think you should keep in mind as you read the book. They are the source of many of the problems you'll encounter when working with XML.

Well-formedness

XML has built-in quality control. A document has to pass some minimal syntax rules in order to be blessed as well-formed XML. Most parsers fail to handle a document that breaks any of these rules, so you should make sure any data you input is of sufficient quality.

Character encodings

Now that we're in the 21st century, we have to pay attention to things like character encodings. Gone are the days when you could be content knowing only about ASCII, the little character set that could. Unicode is the new king, presiding over all major character sets of the world. XML prefers to work with Unicode, but there are many ways to represent it, including Perl's favorite Unicode encoding, UTF-8. You usually won't have to think about it, but you should still be aware of the potential.

Namespaces

Not everyone works with or even knows about namespaces. It's a feature in XML whose usefulness is not immediately obvious, yet it is creeping into our reality slowly but surely. These devices categorize markup and declare tags to be from different places. With them, you can mix and match document types, blurring the distinctions between them. Equations in HTML? Markup as data in XSLT? Yes, and namespaces are the reason. Older modules don't have special support for namespaces, but the newer generation will. Keep it in mind.

Declarations

Declarations aren't part of the document per se; they just define pieces of it. That makes them weird, and something you might not pay enough attention to. Remember that documents often use DTDs and have declarations for such things as entities and attributes. If you forget, you could end up breaking something.

Entities

Entities and entity references seem simple enough: they stand in for content that you'd rather not type in at that moment. Maybe the content is in another file, or maybe it contains characters that are difficult to type. The concept is simple, but the execution can be a royal pain. Sometimes you want to resolve references and sometimes you'd rather keep them there. Sometimes a parser wants to see the declarations; at other times it doesn't care. Entities can contain other entities to an arbitrary depth. They're tricky little beasties and we guarantee that if you don't give careful thought to how you're going to handle them, they will haunt you.

Whitespace

According to XML, anything that isn't a markup tag is significant character data. This fact can lead to some surprising results. For example, it isn't always clear what should happen with whitespace. By default, an XML processor will preserve all of it -- even the newlines you put after tags to make them more readable or the spaces you use to indent text. Some parsers will give you options to ignore space in certain circumstances, but there are no hard and fast rules.

In the end, Perl and XML are well suited for each other. There may be a few traps and pitfalls along the way, but with the generosity of various module developers, your path toward Perl/XML enlightenment should be well lit.

Previous | Home | Next
1.5. Keep in Mind... | Book Index | 2. An XML Recap
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


An XML Recap (Perl and XML)

 Perl & XMLSearch this book

Chapter 2. An XML Recap

Contents:

A Brief History of XML
Markup, Elements, and Structure
Namespaces
Spacing
Entities
Unicode, Character Sets, and Encodings
The XML Declaration
Processing Instructions and Other Markup
Free-Form XML and Well-Formed Documents
Declaring Elements and Attributes
Schemas
Transformations

XML is a revolutionary (and evolutionary) markup language. It combines the generalized markup power of SGML with the simplicity of free-form markup and well-formedness rules. Its unambiguous structure and predictable syntax make it a very easy and attractive format to process with computer programs.

You are free, with XML, to design your own markup language that best fits your data. You can select element names that make sense to you, rather than use tags that are overloaded and presentation-heavy. If you like, you can formalize the language by using element and attribute declarations in the DTD.

XML has syntactic shortcuts such as entities, comments, processing instructions, and CDATA sections. It allows you to group elements and attributes by namespace to further organize the vocabulary of your documents. Using the xml:space attribute can regulate whitespace, sometimes a tricky issue in markup in which human readability is as important as correct formatting.

Some very useful technologies are available to help you maintain and mutate your documents. Schemas, like DTDs, can measure the validity of XML as compared to a canonical model. Schemas go even further by enforcing patterns in character data and improving content model syntax. XSLT is a rich language for transforming documents into different forms. It could be an easier way to work with XML than having to write a program, but isn't always.

This chapter gives a quick recap of XML, where it came from, how it's structured, and how to work with it. If you choose to skip this chapter (because you already know XML or because you're impatient to start writing code), that's fine; just remember that it's here if you need it.

2.1. A Brief History of XML

Early text processing was closely tied to the machines that displayed it. Sophisticated formatting was tied to a particular device -- or rather, a class of devices called printers.

Take troff, for example. Troff was a very popular text formatting language included in most Unix distributions. It was revolutionary because it allowed high-quality formatting without a typesetting machine.

Troff mixes formatting instructions with data. The instructions are symbols composed of characters, with a special syntax so a troff interpreter can tell the two apart. For example, the symbol \fI changes the current font style to italic. Without the backslash character, it would be treated as data. This mixture of instructions and data is called markup.

Troff can be even more detailed than that. The instruction .vs 18p tells the formatter to insert 18 points of vertical space at whatever point in the document where the instruction appears. Beyond aesthetics, we can't tell just by looking at it what purpose this spacing serves; it gives a very specific instruction to the processor that can't be interpreted in any other way. This instruction is fine if you only want to prepare a document for printing in a specific style. If you want to make changes, though, it can be quite painful.

Suppose you've marked up a book in troff so that every newly defined term is in boldface. Your document has thousands of bold font instructions in it. You're happy and ready to send it to the printer when suddenly, you get a call from the design department. They tell you that the design has changed and they now want the new terms to be formatted as italic. Now you have a problem. You have to turn every bold instruction for a new term into an italic instruction.

Your first thought is to open the document in your editor and do a search-and-replace maneuver. But, to your horror, you realize that new terms aren't the only places where you used bold font instructions. You also used them for emphasis and for proper nouns, meaning that a global replace would also mangle these instances, which you definitely don't want. You can change the right instructions only by going through them one at a time, which could take hours, if not days.

No matter how smart you make a formatting language like troff, it still has the same problem: it's inherently presentational. A presentational markup language describes content in terms of how to format it. Troff specifies details about fonts and spacing, but it never tells you what something is. Using troff makes the document less useful in some ways. It's hard to search through troff and come back with the last paragraph of the third section of a book, for example. The presentational markup gets in the way of any task other than its specific purpose: to format the document for printing.

We can characterize troff, then, as a destination format. It's not good for anything but a specific end purpose. What other kind of format could there be? Is there an "origin" format -- that is, something that doesn't dictate any particular formatting but still packages the data in a useful way? People began to ask this key question in the late 1960s when they devised the concept of generic coding: marking up content in a presentation-agnostic way, using descriptive tags rather than formatting instructions.

The Graphic Communications Association (GCA) started a project to explore this new area called GenCode, which develops ways to encode documents in generic tags and assemble documents from multiple pieces -- a precursor to hypertext. IBM's Generalized Markup Language (GML), developed by Charles Goldfarb, Edward Mosher, and Raymond Lorie, built on this concept.[3] As a result of this work, IBM could edit, view on a terminal, print, and search through the same source material using different programs. You can imagine that this benefit would be important for a company that churned out millions of pages of documentation per year.

[3]Cute fact: the initials of these researchers also spell out "GML."

Goldfarb went on to lead a standards team at the American National Standards Institute (ANSI) to make the power of GML available to the world. Building on the GML and GenCode projects, the committee produced the Standard Generalized Markup Language (SGML). Quickly adopted by the U.S. Department of Defense and the Internal Revenue Service, SGML proved to be a big success. It became an international standard when ratified by the ISO in 1986. Since then, many publishing and processing packages and tools have been developed.

Generic coding was a breakthrough for digital content. Finally, content could be described for what it was, instead of how to display it. Something like this looks more like a database than a word-processing file:

<personnel-record>
  <name>
    <first>Rita</first>
    <last>Book</last>
  </name>
  <birthday>
    <year>1969</year>
    <month>4</month>
    <day>23</day>
  </birthday>
</personnel-record>

Notice the lack of presentational information. You can format the name any way you want: first name then last name, or last name first, with a comma. You could format the date in American style (4/23/1969) or European (23/4/1969) simply by specifying whether the <month> or <day> element should present its contents first. The document doesn't dictate its use, which makes it useful as a source document for multiple destinations.

In spite of its revolutionary capabilities, SGML never really caught on with small companies the way it did with the big ones. Software is expensive and bulky. It takes a team of developers to set up and configure a production environment around SGML. SGML feels bureaucratic, confusing, and resource-heavy. Thus, SGML in its original form was not ready to take the world by storm.

"Oh really," you say. "Then what about HTML? Isn't it true that HTML is an application of SGML?" HTML, that celebrity of the Internet, the harbinger of hypertext and workhorse of the World Wide Web, is indeed an application of SGML. By application, we mean that it is a markup language derived with the rules of SGML. SGML isn't a markup language, but a toolkit for designing your own descriptive markup language. Besides HTML, languages for encoding technical documentation, IRS forms, and battleship manuals are in use.

HTML is indeed successful, but it has limitations. It's a very small language, and not very descriptive. It is closer to troff in function than to DocBook and other SGML applications. It has tags like <i> and <b> that change the font style without saying why. Because HTML is so limited and at least partly presentational, it doesn't represent an overwhelming success for SGML, at least not in spirit. Instead of bringing the power of generic coding to the people, it brought another one-trick pony, in which you could display your content in a particular venue and couldn't do much else with it.

Thus, the standards folk decided to try again and see if they couldn't arrive at a compromise between the descriptive power of SGML and the simplicity of HTML. They came up with the Extensible Markup Language (XML). The "X" stands for "extensible," pointing out the first obvious difference from HTML, which is that some people think that "X" is a cooler-sounding letter than "E" when used in an acronym. The second and more relevant difference is that your documents don't have to be stuck in the anemic tag set of HTML. You can extend the tag namespace to be as descriptive as you want -- as descriptive, even, as SGML. Voilà! The bridge is built.

By all accounts, XML is a smashing success. It has lived up to the hype and keeps on growing: XML-RPC, XHTML, SVG, and DocBook XML are some of its products. It comes with several accessories, including XSL for formatting, XSLT for transforming, XPath for searching, and XLink for linking. Much of the standards work is under the auspices of the World Wide Web Consortium (W3C), an organization whose members include Microsoft, Sun, IBM, and many academic and public institutions.

The W3C's mandate is to research and foster new technology for the Internet. That's a rather broad statement, but if you visit their site at http://www.w3.org/ you'll see that they cover a lot of bases. The W3C doesn't create, police, or license standards. Rather, they make recommendations that developers are encouraged, but not required, to follow.[4]

[4]When a trusted body like the W3C makes a recommendation, it often has the effect of a law; many developers begin to follow the recommendation upon its release, and developers who hope to write software that is compatible with everyone else's (which is the whole point behind standards like XML) had better follow the recommendation as well.

However, the system remains open enough to allow healthy dissent, such as the recent and interesting case of XML Schema, a W3C standard that has generated controversy and competition. We'll examine this particular story further in Chapter 3, "XML Basics: Reading and Writing". It's strong enough to be taken seriously, but loose enough not to scare people away. The recommendations are always available to the public.

Every developer should have working knowledge of XML, since it's the universal packing material for data, and so many programs are all about crunching data. The rest of this chapter gives a quick introduction to XML for developers.

Previous | Home | Next
1.6. XML Gotchas | Book Index | 2.2. Markup, Elements, and Structure
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Markup, Elements, and Structure (Perl and XML)

 Perl & XMLSearch this book

2.2. Markup, Elements, and Structure

A markup language provides a way to embed instructions inside data to help a computer program process the data. Most markup schemes, such as troff, TeX, and HTML, have instructions that are optimized for one purpose, such as formatting the document to be printed or to be displayed on a computer screen. These languages rely on a presentational description of data, which controls typeface, font size, color, or other media-specific properties. Although such markup can result in nicely formatted documents, it can be like a prison for your data, consigning it to one format forever; you won't be able to extract your data for other purposes without significant work.

That's where XML comes in. It's a generic markup language that describes data according to its structure and purpose, rather than with specific formatting instructions. The actual presentation information is stored somewhere else, such as in a stylesheet. What's left is a functional description of the parts of your document, which is suitable for many different kinds of processing. With proper use of XML, your document will be ready for an unlimited variety of applications and purposes.

Now let's review the basic components of XML. Its most important feature is the element. Elements are encapsulated regions of data that serve a unique role in your document. For example, consider a typical book, composed of a preface, chapters, appendixes, and an index. In XML, marking up each of these sections as a unique element within the book would be appropriate. Elements may themselves be divided into other elements; you might find the chapter's title, paragraphs, examples, and sections all marked up as elements. This division continues as deeply as necessary, so even a paragraph can contain elements such as emphasized text, quotations, and hypertext links.

Besides dividing text into a hierarchy of regions, elements associate a label and other properties with the data. Every element has a name, or element type, usually describing its function in the document. Thus, a chapter element could be called a "chapter" (or "chapt" or "ch" -- whatever you fancy). An element can include other information besides the type, using a name-value pair called an attribute. Together, an element's type and attributes distinguish it from other elements in the document.

Example 2-1 shows a typical piece of XML.

Example 2-1. An XML fragment

<list id="eriks-todo-47">
  <title>Things to Do This Week</title>
  <item>clean the aquarium</item>
  <item>mow the lawn</item>
  <item priority="important">save the whales</item>
</list>

This is, as you've probably guessed, a to-do list with three items and a title. Anyone who has worked with HTML will recognize the markup. The pieces of text surrounded by angle brackets ("<" and ">") are called tags, and they act as bookends for elements. Every nonempty element must have both a start and end tag, each containing the element type label. The start tag can optionally contain a number of attributes (name-value pairs like priority="important"). Thus, the markup is pretty clear and unambiguous -- even a human can read it.

A human can read it, but more importantly, a computer program can read it very easily. The framers of XML have taken great care to ensure that XML is easy to read by all XML processors, regardless of the types of tags used or the context. If your markup follows all the proper syntactic rules, then the XML is absolutely unambiguous. This makes processing it much easier, since you don't have to add code to handle unclear situations.

Consider HTML, as it was originally defined (an application of XML's predecessor, SGML).[5] For certain elements, it was acceptable to omit the end tag, and it's usually possible to tell from the context where an element should end. Even so, making code robust enough to handle every ambiguous situation comes at the price of complexity and inaccurate output from bad guessing. Now imagine how it would be if the same processor had to handle any element type, not just the HTML elements. Generic XML processors can't make assumptions about how elements should be arranged. An ambiguous situation, such as the omission of an end tag, would be disastrous.

[5]Currently, XHTML is an XML-legal variant of HTML that HTML authors are encouraged to adopt in support of coming XML tools. XML enables different kinds of markup to be processed by the same programs (e.g., editors, syntax-checkers, or formatters). HTML will soon be joined on the Web by such XML-derived languages as DocBook and MathML.

Any piece of XML can be represented in a diagram called a tree, a structure familiar to most programmers. At the top (since trees in computer science grow upside down) is the root element. The elements that are contained one level down branch from it. Each element may contain elements at still deeper levels, and so on, until you reach the bottom, or "leaves" of the tree. The leaves consist of either data (text) or empty elements. An element at any level can be thought of as the root of its own tree (or subtree, if you prefer to call it that). A tree diagram of the previous example is shown in Figure 2-1.

Figure 2-1. A to-do list represented as a tree structure

Besides the arboreal analogy, it's also useful to speak of XML genealogically. Here, we describe an element's content (both data and elements) as its descendants, and the elements that contain it as its ancestors. In our list example, each <item> element is a child of the same parent, the <list> element, and a sibling of the others. (We generally don't carry the terminology too far, as talking about third cousins twice-removed can make your head hurt.) We will use both the tree and family terminology to describe element relationships throughout the book.

Previous | Home | Next
2. An XML Recap | Book Index | 2.3. Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespaces (Perl and XML)

 Perl & XMLSearch this book

2.3. Namespaces

It's sometimes useful to divide up your elements and attributes into groups, or namespaces . A namespace is to an element somewhat as a surname is to a person. You may know three people named Mike, but no two of them have the same last name. To illustrate this concept, look at the document in Example 2-2.

Example 2-2. A document using namespaces

<?xml version="1.0"?>
<report>
  <title>Fish and Bicycles: A Connection?</title>
  <para>I have found a surprising relationship
  of fish to bicycles, expressed by the equation
  <equation>f = kb+n</equation>. The graph below illustrates
  the data curve of my experiment:</para>
  <chart xmlns:graph="http://mathstuff.com/dtds/chartml/">
    <graph:dimension>
      <graph:axis>fish</graph:axis>
      <graph:start>80</graph:start>
      <graph:end>99</graph:end>
      <graph:interval>1</graph:interval>
    </graph:dimension>
    <graph:dimension>
      <graph:axis>bicycle</graph:axis>
      <graph:start>0</graph:start>
      <graph:end>1000</graph:end>
      <graph:interval>50</graph:interval>
    </graph:dimension>
    <graph:equation>fish=0.01*bicycle+81.4</graph:equation>
  </graph:chart>
</report>

Two namespaces are at play in this example. The first is the default namespace, where elements and attributes lack a colon in their name. The elements whose names contain graph: are from the "chartml" namespace (something we just made up). graph: is a namespace prefix that, when attached to an element or attribute name, becomes a qualified name. The two <equation> elements are completely different element types, with a different role to play in the document. The one in the default namespace is used to format an equation literally, and the one in the chart namespace helps a graphing program generate a curve.

A namespace must always be declared in an element that contains the region where it will be used. This is done with an attribute of the form xmlns:prefix=URL, where prefix is the namespace prefix to be used (in this case, graph:) and URL is a unique identifier in the form of a URL or other resource identifier. Outside of the scope of this element, the namespace is not recognized.

Besides keeping two like-named element types or attribute types apart, namespaces serve a vital function in helping an XML processor format a document. Sometimes the change in namespace indicates that the default formatter should be replaced with a kind that handles a specific kind of data, such as the graph in the example. In other cases, a namespace is used to "bless" markup instructions to be treated as meta-markup, as in the case of XSLT.

Namespaces are emerging as a useful part of the XML tool set. However, they can raise a problem when DTDs are used. DTDs, as we will explain later, may contain declarations that restrict the kinds of elements that can be used to finite sets. However, it can be difficult to apply namespaces to DTDs, which have no special facility for resolving namespaces or knowing that elements and attributes that fall under a namespace (beyond the ever-present default one) are defined according to some other XML application. It's difficult to know this information partly because the notion of namespaces was added to XML long after the format of DTDs, which have been around since the SGML days, was set in stone. Therefore, namespaces can be incompatible with some DTDs. This problem is still unresolved, though not because of any lack of effort in the standards community.

Chapter 10, "Coding Strategies" covers some practical issues that emerge when working with namespaces.

Previous | Home | Next
2.2. Markup, Elements, and Structure | Book Index | 2.4. Spacing
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Spacing (Perl and XML)

 Perl & XMLSearch this book

2.4. Spacing

You'l l notice in examples throughout this book that we indent elements and add spaces wherever it helps make the code more readable to humans. Doing so is not unreasonable if you ever have to edit or inspect XML code personally. Sometimes, however, this indentation can result in space that you don't want in your final product. Since XML has a make-no-assumptions policy toward your data, it may seem that you're stuck with all that space.

One solution is to make the XML processor smarter. Certain parsers can decide whether to pass space along to the processing application.[6] They can determine from the element declarations in the DTD when space is only there for readability and is not part of the content. Alternatively, you can instruct your processor to specialize in a particular markup language and train it to treat some elements differently with respect to space.

[6]A parser is a specialized XML handler that preprocesses a document for the rest of the program. Different parsers have varying levels of "intelligence" when interpreting XML. We'll describe this topic in greater detail in Chapter 3, "XML Basics: Reading and Writing".

When neither option applies to your problem, XML provides a way to let a document tell the processor when space needs to be preserved. The reserved attribute xml:space can be used in any element to specify whether space should be kept as is or removed.[7]

[7]We know that it's reserved because it has the special "xml" prefix. The XML standard defines special uses and meanings for elements and attributes with this prefix.

For example:

<address-label xml:space='preserve'>246 Marshmellow Ave.
Slumberville, MA
02149</address-label>

In this case, the characters used to break lines in the address are retained for all future processing. The other setting for xml:space is "default," which means that the XML processor has to decide what to do with extra space.

Previous | Home | Next
2.3. Namespaces | Book Index | 2.5. Entities
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Entities (Perl and XML)

 Perl & XMLSearch this book

2.5. Entities

For your authoring convenience, XML has another feature called entities. An entity is useful when you need a placeholder for text or markup that would be inconvenient or impossible to just type in. It's a piece of XML set aside from your document;[8] you use an entity reference to stand in for it. An XML processor must resolve all entity references with their replacement text at the time of parsing. Therefore, every referenced entity must be declared somewhere so that the processor knows how to resolve it.

[8]Technically, the whole document is one entity, called the document entity. However, people usually use the term "entity" to refer to a subset of the document.

The Document Type Declaration (DTD) is the place to declare an entity. It has two parts, the internal subset that is part of your document, and the external subset that lives in another document. (Often, people talk about the external subset as "the DTD" and call the internal subset "the internal subset," even though both subsets together make up the whole DTD.) In both places, the method for declaring entities is the same. The document in Example 2-3 shows how this feature works.

Example 2-3. A document with entity declarations

<!DOCTYPE memo
  SYSTEM "/xml-dtds/memo.dtd"
[
  <!ENTITY companyname "Willy Wonka's Chocolate Factory">
  <!ENTITY healthplan  SYSTEM "hp.txt">
]>

<memo>
  <to>All Oompa-loompas</to>
  <para>
    &companyname; has a new owner and CEO, Charlie Bucket. Since
    our name, &companyname;, has considerable brand recognition,
    the board has decided not to change it. However, at Charlie's
    request, we will be changing our healthcare provider to the
    more comprehensive &Uuml;mpacare, which has better facilities
    for 'Loompas (text of the plan to follow). Thank you for working
    at &companyname;!
  </para>
  &healthplan;
</memo>

Let's examine the new material in this example. At the top is the DTD, a special markup instruction that contains a lot of important information, including the internal subset and a path to the external subset. Like all declarative markup (i.e., it defines something new), it starts with an exclamation point, and is followed by a keyword, DOCTYPE. After that keyword is the name of an element that will be used to contain the document. We call that element the root element or document element. This element is followed by a path to the external subset, given by SYSTEM "/xml-dtds/memo.dtd", and the internal subset of declarations, enclosed in square brackets ([ ]).

The external subset is used for declarations that will be used in many documents, so it naturally resides in another file. The internal subset is best used for declarations that are local to the document. They may override declarations in the external subset or contain new ones. As you see in the example, two entities are declared in the internal subset. An entity declaration has two parameters: the entity name and its replacement text. The entities are named companyname and healthplan.

These entities are called general entities and are distinguished from other kinds of entities because they are declared by you, the author. Replacement text for general entities can come from two different places. The first entity declaration defines the text within the declaration itself. The second points to another file where the text resides. It uses a system identifier to specify the file's location, acting much like a URL used by a web browser to find a page to load. In this case, the file is loaded by an XML processor and inserted verbatim wherever an entity is referenced. Such an entity is called an external entity.

If you look closely at the example, you'll see markup instructions of the form &name;. The ampersand (&) indicates an entity reference, where name is the name of the entity being referenced. The same reference can be used repeatedly, making it a convenient way to insert repetitive text or markup, as we do with the entity companyname.

An entity can contain markup as well as text, as is the case with healthplan (actually, we don't know what's in that entity because it's in another file, but since it's going to be a large document, you can assume it will have markup as well as text). An entity can even contain other entities, to any nesting level you want. The only restriction is that entities can't contain themselves, at any level, lest you create a circular definition that can never be constructed by the XML processor. Some XML technologies, such as XSLT, do let you have fun with recursive logic, but think of entity references as code constants -- playing with circular references here will make any parser very unhappy.

Finally, the &Uuml; entity reference is declared somewhere in the external subset to fill in for a character that the chocolate factory's ancient text editor programs have trouble rendering -- in this case, a capital "U" with an umlaut over it: Ü. Since the referenced entity is one character wide, the reference in this case is almost more of an alias than a pointer. The usual way to handle unusual characters (the way that's built into the XML specification) involves using a numeric character entity, which, in this case, would be &#00DC;. 0x00DC is the hexadecimal equivalent of the number 220, which is the position of the U-umlaut character in Unicode (the character set used natively by XML, which we cover in more detail in the next section).

However, since an abbreviated descriptive name like Uuml is generally easier to remember than the arcane 00DC, some XML users prefer to use these types of aliases by placing lines such as this into their documents' DTDs:

<!ENTITY % Uuml &#x00DC;>

XML recognizes only five built-in, named entity references, shown in Table 2-1. They're not actually references, but are escapes for five punctuation marks that have special meaning for XML.

Table 2-1. XML entity references

Character | Entity
< | &lt;
> | &gt;
& | &amp;
" | &quot;
' | &apos;

The only two of these references that must be used throughout any XML document are &lt and &amp;. Element tags and entity references can appear at any point in a document. No parser could guess, for example, whether a < character is used as a less-than math symbol or as a genuine XML token; it will always assume the latter and will report a malformed document if this assumption proves false.

Previous | Home | Next
2.4. Spacing | Book Index | 2.6. Unicode, Character Sets, and Encodings
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Unicode, Character Sets, and Encodings (Perl and XML)

 Perl & XMLSearch this book

2.6. Unicode, Character Sets, and Encodings

At low levels, computers see text as a series of positive integer numbers mapped onto character sets, which are collections of numbered characters (and sometimes control codes) that some standards body created. A very common collection is the venerable US-ASCII character set, which contains 128 characters, including upper- and lowercase letters of the Latin alphabet, numerals, various symbols and space characters, and a few special print codes inherited from the old days of teletype terminals. By adding on the eighth bit, this 7-bit system is extended into a larger set with twice as many characters, such as ISO-Latin1, used in many Unix systems. These characters include other European characters, such as Latin letters with accents, Icelandic characters, ligatures, footnote marks, and legal symbols. Alas, humanity, a species bursting with both creativity and pride, has invented many more linguistic symbols than can be mapped onto an 8-bit number.

For this reason, a new character encoding architecture called Unicode has gained acceptance as the standard way to represent every written script in which people might want to store data (or write computer code). Depending on the flavor used, it uses up to 32 bits to describe a character, giving the standard room for millions of individual glyphs. For over a decade, the Unicode Consortium has been filling up this space with characters ranging from the entire Han Chinese character set to various mathematical, notational, and signage symbols, and still leaves the encoding space with enough room to grow for the coming millennium or two.

Given all this effort we're putting into hyping it, it shouldn't surprise you to learn that, while an XML document can use any type of encoding, it will by default assume the Unicode-flavored, variable-length encoding known as UTF-8. This encoding uses between one and six bytes to encode the number that represents the character's Unicode address and the character's length in bytes, if that address is greater than 255. It's possible to write an entire document in 1-byte characters and have it be indistinguishable from ISO Latin-1 (a humble address block with addresses ranging from 0 to 255), but if you need the occasional high character, or if you need a lot of them (as you would when storing Asian-language data, for example), it's easy to encode in UTF-8. Unicode-aware processors handle the encoding correctly and display the right glyphs, while older applications simply ignore the multibyte characters and pass them through unharmed. Since Version 5.6, Perl has handled UTF-8 characters with increasing finesse. We'll discuss Perl's handling of Unicode in more depth in Chapter 3, "XML Basics: Reading and Writing".

Previous | Home | Next
2.5. Entities | Book Index | 2.7. The XML Declaration
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The XML Declaration (Perl and XML)

 Perl & XMLSearch this book

2.7. The XML Declaration

After reading about character encodings, an astute reader may wonder how to declare the encoding in the document so an XML processor knows which one you're using. The answer is: declare the decoding in the XML declaration. The XML declaration is a line at the very top of a document that describes the kind of markup you're using, including XML version, character encoding, and whether the document requires an external subset of the DTD. The declaration looks like this:

<?xml version="1.0" encoding="utf8" standalone="yes"?>

The declaration is optional, as are each of its parameters (except for the required version attribute). The encoding parameter is important only if you use a character encoding other than UTF-8 (since it's the default encoding). If explicitly set to "yes", the standalone declaration causes a validating parser to raise an error if the document references external entities.

Previous | Home | Next
2.6. Unicode, Character Sets, and Encodings | Book Index | 2.8. Processing Instructions and Other Markup
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Processing Instructions and Other Markup (Perl and XML)

 Perl & XMLSearch this book

2.8. Processing Instructions and Other Markup

Besides elements, you can use several other syntactic objects to make XML easier to manage. Processing instructions (PIs) are used to convey information to a particular XML processor. They specify the intended processor with a target parameter, which is followed by an optional data parameter. Any program that doesn't recognize the target simply skips the PI and pretends it never existed. Here is an example based on an actual behind-the-scenes O'Reilly book hacking experience:

<?file-breaker start chap04.xml?><chapter>
<title>The very long title<?lb?>that seemed to go on forever and ever</title>
<?xml2pdf vspace 10pt?>

The first PI has a target called file-breaker and its data is chap04.xml. A program reading this document will look for a PI with that target keyword and will act on that data. In this case, the goal is to create a new file and save the following XML into it.

The second PI has only a target, lb. We have actually seen this example used in documents to tell an XML processor to create a line break at that point. This example has two problems. First, the PI is a replacement for a space character; that's bad because any program that doesn't recognize the PI will not know that a space should be between the two words. It would be better to place a space after the PI and let the target processor remove any following space itself. Second, the target is an instruction, not an actual name of a program. A more unique name like the one in the next PI, xml2pdf, would be better (with the lb appearing as data instead).

PIs are convenient for developers. They have no solid rules that specify how to name a target or what kind of data to use, but in general, target names ought to be very specific and data should be very short.

Those who have written documents using Perl's built-in Plain Old Documentation mini-markup language[9] hackers may note a similarity between PIs and certain POD directives, particularly the =for paragraphs and =begin/=end blocks. In these paragraphs and blocks, you can leave little messages for a POD processor with a target and some arguments (or any string of text).

[9]The gory details of which lie in Chapter 26 of Programming Perl, Third Edition or in the perlpod manpage.

Another useful markup object is the XML comment. Comments are regions of text that any XML processor ignores. They are meant to hold information for human eyes only, such as notes written by authors to themselves and their collaborators. They are also useful for turning "off" regions of markup -- perhaps if you want to debug the document or you're afraid to delete something altogether. Here's an example:

<!-- this is invisible to the parser -->
This is perfectly visible XML content.
<!--
  <para>This paragraph is no longer part of the document.</para>
-->

Note that these comments look and work exactly like their HTML counterparts.

The only thing you can't put inside a comment is another comment. You can't even feint at nesting comments; the string " -- ", for example, is illegal in a comment, no matter how you use it.

The last syntactic convenience we will discuss is the CDATA section. CDATA stands for character data, which in XML parlance means unparsed content. In other words, the XML processor treats an entire CDATA section as though it contains no markup at all -- even things that look like markup. This is useful if you want to include a large region of illegal characters like <, >, and & that would be difficult to convert into character entity references.

For example:

<codelisting>
<![CDATA[if( $val > 3 && @lines ) {
  $input = <FILE>;
}]]>
</codelisting>

Everything after <![CDATA[ and before the ]]> is treated as nonmarkup data, so the markup symbols are perfectly fine. We rarely use CDATA sections because they are kind of unsightly, in our humble opinion, and make writing XML processing code a little harder. But it's there if you need it.[10]

[10]We use CDATA throughout the DocBook-flavored XML that makes up this book. We wrapped all the code listings and sample XML documents in it so we didn't have to suffer the bother of escaping every < and & that appears in them.

Previous | Home | Next
2.7. The XML Declaration | Book Index | 2.9. Free-Form XML and Well-Formed Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Free-Form XML and Well-Formed Documents (Perl and XML)

 Perl & XMLSearch this book

2.9. Free-Form XML and Well-Formed Documents

XML's grandfather, SGML, required that every element and attribute be documented thoroughly with a long list of declarations in the DTD. We'll describe what we mean by that thorough documentation in the next section, but for now, imagine it as a blueprint for a document. This blueprint adds considerable overhead to the processing of a document and was a serious obstacle to SGML's status as a popular markup language for the Internet. HTML, which was originally developed as an SGML instance, was hobbled by this enforced structure, since any "valid" HTML document had to conform to the HTML DTD. Hence, extending the language was impossible without approval by a web committee.

XML does away with that requirement by allowing a special condition called free-form XML. In this mode, a document has to follow only minimal syntax rules to be acceptable. If it follows those rules, the document is well-formed. Following these rules is wonderfully liberating for a developer because it means that you don't have to scan a DTD every time you want to process a piece of XML. All a processor has to do is make sure that minimal syntax rules are followed.

In free-form XML, you can choose the name of any element. It doesn't have to belong to a sanctioned vocabulary, as is the case with HTML. Including frivolous markup into your program is a risk, but as long as you know what you're doing, it's okay. If you don't trust the markup to fit a pattern you're looking for, then you need to use element and attribute declarations, as we describe in the next section.

What are these rules? Here's a short list as seen though a coarse-grained spyglass:

  • A document can have only one top-level element, the document element, that contains all the other elements and data. This element does not include the XML declaration and document type declaration, which must precede it.

  • Every element with content must have both a start tag and an end tag.

  • Element and attribute names are case sensitive, and only certain characters can be used (letters, underscores, hyphens, periods, and numbers), with only letters and underscores eligible as the first character. Colons are allowed, but only as part of a declared namespace prefix.

  • All attributes must have values and all attribute values must be quoted.

  • Elements may never overlap; an element's start and end tags must both appear within the same element.

  • Certain characters, including angle brackets (< >) and the ampersand (&) are reserved for markup and are not allowed in parsed content. Use character entity references instead, or just stick the offending content into a CDATA section.

  • Empty elements must use a syntax distinguishing them from nonempty element start tags. The syntax requires a slash (/) before the closing bracket (>) of the tag.

You will encounter more rules, so for a more complete understanding of well-formedness, you should either read an introductory book on XML or look at the W3C's official recommendation at http://www.w3.org/XML.

If you want to be able to process your document with XML-using programs, make sure it is always well formed. (After all, there's no such thing as non-well-formed XML.) A tool often used to check this status is called a well-formedness checker, which is a type of XML parser that reports errors to the user. Often, such a tool can be detailed in its analysis and give you the exact line number in a file where the problem occurs. We'll discuss checkers and parsers in Chapter 3, "XML Basics: Reading and Writing".

Previous | Home | Next
2.8. Processing Instructions and Other Markup | Book Index | 2.10. Declaring Elements and Attributes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Declaring Elements and Attributes (Perl and XML)

 Perl & XMLSearch this book

2.10. Declaring Elements and Attributes

When you need an extra level of quality control (beyond the healthful status implied by the "well-formed" label), define the grammar patterns of your markup language in the DTD. Defining the patterns will make your markup into a formal language, documented much like a standard published by an international organization. With a DTD, a program can tell in short order whether a document conforms to, or, as we say, is a valid example of, your document type.

Two kinds of declarations allow a DTD to model a language. The first is the element declaration. It adds a new name to the allowed set of elements and specifies, in a special pattern language, what can go inside the element. Here are some examples:

<!ELEMENT sandwich ((meat | cheese)+ | (peanut-butter, jelly)), condiment+, pickle?)>
<!ELEMENT pickle EMPTY>
<!ELEMENT condiment (PCDATA | mustard | ketchup )*>

The first parameter declares the name of the element. The second parameter is a pattern, a content model in parentheses, or a keyword such as EMPTY. Content models resemble regular expression syntax, the main differences being that element names are complete tokens and a comma is used to indicate a required sequence of elements. Every element mentioned in a content model should be declared somewhere in the DTD.

The other important kind of declaration is the attribute list declaration. With it, you can declare a set of optional or required attributes for a given element. The attribute values can be controlled to some extent, though the pattern restrictions are somewhat limited. Let's look at an example:

<!ATTLIST sandwich
  id        ID        #REQUIRED
  price     CDATA     #IMPLIED
  taste     CDATA     #FIXED     "yummy"
  name      (reuben | ham-n-cheese | BLT | PB-n-J )     'BLT'
>

The general pattern of an attribute declaration has three parts: a name, a data type, and a behavior. This example declares three attributes for the element <sandwich>. The first, named id, is of type ID, which is a unique string of characters that can be used only once in any ID-type attribute throughout the document, and is required because of the #REQUIRED keyword. The second, named price, is of type CDATA and is optional, according to the #IMPLIED keyword. The third, named taste, is fixed with the value "yummy" and can't be changed (all <sandwich> elements will inherit this attribute automatically). Finally, the attribute name is one of an enumerated list of values, with the default being 'BLT'.

Though they have been around for a long time and have been very successful, element and attribute declarations have some major flaws. Content model syntax is relatively inflexible. For example, it's surprisingly hard to express the statement "this element must contain one each of the elements A, B, C, and D in any order" (try it and see!). Also, the character data can't be constrained in any way. You can't ensure that a <date> contains a valid date, and not a street address, for example. Third, and most troubling for the XML community, is the fact that DTDs don't play well with namespaces. If you use element declarations, you have to declare all elements you would ever use in your document, not just some of them. If you want to leave open the possibility of importing some element types from another namespace, you can't also use a DTD to validate your document -- at least not without playing the mix-and-match DTD-combination games we described earlier, and combining DTDs doesn't always work , anyway.

Previous | Home | Next
2.9. Free-Form XML and Well-Formed Documents | Book Index | 2.11. Schemas
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schemas (Perl and XML)

 Perl & XMLSearch this book

2.11. Schemas

Several proposed alternate language schemas address the shortcomings of DTD declarations. The W3C's recommended language for doing this is called XML Schema. You should know, however, that it is only one of many competing schema-type languages, some of which may be better suited to your needs. If you prefer to use a competing schema, check CPAN to see if a module has been written to handle your favorite flavor of schemas.

Unlike DTD syntax, XML Schemas are themselves XML documents, making it possible to use many XML tools to edit them. Their real power, however, is in their fine-grained control over the form your data takes. This control makes it more attractive for documents for which checking the quality of data is at least as important as ensuring it has the proper structure. Example 2-4 shows a schema designed to model census forms, where data type checking is necessary.

Example 2-4. An XML schema

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">

  <xs:annotation>
    <xs:documentation>
      Census form for the Republic of Oz
      Department of Paperwork, Emerald City
    </xs:documentation>
  </xs:annotation>

  <xs:element name="census" type="CensusType"/>

  <xs:complexType name="CensusType">
    <xs:element name="censustaker" type="xs:decimal" minoccurs="0"/>
    <xs:element name="address" type="Address"/>
    <xs:element name="occupants" type="Occupants"/>
    <xs:attribute name="date" type="xs:date"/>
  </xs:complexType>

  <xs:complexType name="Address">
    <xs:element name="number" type="xs:decimal"/>
    <xs:element name="street" type="xs:string"/>
    <xs:element name="city"   type="xs:string"/>
    <xs:element name="province"  type="xs:string"/>
    <xs:attribute name="postalcode" type="PCode"/>
  </xs:complexType>

  <xs:simpleType name="PCode" base="xs:string">
    <xs:pattern value="[A-Z]-d{3}"/>
  </xs:simpleType>

  <xs:complexType name="Occupants">
    <xs:element name="occupant" minOccurs="1" maxOccurs="20">
     <xs:complexType>
      <xs:element name="firstname" type="xs:string"/>
      <xs:element name="surname" type="xs:string"/>
      <xs:element name="age">
       <xs:simpleType base="xs:positive-integer">
        <xs:maxExclusive value="200"/>
       </xs:simpleType>
      </xs:element>
     </xs:complexType>
    </xs:element>
   </xs:complexType>

</xs:schema>

The first line identifies this document as a schema and associates it with the XML Schema namespace. The next structure, <annotation>, is a place to document the schema's purpose and other details. After this documentation, we get into the fun stuff and start declaring element types.

We start by declaring the root of our document type, an element called <census>. The declaration is an element of type <xs:element>. Its attributes assign the name "census" and type of description for <census>, "CensusType". In schemas, unlike DTDs, the content descriptions are often kept separate from the declarations, making it easier to define generic element types and assign multiple elements to them. Further down in the schema is the actual content description, an <xs:complexType> element with name="CensusType". It specifies that a <census> contains an optional <censustaker>, followed by a required <occupants> and a required <address>. It also must have an attribute called date.

Both the attribute date and the element <censustaker> have specific data patterns assigned in the description of <census>: a date and a decimal number. If your <census> document had anything but a numerical value as its content, it would be an error according to this schema. You couldn't get this level of control with DTDs.

Schemas can check for many types. These types include numerical values like bytes, floating-point numbers, long integers, binary numbers, and boolean values; patterns for marking times and durations; Internet addresses and URLs; IDs, IDREFs, and other types borrowed from DTDs; and strings of character data.

An element type description uses properties called facets to set even more detailed limits on content. For example, the schema above gives the <age> element, whose data type is positive-integer, a maximum value of 200 using the max-inclusive facet. XML Schemas have many other facets, including precision, scale, encoding, pattern, enumeration, and max-length.

The Address description introduces a new concept: user-defined patterns. With this technique, we define postalcode with a pattern code: [A-Z]-d{3}. Using this code is like saying, "Accept any alphabetic character followed by a dash and three digits." If no data type fits your needs, you can always make up a new one.

Schemas are an exciting new technology that makes XML more useful, especially with data-specific applications such as data entry forms. We'll leave a full account of its uses and forms for another book.

2.11.1. Other Schema Strategies

While it has the blessing of the W3C, XML Schema is not the only schema option available for flexible document validation. Some programmers prefer the methods available through specifications like RelaxNG (available at http://www.oasis-open.org/committees/relax-ng/) or Schematron (http://www.ascc.net/xml/resource/schematron/schematron.html), which achieve the same goals through different philosophical means. Since the latter specification has Perl implementations that are currently available , we'll examine it further in Chapter 3, "XML Basics: Reading and Writing".

Previous | Home | Next
2.10. Declaring Elements and Attributes | Book Index | 2.12. Transformations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Transformations (Perl and XML)

 Perl & XMLSearch this book

2.12. Transformations

The last topic we want to introduce is the concept of transformations. In XML, a transformation is a process of restructuring or converting a document into another form. The W3C recommends a language for transforming XML called XML Stylesheet Language for Transformations (XSLT). It's an incredibly useful and fun technology to work with.

Like XML Schema, an XSLT transformation script is an XML document. It's composed of template rules, each of which is an instruction for how to turn one element type into something else. The term template is often used to mean an example of how something should look, with blanks that you should fill in. That's exactly how template rules work: they are examples of how the final document should be, with the blanks filled in by the XSLT processor.

Example 2-5 is a rudimentary transformation that converts a simple DocBook XML document into an HTML page.

Example 2-5. An XSLT transformation document

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

<xsl:output method="html"/>

<!-- RULE FOR BOOK ELEMENT -->
<xsl:template match="book">
  <html>
    <head>
      <title><xsl:value-of select="title"/></title>
    </head>
    <body>
      <h1><xsl:value-of select="title"/></h1>
      <h3>Table of Contents</h3>
      <xsl:call-template name="toc"/>
      <xsl:apply-templates select="chapter"/>
    </body>
  </html>
</xsl:template>

<!-- RULE FOR CHAPTER -->
<xsl:template match="chapter">
  <xsl:apply-templates/>
</xsl:template>

<!-- RULE FOR CHAPTER TITLE -->
<xsl:template match="chapter/title">
  <h2>
    <xsl:text>Chapter </xsl:text>
    <xsl:number count="chapter" level="any" format="1"/>
  </h2>
  <xsl:apply-templates/>
</xsl:template>

<!-- RULE FOR PARA -->
<xsl:template match="para">
  <p><xsl:apply-templates/></p>
</xsl:template>

<!-- NAMED RULE: TOC -->
<xsl:template name="toc">
  <xsl:if test="count(chapter)>0">
    <xsl:for-each select="chapter">
      <xsl:text>Chapter </xsl:text>
      <xsl:value-of select="position( )"/>
      <xsl:text>: </xsl:text>
      <i><xsl:value-of select="title"/></i>
      <br/>
    </xsl:for-each>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>

First, the XSLT processor reads the stylesheet and creates a table of template rules. Next, it parses the source XML document (the one to be converted) and traverses it one node at a time. A node is an element, a piece of text, a processing instruction, an attribute, or a namespace declaration. For each node, the XSLT processor tries to find the best matching rule. It applies the rule, outputting everything the template says it should, jumping to other rules as necessary.

Example 2-6 is a sample document on which you can run the transformation.

Example 2-6. A document to transform

<book>
  <title>The Blathering Brains</title>
  <chapter>
    <title>At the Bazaar</title>
    <para>What a fantastic day it was. The crates were stacked
          high with imported goods: dates, bananas, dried meats,
          fine silks, and more things than I could imagine. As I
          walked around, savoring the fragrances of cinnamon and
          cardamom, I almost didn't notice a small booth with a
          little man selling brains.</para>
    <para>Brains! Yes, human brains, still quite moist and squishy,
          swimming in big glass jars full of some greenish
          fluid.</para>
    <para>"Would you like a brain, sir?" he asked. "Very reasonable
          prices. Here is Enrico Fermi's brain for only two
          dracmas. Or, perhaps, you would prefer Aristotle?  Or the
          great emperor Akhnaten?"</para>
    <para>I recoiled in horror...</para>
  </chapter>
</book>

Let's walk through the transformation.

  1. The first element is <book>. The best matching rule is the first one, because it explicitly matches "book." The template says to output tags like <html>, <head>, and <title>. Note that these tags are treated as data markup because they don't have the xsl: namespace prefix.

  2. When the processor gets to the XSLT instruction <xsl:value-of select="title"/>, it has to find a <title> element that is a child of the current element, <book>. Then it must obtain the value of that element, which is simply all the text contained within it. This text is output inside a <title> element as the template directs.

  3. The processor continues in this way until it gets to the <xsl:call-template name="toc"/> instruction. If you look at the bottom of the stylesheet, you'll find a template rule that begins with <xsl:template name="toc">. This template rule is a named template and acts like a function call. It assembles a table of contents and returns the text to the calling rule for output.

  4. Inside the named template is an element called <xsl:if test="count(chapter)>0">. This element is a conditional statement whose test is whether more than one <chapter> is inside the current element (still <book>). The test passes, and processing continues inside the element.

  5. The <xsl:for-each select="chapter"> instruction causes the processor to visit each <chapter> child element and temporarily make it the current element while in the body of the <xsl:for-each> element. This step is analogous to a foreach( ) loop in Perl. The <xsl:value-of select="position( )"/> statement derives the numerical position of each <chapter> and outputs it so that the result document reads "Chapter 1," "Chapter 2," and so on.

  6. The named template "toc" returns its text to the calling rule and execution continues. Next, the processor receives an <xsl:apply-templates select="chapter"/> directive. An output of <xsl:apply-templates> without any attributes means that the processor should then process each of the current element's children, making it the current element. However, since a select="chapter" attribute is present, only children who are of type <chapter> should be processed. After all descendants have been processed and this instruction returns its text, it will be output and the rest of the rule will be followed until the end.

  7. Moving on to the first <chapter> element, the processor locates a suitable rule and sees only an <xsl:apply-tempaltes/> rule. The rest of the processing is pretty easy, as the rules for the remaining elements, <title> and <para>, are straightforward.

XSLT is a rich language for handling transformations, but often leaves something to be desired. It can be slow on large documents, since it has to build an internal representation of the whole document before it can do any processing. Its syntax, while a remarkable achievement for XML, is not as expressive and easy to use as Perl. We will explore numerous Perl solutions to some problems that XSL could also solve. You'll have to decide whether you prefer XSLT's simplicity or Perl's power.

That's our whirlwind tour of XML. Next, we'll jump into the fundamentals of XML processing with Perl using parsers and basic writers. At this point, you should have a good idea of what XML is used for and how it's used, and you should be able to recognize all the parts when you see them. If you still have any doubts, stop now and grab an XML tutorial.

Previous | Home | Next
2.11. Schemas | Book Index | 3. XML Basics: Reading and Writing
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Basics: Reading and Writing (Perl and XML)

 Perl & XMLSearch this book

Chapter 3. XML Basics: Reading and Writing

Contents:

XML Parsers
XML::Parser
Stream-Based Versus Tree-Based Processing
Putting Parsers to Work
XML::LibXML
XML::XPath
Document Validation
XML::Writer
Character Sets and Encodings

This chapter covers the two most important tasks in working with XML: reading it into memory and writing it out again. XML is a structured, predictable, and standard data storage format, and as such carries a price. Unlike the line-by-line, make-it-up-as-you-go style that typifies text hacking in Perl, XML expects you to learn the rules of its game -- the structures and protocols outlined in Chapter 2, "An XML Recap" -- before you can play with it. Fortunately, much of the hard work is already done, in the form of module-based parsers and other tools that trailblazing Perl and XML hackers already created (some of which we touched on in Chapter 1, "Perl and XML").

Knowing how to use parsers is very important. They typically drive the rest of the processing for you, or at least get the data into a state where you can work with it. Any good programmer knows that getting the data ready is half the battle. We'll look deeply into the parsing process and detail the strategies used to drive processing.

Parsers come with a bewildering array of options that let you configure the output to your needs. Which character set should you use? Should you validate the document or merely check if it's well formed? Do you need to expand entity references, or should you keep them as references? How can you set handlers for events or tell the parser to build a tree for you? We'll explain these options fully so you can get the most out of parsing.

Finally, we'll show you how to spit XML back out, which can be surprisingly tricky if one isn't aware of XML's expectations regarding text encoding. Getting this step right is vital if you ever want to be able to use your data again without painful hand fixing.

3.1. XML Parsers

File I/O is an intrinsic part of any programming language, but it has always been done at a fairly low level: reading a character or a line at a time, running it through a regular expression filter, etc. Raw text is an unruly commodity, lacking any clear rules for how to separate discrete portions, other than basic, flat concepts such as newline-separated lines and tab-separated columns. Consequently, more data packaging schemes are available than even the chroniclers of Babel could have foreseen. It's from this cacophony that XML has risen, providing clear rules for how to create boundaries between data, assign hierarchy, and link resources in a predictable, unambiguous fashion. A program that relies on these rules can read any well-formed XML document, as if someone had jammed a babelfish into its ear.[11]

[11]Readers of Douglas Adams' book The Hitchhiker's Guide to the Galaxy will recall that a babelfish is a living, universal language-translation device, about the size of an anchovy, that fits, head-first, into a sentient being's aural canal.

Where can you get this babelfish to put in your program's ear? An XML parser is a program or code library that translates XML data into either a stream of events or a data object, giving your program direct access to structured data. The XML can come from one or more files or filehandles, a character stream, or a static string. It could be peppered with entity references that may or may not need to be resolved. Some of the parts could come from outside your computer system, living in some far corner of the Internet. It could be encoded in a Latin character set, or perhaps in a Japanese set. Fortunately for you, the developer, none of these details have to be accounted for in your program because they are all taken care of by the parser, an abstract tunnel between the physical state of data and the crystallized representation seen by your subroutines.

An XML parser acts as a bridge between marked-up data (data packaged with embedded XML instructions) and some predigested form your program can work with. In Perl's case, we mean hashes, arrays, scalars, and objects made of references to these old friends. XML can be complex, residing in many files or streams, and can contain unresolved regions (entities) that may need to be patched up. Also, a parser usually tries to accept only good XML, rejecting it if it contains well-formedness errors. Its output has to reflect the structure (order, containment, associative data) while ignoring irrelevant details such as what files the data came from and what character set was used. That's a lot of work. To itemize these points, an XML parser:

  • Reads a stream of characters and distinguishes between markup and data

  • Optionally replaces entity references with their values

  • Assembles a complete, logical document from many disparate sources

  • Reports syntax errors and optionally reports grammatical (validation) errors

  • Serves data and structural information to a client program

In XML, data and markup are mixed together, so the parser first has to sift through a character stream and tell the two apart. Certain characters delimit the instructions from data, primarily angle brackets (< and >) for elements, comments, and processing instructions, and ampersand (&) and semicolon (;) for entity references. The parser also knows when to expect a certain instruction, or if a bad instruction has occurred; for example, an element that contains data must bracket the data in both a start and end tag. With this knowledge, the parser can quickly chop a character stream into discrete portions as encoded by the XML markup.

The next task is to fill in placeholders. Entity references may need to be resolved. Early in the process of reading XML, the processor will have encountered a list of placeholder definitions in the form of entity declarations, which associate a brief identifier with an entity. The identifier is some literal text defined in the document's DTD, and the entity itself can be defined right there or at the business end of a URL. These entities can themselves contain entity references, so the process of resolving an entity can take several iterations before the placeholders are filled in.

You may not always want entities to be resolved. If you're just spitting XML back out after some minor processing, then you may want to turn entity resolution off or substitute your own routine for handling entity references. For example, you may want to resolve external entity references (entities whose values are in locations external to the document, pointed to by URLs), but not resolve internal ones. Most parsers give you the ability to do this, but none will let you use entity references without declaring them.

That leads to the third task. If you allow the parser to resolve external entities, it will fetch all the documents, local or remote, that contain parts of the larger XML document. In doing so, all these entities get smushed into one unbroken document. Since your program usually doesn't need to know how the document is distributed physically, information about the physical origin of any piece of data goes away once it knits the whole document together.

While interpreting the markup, the parser may trip over a syntactic error. XML was designed to make it very easy to spot such errors. Everything from attributes to empty element tags have rigid rules for their construction so a parser doesn't have to think very hard about it. For example, the following piece of XML has an obvious error. The start tag for the <decree> element contains an attribute with a defective value assignment. The value "now" is missing a second quote character, and there's another error, somewhere in the end tag. Can you see it?

<decree effective="now>All motorbikes
shall be painted red.</decree<

When such an error occurs, the parser has little choice but to shut down the operation. There's no point in trying to parse the rest of the document. The point of XML is to make things unambiguous. If the parser had to guess how the document should look,[12] it would open up the data to uncertainty and you'd lose that precious level of confidence in your program. Instead, the XML framers (wisely, we feel) opted to make XML parsers choke and die on bad XML documents. If the parser likes your XML, it is said to be well formed.

[12]Most HTML browsers try to ignore well-formedness errors in HTML documents, attempting to fix them and move on. While ignoring these errors may seem to be more convenient to the reader, it actually encourages sloppy documents and results in overall degradation of the quality of information on the Web. After all, would you fix parse errors if you didn't have to?

What do we mean by "grammatical errors"? You will encounter them only with so-called validating parsers. A document is considered to be valid if it passes a test defined in a DTD. XML-based languages and applications often have DTDs to set a minimal standard above well-formedness for how elements and data should be ordered. For example, the W3C has posted at least one DTD to describe XHTML (the XML-compliant flavor of HTML), listing all elements that can appear, where they can go, and what they can contain. It would be grammatically correct to put a <p> element inside a <body>, but putting <p> inside <head>, for example, would be incorrect. And don't even think about inserting an element <blooby> anywhere in the document, because it isn't declared anywhere in the DTD.[13] If even one error of this type is in a document, then the whole document is considered invalid. It may be well formed, but not valid against the particular DTD. Often, this level of checking is more of a burden than a help, but it's available if you need it.

[13]If you insist on authoring a <blooby>-enabled web page in XML, you can design your own extension by drafting a DTD that uses entity references to pull in the XHTML DTD, and then defines your own special elements on top of it. At this point it's not officially XHTML anymore, but a subclass thereof.

Rounding out our list is the requirement that a parser ship the digested data to a program or end user. You can do this in many ways, and we devote much of the rest of the book in analyzing them. We can break up the forms into a few categories:

Event stream

First, a parser can generate an event stream: the parser converts a stream of markup characters into a new kind of stream that is more abstract, with data that is partially processed and easier to handle by your program.

Object Representation

Second, a parser can construct a data structure that reflects the information in the XML markup. This construction requires more resources from your system, but may be more convenient because it creates a persistent object that will wait around while you work on it.

Hybrid form

We might call the third group "hybrid" output. It includes parsers that try to be smart about processing, using some advance knowledge about the document to construct an object representing only a portion of your document.

3.1.1. Example (of What Not to Do): A Well-Formedness Checker

We've described XML parsers abstractly, but now it's time to get our hands dirty. We're going to write our own parser whose sole purpose is to check whether a document is well-formed XML or if it fails the basic test. This is about the simplest a parser can get; it doesn't drive any further processing, but just returns a "yes" or "no."

Our mission here is twofold. First, we hope to shave some of the mystique off of XML processing -- at the end of the day, it's just pushing text around. However, we also want to emphasize that writing a proper parser in Perl (or any language) requires a lot of work, which would be better spent writing more interesting code that uses one of the many available XML-parsing Perl modules. To that end, we'll write only a fraction of a pure-Perl XML parser with a very specific goal in mind.

WARNING: Feel free to play with this program, but please don't try to use this code in a production environment! It's not a real Perl and XML solution, but an illustration of the sorts of things that parsers do. Also, it's incomplete and will not always give correct results, as we'll show later. Don't worry; the rest of this book talks about real XML parsers and Perl tools you'll want to use.

The program is a loop in which regular expressions match XML markup objects and pluck them out of the text. The loop runs until nothing is left to remove, meaning the document is well formed, or until the regular expressions can't match anything in the remaining text, in which case it's not well-formed. A few other tests could abort the parsing, such as when an end tag is found that doesn't match the name of the currently open start tag. It won't be perfect, but it should give you a good idea of how a well-formedness parser might work.

Example 3-1 is a routine that parses a string of XML text, tests to see if it is well-formed, and returns a boolean value. We've added some pattern variables to make it easier to understand the regular expressions. For example, the string $ident contains regular expression code to match an XML identifier, which is used for elements, attributes, and processing instructions.

Example 3-1. A rudimentary XML parser

sub is_well_formed {
    my $text = shift;                     # XML text to check

    # match patterns
    my $ident = '[:_A-Za-z][:A-Za-z0-9\-\._]*';   # identifier
    my $optsp = '\s*';                            # optional space
    my $att1 = "$ident$optsp=$optsp\"[^\"]*\"";   # attribute
    my $att2 = "$ident$optsp=$optsp'[^']*'";      # attr. variant
    my $att = "($att1|$att2)";                    # any attribute

    my @elements = ( );                    # stack of open elems

    # loop through the string to pull out XML markup objects
    while( length($text) ) {

        # match an empty element
        if( $text =~ /^&($ident)(\s+$att)*\s*\/>/ ) {
            $text = $';

        # match an element start tag
        } elsif( $text =~ /^&($ident)(\s+$att)*\s*>/ ) {
            push( @elements, $1 );
            $text = $';

        # match an element end tag
        } elsif( $text =~ /^&\/($ident)\s*>/ ) {
            return unless( $1 eq pop( @elements ));
            $text = $';

        # match a comment
        } elsif( $text =~ /^&!--/ ) {
            $text = $';
            # bite off the rest of the comment
            if( $text =~ /-->/ ) {
                $text = $';
                return if( $` =~ /--/ );  # comments can't
                                            # contain '--'
            } else {
                return;
            }

        # match a CDATA section
        } elsif( $text =~ /^&!\[CDATA\[/ ) {
            $text = $';
            # bite off the rest of the comment
            if( $text =~ /\]\]>/ ) {
                $text = $';
            } else {
                return;
            }

        # match a processing instruction
        } elsif( $text =~ m|^&\?$ident\s*[^\?]+\?>| ) {
            $text = $';

        # match extra whitespace
        # (in case there is space outside the root element)
        } elsif( $text =~ m|^\s+| ) {
            $text = $';

        # match character data
        } elsif( $text =~ /(^[^&&>]+)/ ) {
            my $data = $1;
            # make sure the data is inside an element
            return if( $data =~ /\S/ and not( @elements ));
            $text = $';

        # match entity reference
        } elsif( $text =~ /^&$ident;+/ ) {
            $text = $';

        # something unexpected
        } else {
            return;
        }
    }
    return if( @elements );     # the stack should be empty
    return 1;
}

Perl's arrays are so useful partly due to their ability to masquerade as more abstract computer science data constructs.[14] Here, we use a data structure called a stack, which is really just an array that we access with push( ) and pop( ). Items in a stack are last-in, first-out (LIFO), meaning that the last thing put into it will be the first thing to be removed from it. This arrangement is convenient for remembering the names of currently open elements because at any time, the next element to be closed was the last element pushed onto the stack. Whenever we encounter a start tag, it will be pushed onto the stack, and it will be popped from the stack when we find an end tag. To be well-formed, every end tag must match the previous start tag, which is why we need the stack.

[14]The O'Reilly book Mastering Algorithms with Perl by Jon Orwant, Jarkko Hietaniemi, and John Macdonald devotes a chapter to this topic.

The stack represents all the elements along a branch of the XML tree, from the root down to the current element being processed. Elements are processed in the order in which they appear in a document; if you view the document as a tree, it looks like you're going from the root all the way down to the tip of a branch, then back up to another branch, and so on. This is called depth-first order, the canonical way all XML documents are processed.

There are a few places where we deviate from the simple looping scheme to do some extra testing. The code for matching a comment is several steps, since it ends with a three-character delimiter, and we also have to check for an illegal string of dashes "--" inside the comment. The character data matcher, which performs an extra check to see if the stack is empty, is also noteworthy; if the stack is empty, that's an error because nonwhitespace text is not allowed outside of the root element. Here is a short list of well-formedness errors that would cause the parser to return a false result:

  • An identifier in an element or attribute is malformed (examples: "12foo," "-bla," and "..").

  • A nonwhitespace character is found outside of the root element.

  • An element end tag doesn't match the last discovered start tag.

  • An attribute is unquoted or uses a bad combination of quote characters.

  • An empty element is missing a slash character (/) at the end of its tag.

  • An illegal character, such as a lone ampersand (&) or an angle bracket (<), is found in character data.

  • A malformed markup tag (examples: "<fooby<" and "< ?bubba?>") is found.

Try the parser out on some test cases. Probably the simplest complete, well-formed XML document you will ever see is this:

<:-/>

The next document should cause the parser to halt with an error. (Hint: look at the <message> end tag.)

<memo>
  <to>self</to>
  <message>Don't forget to mow the car and wash the
  lawn.<message>
</memo>

Many other kinds of syntax errors could appear in a document, and our program picks up most of them. However, it does miss a few. For example, there should be exactly one root element, but our program will accept more than one:

<root>I am the one, true root!</root>
<root>No, I am!</root>
<root>Uh oh...</root>

Other problems? The parser cannot handle a document type declaration. This structure is sometimes seen at the top of a document that specifies a DTD for validating parsers, and it may also declare some entities. With a specialized syntax of its own, we'd have to write another loop just for the document type declaration.

Our parser's most significant omission is the resolution of entity references. It can check basic entity reference syntax, but doesn't bother to expand the entity and insert it into the text. Why is that bad? Consider that an entity can contain more than just some character data. It can contain any amount of markup, too, from an element to a big, external file. Entities can also contain other entity references, so it might require many passes to resolve one entity reference completely. The parser doesn't even check to see if the entities are declared (it couldn't anyway, since it doesn't know how to read a document type declaration syntax). Clearly, there is a lot of room for errors to creep into a document through entities, right under the nose of our parser. To fix the problems just mentioned, follow these steps:

  1. Add a parsing loop to read in a document type declaration before any other parsing occurs. Any entity declarations would be parsed and stored, so we can resolve entity references later in the document.

  2. Parse the DTD, if the document type declaration mentions one, to read any entity declarations.

  3. In the main loop, resolve all entity references when we come across them. These entities have to be parsed, and there may be entity references within them, too. The process can be rather loopy, with loops inside loops, recursion, or other complex programming stunts.

What started out as a simple parser now has grown into a complex beast. That tells us two things: that the theory of parsing XML is easy to grasp; and that, in practice, it gets complicated very quickly. This exercise was useful because it showed issues involved in parsing XML, but we don't encourage you to write code like this. On the contrary, we expect you to take advantage of the exhaustive work already put into making ready-made parsers. Let's leave the dark ages and walk into the happy land of prepackaged parsers.

Previous | Home | Next
2.12. Transformations | Book Index | 3.2. XML::Parser
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::Parser (Perl and XML)

 Perl & XMLSearch this book

3.2. XML::Parser

Writing a parser requires a lot of work. You can't be sure if you've covered everything without a lot of testing. Unless you're a mutant who loves to write efficient, low-level parser code, your program will probably be slow and resource-intensive. The good news is that a wide variety of free, high quality, and easy-to-use XML parser packages (written by friendly mutants) already exist to help you. People have bashed Perl and XML together for years, and you have a barnful of conveniently pre-invented wheels at your disposal.

Where do Perl programmers go to find ready-made modules to use in their programs? They go to the Comprehensive Perl Archive Network (CPAN), a many-mirrored public resource full of free, open-source Perl code. If you aren't familiar with using CPAN, you must change your isolationist ways and learn to become a programmer of the world. You'll find a multitude of modules authored by folks who have walked the path of Perl and XML before you, and who've chosen to share the tools they've made with the rest of the world.

TIP: Don't think of CPAN as a catalog of ready-made solutions for all specific XML problems. Rather, look at it as a toolbox or a source of building blocks you can assemble and configure to craft a solution. While some modules specialize in popular XML applications like RSS and SOAP, most are more general-purpose. Chances are, you won't find a module that specifically addresses your needs. You'll more likely take one of the general XML modules and adapt it somehow. We'll show that this process is painless and reveal several ways to configure general modules to your particular application.

XML parsers differ from one another in two major ways. First, they differ in their parsing style , which is how the parser works with XML. There are a few different strategies, such as building a data structure or creating an event stream. Another attribute of parsers, called standards-completeness, is a spectrum ranging from ad hoc on one extreme to an exhaustive, standards-based solution on the other. The balance on the latter axis is slowly moving from the eccentric, nonstandard side toward the other end as the Perl community agrees on how to implement major standards like SAX and DOM.

The XML::Parser module is the great-grandpappy of all Perl-based XML processors. It is a multifaceted parser, offering a handful of different parsing styles. On the standards axis, it's closer to ad hoc than standards-compliant; however, being the first efficient XML parser to appear on the Perl horizon, it has a dear place in our hearts and is still very useful. While XML::Parser uses a nonstandard API and has a reputation for getting a bit persnickety over some issues, it works. It parses documents with reasonable speed and flexibility, and as all Perl hackers know, people tend to glom onto the first usable solution that appears on the radar, no matter how ugly it is. Thus, nearly all of the first few years' worth of Perl and XML modules and programs based themselves on XML::Parser.

Since 2001 or so, however, other low-level parsing modules have emerged that base themselves on faster and more standards-compliant core libraries. We'll touch on these modules shortly. However, we'll start out with an examination of XML::Parser, giving a nod to its venerability and functionality.

In the early days of XML, a skilled programmer named James Clark wrote an XML parser library in C and called it Expat.[15] Fast, efficient, and very stable, it became the parser of choice among early adopters of XML. To bring XML into the Perl realm, Larry Wall wrote a low-level API for it and called the module XML::Parser::Expat. Then he built a layer on top of that, XML::Parser, to serve as a general-purpose parser for everybody. Now maintained by Clark Cooper, XML::Parser has served as the foundation of many XML modules.

[15]James Clark is a big name in the XML community. He tirelessly promotes the standard with his free tools and involvement with the W3C. You can see his work at http://www.jclark.com/. Clark is also editor of the XSLT and XPath recommendation documents at http://www.w3.org/.

The C underpinnings are the secret to XML::Parser's success. We've seen how to write a basic parser in Perl. If you apply our previous example to a large XML document, you'll wait a long time before it finishes. Others have written complete XML parsers in Perl that are portable to any system, but you'll find much better performance in a compiled C parser like Expat. Fortunately, as with every other Perl module based on C code (and there are actually lots of these modules because they're not too hard to make, thanks to Perl's standard XS library),[16] it's easy to forget you're driving Expat around when you use XML::Parser.

[16]See man perlxs or Chapter 25 of O'Reilly's Programming Perl, Third Edition for more information.

3.2.1. Example: Well-Formedness Checker Revisited

To show how XML::Parser might be used, let's return to the well-formedness checker problem. It's very easy to create this tool with XML::Parser, as shown in Example 3-2.

Example 3-2. Well-formedness checker using XML::Parser

use XML::Parser;

my $xmlfile = shift @ARGV;              # the file to parse

# initialize parser object and parse the string
my $parser = XML::Parser->new( ErrorContext => 2 );
eval { $parser->parsefile( $xmlfile ); };

# report any error that stopped parsing, or announce success
if( $@ ) {
    $@ =~ s/at \/.*?$//s;               # remove module line number
    print STDERR "\nERROR in '$file':\n$@\n";
} else {
    print STDERR "'$file' is well-formed\n";
}

Here's how this program works. First, we create a new XML::Parser object to do the parsing. Using an object rather than a static function call means that we can configure the parser once and then process multiple files without the overhead of repeatedly recreating the parser. The object retains your settings and keeps the Expat parser routine alive for as long as you want to parse files, and then cleans everything up when you're done.

Next, we call the parsefile( ) method inside an eval block because XML::Parser tends to be a little overzealous when dealing with parse errors. If we didn't use an eval block, our program would die before we had a chance to do any cleanup. We check the variable $@ for content in case there was an error. If there was, we remove the line number of the module at which the parse method "died" and then print out the message.

When initializing the parser object, we set an option ErrorContext => 2. XML::Parser has several options you can set to control parsing. This one is a directive sent straight to the Expat parser that remembers the context in which errors occur and saves two lines before the error. When we print out the error message, it tells us what line the error happened on and prints out the region of text with an arrow pointing to the offending mistake.

Here's an example of our checker choking on a syntactic faux pas (where we decided to name our program xwf as an XML well-formedness checker):

$ xwf ch01.xml

ERROR in 'ch01.xml':

not well-formed (invalid token) at line 66, column 22, byte 2354:

<chapter id="dorothy-in-oz">
<title>Lions, Tigers & Bears</title>
=====================^

Notice how simple it is to set up the parser and get powerful results. What you don't see until you run the program yourself is that it's fast. When you type the command, you get a result in a split second.

You can configure the parser to work in different ways. You don't have to parse a file, for example. Use the method parse( ) to parse a text string instead. Or, you could give it the option NoExpand => 1 to override default entity expansion with your own entity resolver routine. You could use this option to prevent the parser from opening external entities, limiting the scope of its checking.

Although the well-formedness checker is a very useful tool that you certainly want in your XML toolbox if you work with XML files often, it only scratches the surface of what we can do with XML::Parser. We'll see in the next section that a parser's most important role is in shoveling packaged data into your program. How it does this depends on the particular style you select.

3.2.2. Parsing Styles

XML::Parser supports several different styles of parsing to suit various development strategies. The style doesn't change how the parser reads XML. Rather, it changes how it presents the results of parsing. If you need a persistent structure containing the document, you can have it. Or, if you'd prefer to have the parser call a set of routines you write, you can do it that way. You can set the style when you initialize the object by setting the value of style. Here's a quick summary of the available styles:

Debug

This style prints the document to STDOUT, formatted as an outline (deeper elements are indented more). parse( ) doesn't return anything special to your program.

Tree

This style creates a hierarchical, tree-shaped data structure that your program can use for processing. All elements and their data are crystallized in this form, which consists of nested hashes and arrays.

Object

Like tree, this method returns a reference to a hierarchical data structure representing the document. However, instead of using simple data aggregates like hashes and lists, it consists of objects that are specialized to contain XML markup objects.

Subs

This style lets you set up callback functions to handle individual elements. Create a package of routines named after the elements they should handle and tell the parser about this package by using the pkg option. Every time the parser finds a start tag for an element called <fooby>, it will look for the function fooby( ) in your package. When it finds the end tag for the element, it will try to call the function _fooby( ) in your package. The parser will pass critical information like references to content and attributes to the function, so you can do whatever processing you need to do with it.

Stream

Like Subs, you can define callbacks for handling particular XML components, but callbacks are more general than element names. You can write functions called handlers to be called for "events" like the start of an element (any element, not just a particular kind), a set of character data, or a processing instruction. You must register the handler package with either the Handlers option or the setHandlers( ) method.

custom

You can subclass the XML::Parser class with your own object. Doing so is useful for creating a parser-like API for a more specific application. For example, the XML::Parser::PerlSAX module uses this strategy to implement the SAX event processing standard.

Example 3-3 is a program that uses XML::Parser with Style set to Tree. In this mode, the parser reads the whole XML document while building a data structure. When finished, it hands our program a reference to the structure that we can play with.

Example 3-3. An XML tree builder

use XML::Parser;

# initialize parser and read the file
$parser = new XML::Parser( Style => 'Tree' );
my $tree = $parser->parsefile( shift @ARGV );

# serialize the structure
use Data::Dumper;
print Dumper( $tree );

In tree mode, the parsefile( ) method returns a reference to a data structure containing the document, encoded as lists and hashes. We use Data::Dumper, a handy module that serializes data structures, to view the result. Example 3-4 is the datafile.

Example 3-4. An XML datafile

<preferences>
  <font role="console">
    <fname>Courier</name>
    <size>9</size>
  </font>
  <font role="default">
    <fname>Times New Roman</name>
    <size>14</size>
  </font>
  <font role="titles">
    <fname>Helvetica</name>
    <size>10</size>
  </font>
</preferences>

With this datafile, the program produces the following output (condensed and indented to be easier to read):

$tree = [
          'preferences', [
            {}, 0, '\n',
            'font', [
              { 'role' => 'console' }, 0, '\n',
              'size', [ {}, 0, '9' ], 0, '\n',
              'fname', [ {}, 0, 'Courier' ], 0, '\n'
            ], 0, '\n',
            'font', [
              { 'role' => 'default' }, 0, '\n',
              'fname', [ {}, 0, 'Times New Roman' ], 0, '\n',
              'size', [ {}, 0, '14' ], 0, '\n'
            ], 0, '\n',
            'font', [
               { 'role' => 'titles' }, 0, '\n',
               'size', [ {}, 0, '10' ], 0, '\n',
               'fname', [ {}, 0, 'Helvetica' ], 0, '\n',
            ], 0, '\n',
          ]
        ];

It's a lot easier to write code that dissects the above structure than to write a parser of your own. We know, because the parser returned a data structure instead of dying mid-parse, that the document was 100 percent well-formed XML. In Chapter 4, "Event Streams", we will use the Stream mode of XML::Parser, and in Chapter 6, "Tree Processing", we'll talk more about trees and objects.

Previous | Home | Next
3. XML Basics: Reading and Writing | Book Index | 3.3. Stream-Based Versus Tree-Based Processing
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Stream-Based Versus Tree-Based Processing (Perl and XML)

 Perl & XMLSearch this book

3.3. Stream-Based Versus Tree-Based Processing

Remember the Perl mantra, "There's more than one way to do it"? It is also true when working with XML. Depending on how you want to work and what kind of resources you have, many options are available. One developer may prefer a low-maintenance parsing job and is prepared to be loose and sloppy with memory to get it. Another will need to squeeze out faster and leaner performance at the expense of more complex code. XML processing tasks vary widely, so you should be free to choose the shortest path to a solution.

There are a lot of different XML processing strategies. Most fall into two categories: stream-based and tree-based. With the stream-based strategy, the parser continuously alerts a program to patterns in the XML. The parser functions like a pipeline, taking XML markup on one end and pumping out processed nuggets of data to your program. We call this pipeline an event stream because each chunk of data sent to the program signals something new and interesting in the XML stream. For example, the beginning of a new element is a significant event. So is the discovery of a processing instruction in the markup. With each update, your program does something new -- perhaps translating the data and sending it to another place, testing it for some specific content, or sticking it onto a growing heap of data.

With the tree-based strategy, the parser keeps the data to itself until the very end, when it presents a complete model of the document to your program. Instead of a pipeline, it's like a camera that takes a picture and transmits the replica to you. The model is usually in a much more convenient state than raw XML. For example, nested elements may be represented in native Perl structures like lists or hashes, as we saw in an earlier example. Even more useful are trees of blessed objects with methods that help navigate the structure from one place to another. The whole point to this strategy is that your program can pull out any data it needs, in any order.

Why would you prefer one over the other? Each has strong and weak points. Event streams are fast and often have a much slimmer memory footprint, but at the expense of greater code complexity and impermanent data. Tree building, on the other hand, lets the data stick around for as long as you need it, and your code is usually simple because you don't need special tricks to do things like backwards searching. However, trees wither when it comes to economical use of processor time and memory.

All of this is relative, of course. Small documents don't cause much hardship to a typical computer, especially since CPU cycles and megabytes are getting cheaper every day. Maybe the convenience of a persistent data structure will outweigh any drawbacks. On the other hand, when working with Godzilla-sized documents like books, or huge numbers of documents all at once, you'll definitely notice the crunch. Then the agility of event stream processors will start to look better. It's impossible to give you any hard-and-fast rules, so we'll leave the decision up to you.

An interesting thing to note about the stream-based and tree-based strategies is that one is the basis for the other. That's right, an event stream drives the process of building a tree data structure. Thus, most low-level parsers are event streams because you can always write a tree building layer on top. This is how XML::Parser and most other parsers work.

In a related, more recent, and very cool development, XML event streams can also turn any kind of document into some form of XML by writing stream-based parsers that generate XML events from whatever data structures lurk in that document type.

There's a lot more to say about event streams and tree builders -- so much, in fact, that we've devoted two whole chapters to the topics. Chapter 4, "Event Streams" takes a deep plunge into the theory behind event streams with lots of examples for making useful programs out of them. Chapter 6, "Tree Processing" takes you deeper into the forest with lots of tree-based examples. After that, Chapter 8, "Beyond Trees: XPath, XSLT, and More" shows you unusual hybrids that provide the best of both worlds.

Previous | Home | Next
3.2. XML::Parser | Book Index | 3.4. Putting Parsers to Work
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Putting Parsers to Work (Perl and XML)

 Perl & XMLSearch this book

3.4. Putting Parsers to Work

Enough tinkering with the parser's internal details. We want to see what you can do with the stuff you get from parsers. We've already seen an example of a complete, parser-built tree structure in Example 3-3, so let's do something with the other type. We'll take an XML event stream and make it drive processing by plugging it into some code to handle the events. It may not be the most useful tool in the world, but it will serve well enough to show you how real-world XML processing programs are written.

XML::Parser (with Expat running underneath) is at the input end of our program. Expat subscribes to the event-based parsing school we described earlier. Rather than loading your whole XML document into memory and then turning around to see what it hath wrought, it stops every time it encounters a discrete chunk of data or markup, such as an angle-bracketed tag or a literal string inside an element. It then checks to see if our program wants to react to it in any way.

Your first responsibility is to give the parser an interface to the pertinent bits of code that handle events. Each type of event is handled by a different subroutine, or handler. We register our handlers with the parser by setting the Handlers option at initialization time. Example 3-5 shows the entire process.

Example 3-5. A stream-based XML processor

use XML::Parser;

# initialize the parser
my $parser = XML::Parser->new( Handlers =>
                                     {
                                      Start=>\&handle_start,
                                      End=>\&handle_end,
                                     });
$parser->parsefile( shift @ARGV );

my @element_stack;                # remember which elements are open

# process a start-of-element event: print message about element
#
sub handle_start {
    my( $expat, $element, %attrs ) = @_;

    # ask the expat object about our position
    my $line = $expat->current_line;

    print "I see an $element element starting on line $line!\n";

    # remember this element and its starting position by pushing a
    # little hash onto the element stack
    push( @element_stack, { element=>$element, line=>$line });

    if( %attrs ) {
        print "It has these attributes:\n";
        while( my( $key, $value ) = each( %attrs )) {
            print "\t$key => $value\n";
        }
    }
}

# process an end-of-element event
#
sub handle_end {
    my( $expat, $element ) = @_;

    # We'll just pop from the element stack with blind faith that
    # we'll get the correct closing element, unlike what our
    # homebrewed well-formedness did, since XML::Parser will scream
    # bloody murder if any well-formedness errors creep in.
    my $element_record = pop( @element_stack );
    print "I see that $element element that started on line ",
          $$element_record{ line }, " is closing now.\n";
}

It's easy to see how this process works. We've written two handler subroutines called handle_start( ) and handle_end( ) and registered each with a particular event in the call to new( ). When we call parse( ), the parser knows it has handlers for a start-of-element event and an end-of-element event. Every time the parser trips over an element start tag, it calls the first handler and gives it information about that element (element name and attributes). Similarly, any end tag it encounters leads to a call of the other handler with similar element-specific information.

Note that the parser also gives each handler a reference called $expat. This is a reference to the XML::Parser::Expat object, a low-level interface to Expat. It has access to interesting information that might be useful to a program, such as line numbers and element depth. We've taken advantage of this fact, using the line number to dazzle users with our amazing powers of document analysis.

Want to see it run? Here's how the output looks after processing the customer database document from Example 1-1:

I see a spam-document element starting on line 1!
It has these attributes:
        version => 3.5
        timestamp => 2002-05-13 15:33:45
I see a customer element starting on line 3!
I see a first-name element starting on line 4!
I see that the first-name element that started on line 4 is closing now.
I see a surname element starting on line 5!
I see that the surname element that started on line 5 is closing now.
I see a address element starting on line 6!
I see a street element starting on line 7!
I see that the street element that started on line 7 is closing now.
I see a city element starting on line 8!
I see that the city element that started on line 8 is closing now.
I see a state element starting on line 9!
I see that the state element that started on line 9 is closing now.
I see a zip element starting on line 10!
I see that the zip element that started on line 10 is closing now.
I see that the address element that started on line 6 is closing now.
I see a email element starting on line 12!
I see that the email element that started on line 12 is closing now.
I see a age element starting on line 13!
I see that the age element that started on line 13 is closing now.
I see that the customer element that started on line 3 is closing now.
  [... snipping other customers for brevity's sake ...]
I see that the spam-document element that started on line 1 is closing now.

Here we used the element stack again. We didn't actually need to store the elements' names ourselves; one of the methods you can call on the XML::Parser::Expat object returns the current context list, a newest-to-oldest ordering of all elements our parser has probed into. However, a stack proved to be a useful way to store additional information like line numbers. It shows off the fact that you can let events build up structures of arbitrary complexity -- the "memory" of the document's past.

There are many more event types than we handle here. We don't do anything with character data, comments, or processing instructions, for example. However, for the purpose of this example, we don't need to go into those event types. We'll have more exhaustive examples of event processing in the next chapter, anyway.

Before we close the topic of event processing, we want to mention one thing: the Simple API for XML processing, more commonly known as SAX. It's very similar to the event processing model we've seen so far, but the difference is that it's a W3C-supported standard. Being a W3C-supported standard means that it has a standardized, canonical set of events. How these events should be presented for processing is also standardized. The cool thing about it is that with a standard interface, you can hook up different program components like Legos and it will all work. If you don't like one parser, just plug in another (and sophisticated tools like the XML::SAX module family can even help you pick a parser based on the features you need). Get your XML data from a database, a file, or your mother's shopping list; it shouldn't matter where it comes from. SAX is very exciting for the Perl community because we've long been criticized for our lack of standards compliance and general barbarism. Now we can be criticized for only one of those things. You can expect a nice, thorough discussion on SAX (specifically, PerlSAX, our beloved language's mutation thereof) in Chapter 5, "SAX".

Previous | Home | Next
3.3. Stream-Based Versus Tree-Based Processing | Book Index | 3.5. XML::LibXML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::LibXML (Perl and XML)

 Perl & XMLSearch this book

3.5. XML::LibXML

XML::LibXML, like XML::Parser, is an interface to a library written in C. Called libxml2, it's part of the GNOME project.[17] Unlike XML::Parser, this new parser supports a major standard for XML tree processing known as the Document Object Model ( DOM).

[17]For downloads and documentation, see http://www.libxml.org/.

DOM is another much-ballyhooed XML standard. It does for tree processing what SAX does for event streams. If you have your heart set on climbing trees in your program and you think there's a likelihood that it might be reused or applied to different data sources, you're better off using something standard and interchangeable. Again, we're happy to delve into DOM in a future chapter and get you thinking in standards-complaint ways. That topic is coming up in Chapter 7, "DOM".

Now we want to show you an example of another parser in action. We'd be remiss if we focused on just one kind of parser when so many are out there. Again, we'll show you a basic example, nothing fancy, just to show you how to invoke the parser and tame its power. Let's write another document analysis tool like we did in Example 3-5, this time printing a frequency distribution of elements in a document.

Example 3-6 shows the program. It's a vanilla parser run because we haven't set any options yet. Essentially, the parser parses the filehandle and returns a DOM object, which is nothing more than a tree structure of well-designed objects. Our program finds the document element, and then traverses the entire tree one element at a time, all the while updating the hash of frequency counters.

Example 3-6. A frequency distribution program

use XML::LibXML;
use IO::Handle;

# initialize the parser
my $parser = new XML::LibXML;

# open a filehandle and parse
my $fh = new IO::Handle;
if( $fh->fdopen( fileno( STDIN ), "r" )) {
    my $doc = $parser->parse_fh( $fh );
    my %dist;
    &proc_node( $doc->getDocumentElement, \%dist );
    foreach my $item ( sort keys %dist ) {
        print "$item: ", $dist{ $item }, "\n";
    }
    $fh->close;
}

# process an XML tree node: if it's an element, update the
# distribution list and process all its children
#
sub proc_node {
    my( $node, $dist ) = @_;
    return unless( $node->nodeType eq &XML_ELEMENT_NODE );
    $dist->{ $node->nodeName } ++;
    foreach my $child ( $node->getChildnodes ) {
        &proc_node( $child, $dist );
    }
}

Note that instead of using a simple path to a file, we use a filehandle object of the IO::Handle class. Perl filehandles, as you probably know, are magic and subtle beasties, capable of passing into your code characters from a wide variety of sources, including files on disk, open network sockets, keyboard input, databases, and just about everything else capable of outputting data. Once you define a filehandle's source, it gives you the same interface for reading from it as does every other filehandle. This dovetails nicely with our XML-based ideology, where we want code to be as flexible and reusable as possible. After all, XML doesn't care where it comes from, so why should we pigeonhole it with one source type?

The parser object returns a document object after parsing. This object has a method that returns a reference to the document element -- the element at the very root of the whole tree. We take this reference and feed it to a recursive subroutine, proc_node( ), which happily munches on elements and scribbles into a hash variable every time it sees an element. Recursion is an efficient way to write programs that process XML because the structure of documents is somewhat fractal: the same rules for elements apply at any depth or position in the document, including the root element that represents the entire document (modulo its prologue). Note the "node type" check, which distinguishes between elements and other parts of a document (such as pieces of text or processing instructions).

For every element the routine looks at, it has to call the object's getChildnodes( ) method to continue processing on its children. This call is an essential difference between stream-based and tree-based methodologies. Instead of having an event stream take the steering wheel of our program and push data at it, thus calling subroutines and codeblocks in a (somewhat) unpredictable order, our program now has the responsibility of navigating through the document under its own power. Traditionally, we start at the root element and go downward, processing children in order from first to last. However, because we, not the parser, are in control now, we can scan through the document in any way we want. We could go backwards, we could scan just a part of the document, we could jump around, making multiple passes though the tree -- the sky's the limit. Here's the result from processing a small chapter coded in DocBook XML:

$ xfreq < ch03.xml
chapter: 1
citetitle: 2
firstterm: 16
footnote: 6
foreignphrase: 2
function: 10
itemizedlist: 2
listitem: 21
literal: 29
note: 1
orderedlist: 1
para: 77
programlisting: 9
replaceable: 1
screen: 1
section: 6
sgmltag: 8
simplesect: 1
systemitem: 2
term: 6
title: 7
variablelist: 1
varlistentry: 6
xref: 2

The result shows only a few lines of code, but it sure does a lot of work. Again, thanks to the C library underneath, it's quite speedy.

Previous | Home | Next
3.4. Putting Parsers to Work | Book Index | 3.6. XML::XPath
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::XPath (Perl and XML)

 Perl & XMLSearch this book

3.6. XML::XPath

We've seen examples of parsers that dutifully deliver the entire document to you. Often, though, you don't need the whole thing. When you query a database, you're usually looking for only a single record. When you crack open a telephone book, you're not going to sit down and read the whole thing. There is obviously a need for some mechanism of extracting a specific piece of information from a vast document. Look no further than XPath.

XPath is a recommendation from the folks who brought you XML.[18] It's a grammar for writing expressions that pinpoint specific pieces of documents. Think of it as an addressing scheme. Although we'll save the nitty-gritty of XPath wrangling for Chapter 8, "Beyond Trees: XPath, XSLT, and More", we can tantalize you by revealing that it works much like a mix of regular expressions with Unix-style file paths. Not surprisingly, this makes it an attractive feature to add to parsers.

[18]Check out the specification at http://www.w3.org/TR/xpath/.

Matt Sergeant's XML::XPath module is a solid implementation, built on the foundation of XML::Parser. Given an XPath expression, it returns a list of all document parts that match the description. It's an incredibly simple way to perform some powerful search and retrieval work.

For instance, suppose we have an address book encoded in XML in this basic form:

<contacts>
  <entry>
    <name>Bob Snob</name>
    <street>123 Platypus Lane</street>
    <city>Burgopolis</city>
    <state>FL</state>
    <zip>12345</zip>
  </entry>
 <!--More entries go here-->
</contacts>

Suppose you want to extract all the zip codes from the file and compile them into a list. Example 3-7 shows how you could do it with XPath.

Example 3-7. Zip code extractor

use XML::XPath;

my $file = 'customers.xml';
my $xp = XML::XPath->new(filename=>$file);

# An XML::XPath nodeset is an object which contains the result of
# smacking an XML document with an XPath expression; we'll do just
# this, and then query the nodeset to see what we get.
my $nodeset = $xp->find('//zip');

my @zipcodes;                   # Where we'll put our results
if (my @nodelist = $nodeset->get_nodelist) {
  # We found some zip elements! Each node is an object of the class
  # XML::XPath::Node::Element, so I'll use that class's 'string_value'
  # method to extract its pertinent text, and throw the result for all
  # the nodes into our array.
  @zipcodes = map($_->string_value, @nodelist);

  # Now sort and prepare for output
  @zipcodes = sort(@zipcodes);
  local $" = "\n";
  print "I found these zipcodes:\n@zipcodes\n";
} else {
  print "The file $file didn't have any 'zip' elements in it!\n";
}

Run the program on a document with three entries and we'll get something like this:

I found these zipcodes:
03642
12333
82649

This module also shows an example of tree-based parsing, by the way, as its parser loads the whole document into an object tree of its own design and then allows the user to selectively interact with parts of it via XPath expressions. This example is just a sample of what you can do with advanced tree processing modules. You'll see more of these modules in Chapter 8, "Beyond Trees: XPath, XSLT, and More".

XML::LibXML's element objects support a findnodes( ) method that works much like XML::XPath's, using the invoking Element object as the current context and returning a list of objects that match the query. We'll play with this functionality later in Chapter 10, "Coding Strategies".

Previous | Home | Next
3.5. XML::LibXML | Book Index | 3.7. Document Validation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Document Validation (Perl and XML)

 Perl & XMLSearch this book

3.7. Document Validation

Being well-formed is a minimal requirement for XML everywhere. However, XML processors have to accept a lot on blind faith. If we try to build a document to meet some specific XML application's specifications, it doesn't do us any good if a content generator slips in a strange element we've never seen before and the parser lets it go by with nary a whimper. Luckily, a higher level of quality control is available to us when we need to check for things like that. It's called document validation.

Validation is a sophisticated way of comparing a document instance against a template or grammar specification. It can restrict the number and type of elements a document can use and control where they go. It can even regulate the patterns of character data in any element or attribute. A validating parser tells you whether a document is valid or not, when given a DTD or schema to check against.

Remember that you don't need to validate every XML document that passes over your desk. DTDs and other validation schemes shine when working with specific XML-based markup languages (such as XHTML for web pages, MathML for equations, or CaveML for spelunking), which have strict rules about which elements and attributes go where (because having an automated way to draw attention to something fishy in the document structure becomes a feature).

However, validation usually isn't crucial when you use Perl and XML to perform a less specific task, such as tossing together XML documents on the fly based on some other, less sane data format, or when ripping apart and analyzing existing XML documents.

Basically, if you feel that validation is a needless step for the job at hand, you're probably right. However, if you knowingly generate or modify some flavor of XML that needs to stick to a defined standard, then taking the extra step or three necessary to perform document validation is probably wise. Your toolbox, naturally, gives you lots of ways to do this. Read on.

3.7.1. DTDs

Document type descriptions (DTDs) are documents written in a special markup language defined in the XML specification, though they themselves are not XML. Everything within these documents is a declaration starting with a <! delimiter and comes in four flavors: elements, attributes, entities, and notations.

Example 3-8 is a very simple DTD.

Example 3-8. A wee little DTD

<!ELEMENT memo (to, from, message)>
<!ATTLIST memo priority (urgent|normal|info) 'normal'>
<!ENTITY % text-only "(#PCDATA)*">
<!ELEMENT to %text-only;>
<!ELEMENT from %text-only;>
<!ELEMENT message (#PCDATA | emphasis)*>
<!ELEMENT emphasis %text-only;>
<!ENTITY myname "Bartholomus Chiggin McNugget">

This DTD declares five elements, an attribute for the <memo> element, a parameter entity to make other declarations cleaner, and an entity that can be used inside a document instance. Based on this information, a validating parser can reject or approve a document. The following document would pass muster:

<!DOCTYPE memo SYSTEM "/dtdstuff/memo.dtd">
<memo priority="info">
  <to>Sara Bellum</to>
  <from>&myname;</from>
  <message>Stop reading memos and get back to work!</message>
</memo>

If you removed the <to> element from the document, it would suddenly become invalid. A well-formedness checker wouldn't give a hoot about missing elements. Thus, you see the value of validation.

Because DTDs are so easy to parse, some general XML processors include the ability to validate the documents they parse against DTDs. XML::LibXML is one such parser. A very simple validating parser is shown in Example 3-9.

Example 3-9. A validating parser

use XML::LibXML;
use IO::Handle;

# initialize the parser
my $parser = new XML::LibXML;

# open a filehandle and parse
my $fh = new IO::Handle;
if( $fh->fdopen( fileno( STDIN ), "r" )) {
    my $doc = $parser->parse_fh( $fh );
    if( $doc and $doc->is_valid ) {
        print "Yup, it's valid.\n";
    } else {
        print "Yikes! Validity error.\n";
    }
    $fh->close;
}

This parser would be simple to add to any program that requires valid input documents. Unfortunately, it doesn't give any information about what specific problem makes it invalid (e.g., an element in an improper place), so you wouldn't want to use it as a general-purpose validity checking tool.[19] T. J. Mather's XML::Checker is a better module for reporting specific validation errors.

[19]The authors prefer to use a command-line tool called nsgmls available from http://www.jclark.com/. Public web sites, such as http://www.stg.brown.edu/service/xmlvalid/, can also validate arbitrary documents. Note that, in these cases, the XML document must have a DOCTYPE declaration, whose system identifier (if it has one) must contain a resolvable URL and not a path on your local system.

3.7.2. Schemas

DTDs have limitations; they aren't able to check what kind of character data is in an element and if it matches a particular pattern. What if you wanted a parser to tell you if a <date> element has the wrong format for a date, or if it contains a street address by mistake? For that, you need a solution such as XML Schema. XML Schema is a second generation of DTD and brings more power and flexibility to validation.

As noted in Chapter 2, "An XML Recap", XML Schema enjoys the dubious distinction among the XML-related W3C specification family for being the most controversial schema (at least among hackers). Many people like the concept of schemas, but many don't approve of the XML Schema implementation, which is seen as too cumbersome or constraining to be used effectively.

Alternatives to XML Schema include OASIS-Open's RelaxNG (http://www.oasis-open.org/committees/relax-ng/) and Rick Jelliffe's Schematron (http://www.ascc.net/xml/resource/schematron/schematron.html). Like XML Schema, these specifications detail XML-based languages used to describe other XML-based languages and let a program that knows how to speak that schema use it to validate other XML documents. We find Schematron particularly interesting because it has had a Perl module attached to it for a while (in the form of Kip Hampton's XML::Schematron family).

Schematron is especially interesting to many Perl and XML hackers because it builds on existing popular XML technologies that already have venerable Perl implementations. Schematron defines a very simple language with which you list and group together assertions of what things should look like based on XPath expressions. Instead of a forward-looking grammar that must list and define everything that can possibly appear in the document, you can choose to validate a fraction of it. You can also choose to have elements and attributes validate based on conditions involving anything anywhere else in the document (wherever an XPath expression can reach). In practice, a Schematron document looks and feels like an XSLT stylesheet, and with good reason: it's intended to be fully implementable by way of XSLT. In fact, two of the XML::Schematron Perl modules work by first transforming the user-specified schema document into an XSLT sheet, which it then simply passes through an XSLT processor.

Schematron lacks any kind of built-in data typing, so you can't, for example, do a one-word check to insist that an attribute conforms to the W3C date format. You can, however, have your Perl program make a separate step using any method you'd like (perhaps through the XML::XPath module) to come through date attributes and run a good old Perl regular expression on them. Also note that no schema language will ever provide a way to query an element's content against a database, or perform any other action outside the realm of the document. This is where mixing Perl and schemas can come in very handy.

Previous | Home | Next
3.6. XML::XPath | Book Index | 3.8. XML::Writer
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::Writer (Perl and XML)

 Perl & XMLSearch this book

3.8. XML::Writer

Compared to all we've had to deal with in this chapter so far, writing XML will be a breeze. It's easier to write it because now the shoe's on the other foot: your program has a data structure over which it has had complete control and knows everything about, so it doesn't need to prepare for every contingency that it might encounter when processing input.

There's nothing particularly difficult about generating XML. You know about elements with start and end tags, their attributes, and so on. It's just tedious to write an XML output method that remembers to cross all the t's and dot all the i's. Does it put a space between every attribute? Does it close open elements? Does it put that slash at the end of empty elements? You don't want to have to think about these things when you're writing more important code. Others have written modules to take care of these serialization details for you.

David Megginson's XML::Writer is a fine example of an abstract XML generation interface. It comes with a handful of very simple methods for building any XML document. Just create a writer object and call its methods to crank out a stream of XML. Table 3-1 lists some of these methods.

Table 3-1. XML::Writer methods

Name | Function
end( ) | Close the document and perform simple well-formedness checking (e.g., make sure that there is one root element and that every start tag has an associated end tag). If the option UNSAFE is set, however, most well-formedness checking is skipped.
xmlDecl([$endoding, $standalone]) | Add an XML Declaration at the top of the document. The version is hard-wired as "1.0".
doctype($name, [$publicId, $systemId]) | Add a document type declaration at the top of the document.
comment($text) | Write an XML comment.
pi($target [, $data]) | Output a processing instruction.
startTag($name [, $aname1 => $value1, ...]) | Create an element start tag. The first argument is the element name, which is followed by attribute name-value pairs.
emptyTag($name [, $aname1 => $value1, ...]) | Set up an empty element tag. The arguments are the same as for the startTag( ) method.
endTag([$name]) | Create an element end tag. Leave out the argument to have it close the currently open element automatically.
dataElement($name, $data [, $aname1 => $value1, ...]) | Print an element that contains only character data. This element includes the start tag, the data, and the end tag.
characters($data) | Output a parcel of character data.

Using these routines, we can build a complete XML document. The program in Example 3-10, for example, creates a basic HTML file.

Example 3-10. HTML generator

use IO;
my $output = new IO::File(">output.xml");

use XML::Writer;
my $writer = new XML::Writer( OUTPUT => $output );

$writer->xmlDecl( 'UTF-8' );
$writer->doctype( 'html' );
$writer->comment( 'My happy little HTML page' );
$writer->pi( 'foo', 'bar' );
$writer->startTag( 'html' );
$writer->startTag( 'body' );
$writer->startTag( 'h1' );
$writer->startTag( 'font', 'color' => 'green' );
$writer->characters( "<Hello World!>" );
$writer->endTag( );
$writer->endTag( );
$writer->dataElement( "p", "Nice to see you." );
$writer->endTag( );
$writer->endTag( );
$writer->end( );

This example outputs the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<!-- My happy little HTML page -->
<?foo bar?>
<html><body><h1><font color="green">&lt;Hello World!&gt;</font></h1><p>Nice to see you.</p></body></html>

Some nice conveniences are built into this module. For example, it automatically takes care of illegal characters like the ampersand (&) by turning them into the appropriate entity references. Quoting of entity values is automatic, too. At any time during the document-building process, you can check the context you're in with predicate methods like within_element('foo'), which tells you if an element named 'foo' is open.

By default, the module outputs a document with all the tags run together. You might prefer to insert whitespace in some places to make the XML more readable. If you set the option NEWLINES to true, then it will insert newline characters after element tags. If you set DATA_MODE, a similar effect will be achieved, and you can combine DATA_MODE with DATA_INDENT to automatically indent lines in proportion to depth in the document for a nicely formatted document.

The nice thing about XML is that it can be used to organize just about any kind of textual data. With XML::Writer, you can quickly turn a pile of information into a tightly regimented document. For example, you can turn a directory listing into a hierarchical database like the program in Example 3-11.

Example 3-11. Directory mapper

use XML::Writer;
my $wr = new XML::Writer( DATA_MODE => 'true', DATA_INDENT => 2 );
&as_xml( shift @ARGV );
$wr->end;

# recursively map directory information into XML
#
sub as_xml {
    my $path = shift;
    return unless( -e $path );

    # if this is a directory, create an element and
    # stuff it full of items
    if( -d $path ) {
        $wr->startTag( 'directory', name => $path );

        # Load the names of all things in this
        # directory into an array
        my @contents = ( );
        opendir( DIR, $path );
        while( my $item = readdir( DIR )) {
            next if( $item eq '.' or $item eq '..' );
            push( @contents, $item );
        }
        closedir( DIR );

        # recurse on items in the directory
        foreach my $item ( @contents ) {
            &as_xml( "$path/$item" );
        }

        $wr->endTag( 'directory' );

    # We'll lazily call anything that's not a directory a file.
    } else {
        $wr->emptyTag( 'file', name => $path );
    }
}

Here's how the example looks when run on a directory (note the use of DATA_MODE and DATA_INDENT to improve readability):

$ ~/bin/dir /home/eray/xtools/XML-DOM-1.25

<directory name="/home/eray/xtools/XML-DOM-1.25">
  <directory name="/home/eray/xtools/XML-DOM-1.25/t">
    <file name="/home/eray/xtools/XML-DOM-1.25/t/attr.t" />
    <file name="/home/eray/xtools/XML-DOM-1.25/t/minus.t" />
    <file name="/home/eray/xtools/XML-DOM-1.25/t/example.t" />
    <file name="/home/eray/xtools/XML-DOM-1.25/t/print.t" />
    <file name="/home/eray/xtools/XML-DOM-1.25/t/cdata.t" />
    <file name="/home/eray/xtools/XML-DOM-1.25/t/astress.t" />
    <file name="/home/eray/xtools/XML-DOM-1.25/t/modify.t" />
  </directory>
  <file name="/home/eray/xtools/XML-DOM-1.25/DOM.gif" />
  <directory name="/home/eray/xtools/XML-DOM-1.25/samples">
    <file
    name="/home/eray/xtools/XML-DOM-1.25/samples/REC-xml-19980210.xml"
    />
  </directory>
  <file name="/home/eray/xtools/XML-DOM-1.25/MANIFEST" />
  <file name="/home/eray/xtools/XML-DOM-1.25/Makefile.PL" />
  <file name="/home/eray/xtools/XML-DOM-1.25/Changes" />
  <file name="/home/eray/xtools/XML-DOM-1.25/CheckAncestors.pm" />
  <file name="/home/eray/xtools/XML-DOM-1.25/CmpDOM.pm" />

We've seen XML::Writer used step by step and in a recursive context. You could also use it conveniently inside an object tree structure, where each XML object type has its own "to-string" method making the appropriate calls to the writer object. XML::Writer is extremely flexible and useful.

3.8.1. Other Methods of Output

Remember that many parser modules have their own ways to turn their current content into simple, pretty strings of XML. XML::LibXML, for example, lets you call a toString( ) method on the document or any element object within it. Consequently, more specific processor classes that subclass from this module or otherwise make internal use of it often make the same method available in their own APIs and pass end user calls to it to the underlying parser object. Consult the documentation of your favorite processor to see if it supports this or a similar feature.

Finally, sometimes all you really need is Perl's print function. While it lives at a lower level than tools like XML::Writer, ignorant of XML-specific rules and regulations, it gives you a finer degree of control over the process of turning memory structures into text worthy of throwing at filehandles. If you're doing especially tricky work, falling back to print may be a relief, and indeed some of the stunts we pull in Chapter 10, "Coding Strategies" use print. Just don't forget to escape those naughty < and & characters with their respective entity references, as shown in Table 2-1, or be generous with CDATA sections.

Previous | Home | Next
3.7. Document Validation | Book Index | 3.9. Character Sets and Encodings
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Character Sets and Encodings (Perl and XML)

 Perl & XMLSearch this book

3.9. Character Sets and Encodings

No matter how you choose to manage your program's output, you must keep in mind the concept of character encoding -- the protocol your output XML document uses to represent the various symbols of its language, be they an alphabet of letters or a catalog of ideographs and diacritical marks. Character encoding may represent the trickiest part of XML-slinging, perhaps especially so for programmers in Western Europe and the Americas, most of whom have not explored the universe of possible encodings beyond the 128 characters of ASCII.

While it's technically legal for an XML document's encoding declaration to contain the name of any text encoding scheme, the only ones that XML processors are, according to spec, required to understand are UTF-8 and UTF-16. UTF-8 and UTF-16 are two flavors of Unicode, a recent and powerful character encoding architecture that embraces every funny little squiggle a person might care to make.

In this section, we conspire with Perl and XML to nudge you gently into thinking about Unicode, if you're not pondering it already. While you can do everything described in this book by using the legacy encoding of your choice, you'll find, as time passes, that you're swimming against the current.

3.9.1. Unicode, Perl, and XML

Unicode has crept in as the digital age's way of uniting the thousands of different writing systems that have paid the salaries of monks and linguists for centuries. Of course, if you program in an environment where non-ASCII characters are found in abundance, you're probably already familiar with it. However, even then, much of your text processing work might be restricted to low-bit Latin alphanumerics, simply because that's been the character set of choice -- of fiat, really -- for the Internet. Unicode hopes to change this trend, Perl hopes to help, and sneaky little XML is already doing so.

As any Unicode-evangelizing document will tell you,[20] Unicode is great for internationalizing code. It lets programmers come up with localization solutions without the additional worry of juggling different character architectures.

[20]These documents include Chapter 15 of O'Reilly's Programming Perl, Third Edition and the FAQ that the Unicode consortium hosts at http://unicode.org/unicode/faq/.

However, Unicode's importance increases by an order of magnitude when you introduce the question of data representation. The languages that a given program's users (or programmers) might prefer is one thing, but as computing becomes more ubiquitous, it touches more people's lives in more ways every day, and some of these people speak Kurku. By understanding the basics of Unicode, you can see how it can help to transparently keep all the data you'll ever work with, no matter the script, in one architecture.

3.9.2. Unicode Encodings

We are careful to separate the words "architecture" and "encoding" because Unicode actually represents one of the former that contains several of the latter.

In Unicode, every discrete squiggle that's gained official recognition, from A to [image] to [image], has its own code point -- a unique positive integer that serves as its address in the whole map of Unicode. For example, the first letter of the Latin alphabet, capitalized, lives at the hexadecimal address 0x0041 (as it does in ASCII and friends), and the other two symbols, the lowercase Greek alpha and the smileyface, are found in 0x03B1 and 0x263A, respectively. A character can be constructed from any one of these code points, or by combining several of them. Many code points are dedicated to holding the various diacritical marks, such as accents and radicals, that many scripts use in conjunction with base alphabetical or ideographic glyphs.

These addresses, as well as those of the tens of thousands (and, in time, hundreds of thousands) of other glyphs on the map, remain true across Unicode's encodings. The only difference lies in the way these numbers are encoded in the ones and zeros that make up the document at its lowest level.

Unicode officially supports three types of encoding, all named UTF (short for Unicode Transformation Format), followed by a number representing the smallest bit-size any character might take. The encodings are UTF-8, UTF-16, and UTF-32. UTF-8 is the most flexible of all, and is therefore the one that Perl has adopted.

3.9.2.1. UTF-8

The UTF-8 encoding, arguably the most Perlish in its impish trickery, is also the most efficient since it's the only one that can pack characters into single bytes. For that reason, UTF-8 is the default encoding for XML documents: if XML documents specify no encoding in their declarations, then processors should assume that they use UTF-8.

Each character appearing within a document encoded with UTF-8 uses as many bytes as it has to in order to represent that character's code point, up to a maximum of six bytes. Thus, the character A, with the itty-bitty address of 0x41, gets one byte to represent it, while our friend [image] lives way up the street in one of Unicode's blocks of miscellaneous doohickeys, with the address 0x263A. It takes three bytes for itself -- two for the character's code point number and one that signals to text processors that there are, in fact, multiple bytes to this character. Several centuries from now, after Earth begrudgingly joins the Galactic Friendship Union and we find ourselves needing to encode the characters from countless off-planet civilizations, bytes four through six will come in quite handy.

3.9.2.2. UTF-16

The UTF-16 encoding uses a full two bytes to represent the character in question, even if its ordinal is small enough to fit into one (which is how UTF-8 would handle it). If, on the other hand, the character is rare enough to have a very high ordinal, then it gets an additional two bytes tacked onto it (called a surrogate pair), bringing that one character's total length to four bytes.

TIP: Because Unicode 2.0 used a 16-bits-per-character style as its sole supported encoding, many people, and the programs they write, talk about the "Unicode encoding" when they really mean Unicode UTF-16. Even new applications' "Save As..." dialog boxes sometimes offer "Unicode" and "UTF-8" as separate choices, even though these labels don't make much sense in Unicode 3.2 terminology.

3.9.2.3. UTF-32

UTF-32 works a lot like UTF-16, but eliminates any question of variable character size by declaring that every invoked Unicode-mapped glyph shall occupy exactly four bytes. Because of its maximum maximosity, this encoding doesn't see much practical use, since all but the most unusual communication would have significantly more than half of its total mass made up of leading zeros, which doesn't work wonders for efficiency. However, if guaranteed character width is an inflexible issue, this encoding can handle all the million-plus glyph addresses that Unicode accommodates. Of the three major Unicode encodings, UTF-32 is the one that XML parsers aren't obliged to understand. Hence, you probably don't need to worry about it, either.

3.9.3. Other Encodings

The XML standard defines 21 names for character sets that parsers might use (beyond the two they're required to know, UTF-8 and UTF-16). These names range from ISO-8859-1 (ASCII plus 128 characters outside the Latin alphabet) to Shift_JIS, a Microsoftian encoding for Japanese ideographs. While they're not Unicode encodings per se, each character within them maps to one or more Unicode code points (and vice versa, allowing for round-tripping between common encodings by way of Unicode).

XML parsers in Perl all have their own ways of dealing with other encodings. Some may need an extra little nudge. XML::Parser, for example, is weak in its raw state because its underlying library, Expat, understands only a handful of non-Unicode encodings. Fortunately, you can give it a helping hand by installing Clark Cooper's XML::Encoding module, an XML::Parser subclass that can read and understand map files (themselves XML documents) that bind the character code points of other encodings to their Unicode addresses.

3.9.3.1. Core Perl support

As with XML, Perl's relationship with Unicode has heated up at a cautious but inevitable pace.[21] Generally, you should use Perl version 5.6 or greater to work with Unicode properly in your code. If you do have 5.6 or greater, consult its perlunicode manpage for details on how deep its support runs, as each release since then has gradually deepened its loving embrace with Unicode. If you have an even earlier Perl, whew, you really ought to consider upgrading it. You can eke by with some of the tools we'll mention later in this chapter, but hacking Perl and XML means hacking in Unicode, and you'll notice the lack of core support for it.

[21]The romantic metaphor may start to break down for you here, but you probably understand by now that Perl's polyamorous proclivities help make it the language that it is.

Currently, the most recent stable Perl release, 5.6.1, contains partial support for Unicode. Invoking the use utf8 pragma tells Perl to use UTF-8 encoding with most of its string-handling functions. Perl also allows code to exist in UTF-8, allowing identifiers built from characters living beyond ASCII's one-byte reach. This can prove very useful for hackers who primarily think in glyphs outside the Latin alphabet.

Perl 5.8's Unicode support will be much more complete, allowing UTF-8 and regular expressions to play nice. The 5.8 distribution also introduces the Encode module to Perl's standard library, which will allow any Perl programmer to shift text from legacy encodings to Unicode without fuss:

use Encode 'from_to';
from_to($data, "iso-8859-3", "utf-8"); # from legacy to
utf-8

Finally, Perl 6, being a redesign of the whole language that includes everything the Perl community learned over the last dozen years, will naturally have an even more intimate relationship with Unicode (and will give us an excuse to print a second edition of this book in a few years). Stay tuned to the usual information channels for continuing developments on this front as we see what happens.

3.9.4. Encoding Conversion

If you use a version of Perl older than 5.8, you'll need a little extra help when switching from one encoding to another. Fortunately, your toolbox contains some ratchety little devices to assist you.

3.9.4.1. iconv and Text::Iconv

iconv is a library and program available for Windows and Unix (inlcuding Mac OS X) that provides an easy interface for turning a document of type A into one of type B. On the Unix command line, you can use it like this:

$ iconv -f latin1 -t utf8 my_file.txt > my_unicode_file.txt

If you have iconv on your system, you can also grab the Text::Iconv Perl module from CPAN, which gives you a Perl API to this library. This allows you to quickly re-encode on-disk files or strings in memory.

3.9.4.2. Unicode::String

A more portable solution comes in the form of the Unicode::String module, which needs no underlying C library. The module's basic API is as blissfully simple as all basic APIs should be. Got a string? Feed it to the class's constructor method and get back an object holding that string, as well as a bevy of methods that let you squash and stretch it in useful and amusing ways. Example 3-12 tests the module.

Example 3-12. Unicode test

use Unicode::String;

my $string = "This sentence exists in ASCII and UTF-8, but not UTF-16. Darn!\n";
my $u = Unicode::String->new($string);

# $u now holds an object representing a stringful of 16-bit characters

# It uses overloading so Perl string operators do what you expect!
$u .= "\n\nOh, hey, it's Unicode all of a sudden. Hooray!!\n"

# print as UTF-16 (also known as UCS2)
print $u->ucs2;

# print as something more human-readable
print $u->utf8;

The module's many methods allow you to downgrade your strings, too -- specifically, the utf7 method lets you pop the eighth bit off of UTF-8 characters, which is acceptable if you need to throw a bunch of ASCII characters at a receiver that would flip out if it saw chains of UTF-8 marching proudly its way instead of the austere and solitary encodings of old.

WARNING: XML::Parser sometimes seems a little too eager to get you into Unicode. No matter what a document's declared encoding is, it silently transforms all characters with higher Unicode code points into UTF-8, and if you ask the parser for your data back, it delivers those characters back to you in that manner. This silent transformation can be an unpleasant surprise. If you use XML::Parser as the core of any processing software you write, be aware that you may need to use the convertion tools mentioned in this section to massage your data into a more suitable format.

3.9.4.3. Byte order marks

If, for some reason, you have an XML document from an unknown source and have no idea what its encoding might be, it may behoove you to check for the presence of a byte order mark (BOM) at the start of the document. Documents that use Unicode's UTF-16 and UTF-32 encodings are endian-dependent (while UTF-8 escapes this fate by nature of its peculiar protocol). Not knowing which end of a byte carries the significant bit will make reading these documents similar to reading them in a mirror, rendering their content into a garble that your programs will not appreciate.

Unicode defines a special code point, U+FEFF, as the byte order mark. According to the Unicode specification, documents using the UTF-16 or UTF-32 encodings have the option of dedicating their first two or four bytes to this character.[22] This way, if a program carefully inspecting the document scans the first two bits and sees that they're 0xFE and 0xFF, in that order, it knows it's big-endian UTF-16. On the other hand, if it sees 0xFF 0xFE, it knows that document is little-endian because there is no Unicode code point of U+FFFE. (UTF-32's big- and little-endian BOMs have more padding: 0x00 0x00 0xFE 0xFF and 0xFF 0xFE 0x00 0x00, respectively.)

[22]UTF-8 has its own byte order mark, but its purpose is to identify the document at UTF-8, and thus has little use in the XML world. The UTF-8 encoding doesn't have to worry about any of this endianness business since all its characters are made of strung-together byte sequences that are always read from first to last instead of little boxes holding byte pairs whose order may be questionable.

The XML specification states that UTF-16- and UTF-32-encoded documents must use a BOM, but, referring to the Unicode specification, we see that documents created by the engines of sane and benevolent masters will arrive to you in network order. In other words, they arrive to you in a big-endian fashion, which was some time ago declared as the order to use when transmitting data between machines. Conversely, because you are sane and benevolent, you should always transmit documents in network order when you're not sure which order to use. However, if you ever find yourself in doubt that you've received a sane document, just close your eyes and hum this tune:

open XML_FILE, $filename or die "Can't read $filename: $!";
my $bom; # will hold possible byte order mark

# read the first two bytes
read XML_FILE, $bom, 2;

# Fetch their numeric values, via Perl's ord() function
my $ord1 = ord(substr($bom,0,1));
my $ord2 = ord(substr($bom,1,1));

if ($ord1 == 0xFE && $ord2 == 0xFF) {
  # It looks like a UTF-16 big-endian document!
  # ... act accordingly here ...
} elsif ($ord1 == 0xFF && $ord2 == 0xEF) {
  # Oh, someone was naughty and sent us a UTF-16 little-endian document.
  # Probably we'll want to effect a byteswap on the thing before working with it.
} else {
  # No byte order mark detected.
}

You might run this example as a last-ditch effort if your parser complains that it can't find any XML in the document. The first line might indeed be a valid <?xml ... > declaration , but your parser sees some gobbledygook instead.

Previous | Home | Next
3.8. XML::Writer | Book Index | 4. Event Streams
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Event Streams (Perl and XML)

 Perl & XMLSearch this book

Chapter 4. Event Streams

Contents:

Working with Streams
Events and Handlers
The Parser as Commodity
Stream Applications
XML::PYX
XML::Parser

Now that you're all warmed up with parsers and have enough knowledge to make you slightly dangerous, we'll analyze one of the two important styles of XML processing: event streams. We'll look at some examples that show the basic theory of stream processing and graduate with a full treatment of the standard Simple API for XML (SAX).

4.1. Working with Streams

In the world of computer science, a stream is a sequence of data chunks to be processed. A file, for example, is a sequence of characters (one or more bytes each, depending on the encoding). A program using this data can open a filehandle to the file, creating a character stream, and it can choose to read in data in chunks of whatever size it chooses. Streams can be dynamically generated too, whether from another program, received over a network, or typed in by a user. A stream is an abstraction, making the source of the data irrelevant for the purpose of processing.

To summarize, here are a stream's important qualities:

  • It consists of a sequence of data fragments.

  • The order of fragments transmitted is significant.

  • The source of data (e.g., file or program output) is not important.

XML streams are just clumpy character streams. Each data clump, called a token in parser parlance, is a conglomeration of one or more characters. Each token corresponds to a type of markup, such as an element start or end tag, a string of character data, or a processing instruction. It's very easy for parsers to dice up XML in this way, requiring minimal resources and time.

What makes XML streams different from character streams is that the context of each token matters; you can't just pump out a stream of random tags and data and expect an XML processor to make sense of it. For example, a stream of ten start tags followed by no end tags is not very useful, and definitely not well-formed XML. Any data that isn't well-formed will be rejected. After all, the whole purpose of XML is to package data in a way that guarantees the integrity of a document's structure and labeling, right?

These contextual rules are helpful to the parser as well as the front-end processor. XML was designed to be very easy to parse, unlike other markup languages that can require look-ahead or look-behind. For example, SGML does not have a rule requiring nonempty elements to have an end tag. To know when an element ends requires sophisticated reasoning by the parser. This requirement leads to code complexity, slower processing speed, and increased memory usage.

Previous | Home | Next
3.9. Character Sets and Encodings | Book Index | 4.2. Events and Handlers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Events and Handlers (Perl and XML)

 Perl & XMLSearch this book

4.2. Events and Handlers

Why do we call it an event stream and not an element stream or a markup object stream? The fact that XML is hierarchical (elements contain other elements) makes it impossible to package individual elements and serve them up as tokens in the stream. In a well-formed document, all elements are contained in one root element. A root element that contains the whole document is not a stream. Thus, we really can't expect a stream to give a complete element in a token, unless it's an empty element.

Instead, XML streams are composed of events. An event is a signal that the state of the document (as we've seen it so far in the stream) has changed. For example, when the parser comes across the start tag for an element, it indicates that another element was opened and the state of parsing has changed. An end tag affects the state by closing the most recently opened element. An XML processor can keep track of open elements in a stack data structure, pushing newly opened elements and popping off closed ones. At any given moment during parsing, the processor knows how deep it is in the document by the size of the stack.

Though parsers support a variety of events, there is a lot of overlap. For example, one parser may distinguish between a start tag and an empty element, while another may not, but all will signal the presence of that element. Let's look more closely at how a parser might dole out tokens, as shown Example 4-1.

Example 4-1. XML fragment

<recipe>
  <name>peanut butter and jelly sandwich</name>
  <!-- add picture of sandwich here -->
  <ingredients>
    <ingredient>Gloppy&trade; brand peanut butter</ingredient>
    <ingredient>bread</ingredient>
    <ingredient>jelly</ingredient>
  </ingredients>
  <instructions>
    <step>Spread peanutbutter on one slice of bread.</step>
    <step>Spread jelly on the other slice of bread.</step>
    <step>Put bread slices together, with peanut butter and
  jelly touching.</step>
  </instructions>
</recipe>

Apply a parser to the preceding example and it might generate this list of events:

  1. A document start (if this is the beginning of a document and not a fragment)

  2. A start tag for the <recipe> element

  3. A start tag for the <name> element

  4. The piece of text "peanut butter and jelly sandwich"

  5. An end tag for the <name> element

  6. A comment with the text "add picture of sandwich here"

  7. A start tag for the <ingredients> element

  8. A start tag for the <ingredient> element

  9. The text "Gloppy"

  10. A reference to the entity trade

  11. The text "brand peanut butter"

  12. An end tag for the <ingredient> element

. . . and so on, until the final event -- the end of the document -- is reached.

Somewhere between chopping up a stream into tokens and processing the tokens is a layer one might call a dispatcher. It branches the processing depending on the type of token. The code that deals with a particular token type is called a handler. There could be a handler for start tags, another for character data, and so on. It could be a compound if statement, switching to a subroutine to handle each case. Or, it could be built into the parser as a callback dispatcher, as is the case with XML::Parser's stream mode. If you register a set of subroutines, one to an event type, the parser calls the appropriate one for each token as it's generated. Which strategy you use depends on the parser.

Previous | Home | Next
4. Event Streams | Book Index | 4.3. The Parser as Commodity
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Parser as Commodity (Perl and XML)

 Perl & XMLSearch this book

4.3. The Parser as Commodity

You don't have to write an XML processing program that separates parser from handler, but doing so can be advantageous. By making your program modular, you make it easier to organize and test your code. The ideal way to modularize is with objects, communicating on sanctioned channels and otherwise leaving one another alone. Modularization makes swapping one part for another easier, which is very important in XML processing.

The XML stream, as we said before, is an abstraction, which makes the source of data irrelevant. It's like the spigot you have in the backyard, to which you can hook up a hose and water your lawn. It doesn't matter where you plug it in, you just want the water. There's nothing special about the hose either. As long as it doesn't leak and it reaches where you want to go, you don't care if it's made of rubber or bark. Similarly, XML parsers have become a commodity: something you can download, plug in, and see it work as expected. Plugging it in easily, however, is the tricky part.

The key is the screwhead on the end of the spigot. It's a standard gauge of pipe that uses a specific thread size, and any hose you buy should fit. With XML event streams, we also need a standard interface there. XML developers have settled on SAX, which has been in use for a few years now. Until recently, Perl XML parsers were not interchangeable. Each had its own interface, making it difficult to swap out one in favor of another. That's changing now, as developers adopt SAX and agree on conventions for hooking up handlers to parsers. We'll see some of the fruits of this effort in Chapter 5, "SAX".

Previous | Home | Next
4.2. Events and Handlers | Book Index | 4.4. Stream Applications
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Stream Applications (Perl and XML)

 Perl & XMLSearch this book

4.4. Stream Applications

Stream processing is great for many XML tasks. Here are a few of them:

Filter

A filter outputs an almost identical copy of the source document, with a few small changes. Every incidence of an <A> element might be converted into a <B> element, for example. The handler is simple, as it has to output only what it receives, except to make a subtle change when it detects a specific event.

Selector

If you want a specific piece of information from a document, without the rest of the content, you can write a selector program. This program combs through events, looking for an element or attribute containing a particular bit of unique data called a key, and then stops. The final job of the program is to output the sought-after record, possibly reformatted.

Summarizer

This program type consumes a document and spits out a short summary. For example, an accounting program might calculate a final balance from many transaction records; a program might generate a table of contents by outputting the titles of sections; an index generator might create a list of links to certain keywords highlighted in the text. The handler for this kind of program has to remember portions of the document to repackage it after the parser is finished reading the file.

Converter

This sophisticated type of program turns your XML-formatted document into another format -- possibly another application of XML. For example, turning DocBook XML into HTML can be done in this way. This kind of processing pushes stream processing to its limits.

XML stream processing works well for a wide variety of tasks, but it does have limitations. The biggest problem is that everything is driven by the parser, and the parser has a mind of its own. Your program has to take what it gets in the order given. It can't say, "Hold on, I need to look at the token you gave me ten steps back" or "Could you give me a sneak peek at a token twenty steps down the line?" You can look back to the parsing past by giving your program a memory. Clever use of data structures can be used to remember recent events. However, if you need to look behind a lot, or look ahead even a little, you probably need to switch to a different strategy: tree processing, the topic of Chapter 6, "Tree Processing".

Now you have the grounding for XML stream processing. Let's move on to specific examples and see how to wrangle with XML streams in real life.

Previous | Home | Next
4.3. The Parser as Commodity | Book Index | 4.5. XML::PYX
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::PYX (Perl and XML)

 Perl & XMLSearch this book

4.5. XML::PYX

In the Perl universe, standard APIs have been slow to catch on for many reasons. CPAN, the vast storehouse of publicly offered modules, grows organically, with no central authority to approve of a submission. Also, with XML, a relative newcomer on the data format scene, the Perl community has only begun to work out standard solutions.

We can characterize the first era of XML hacking in Perl to be the age of nonstandard parsers. It's a time when documentation is scarce and modules are experimental. There is much creativity and innovation, and just as much idiosyncrasy and quirkiness. Surprisingly, many of the tools that first appeared on the horizon were quite useful. It's fascinating territory for historians and developers alike.

XML::PYX is one of these early parsers. Streams naturally lend themselves to the concept of pipelines, where data output from one program can be plugged into another, creating a chain of processors. There's no reason why XML can't be handled that way, so an innovative and elegant processing style has evolved around this concept. Essentially, the XML is repackaged as a stream of easily recognizable and transmutable symbols, even as a command-line utility.

One example of this repackaging is PYX, a symbolic encoding of XML markup that is friendly to text processing languages like Perl. It presents each XML event on a separate line very cleverly. Many Unix programs like awk and grep are line oriented, so they work well with PYX. Lines are happy in Perl too.

Table 4-1 summarizes the notation of PYX.

Table 4-1. PYX notation

Symbol | Represents
( | An element start tag
) | An element end tag
- | Character data
A | An attribute
? | A processing instruction

For every event coming through the stream, PYX starts a new line, beginning with one of the five symbols shown in Table 4-1. This line is followed by the element name or whatever other data is pertinent. Special characters are escaped with a backslash, as you would see in Perl code.

Here's how a parser converting an XML document into PYX notation would look. The following code is XML input by the parser:

<shoppinglist>
  <!-- brand is not important -->
  <item>toothpaste</item>
  <item>rocket engine</item>
  <item optional="yes">caviar</item>
</shoppinglist>

As PYX, it would look like this:

(shoppinglist
-\n
(item
-toothpaste
)item
-\n
(item
-rocket engine
)item
-\n
(item
Aoptional yes
-caviar
)item
-\n
)shoppinglist

Notice that the comment didn't come through in the PYX translation. PYX is a little simplistic in some ways, omitting some details in the markup. It will not alert you to CDATA markup sections, although it will let the content pass through. Perhaps the most serious loss is character entity references that disappear from the stream. You should make sure you don't need that information before working with PYX.

Matt Sergeant has written a module, XML::PYX, which parses XML and translates it into PYX. The compact program in Example 4-2 strips out all XML element tags, leaving only the character data.

Example 4-2. PYX parser

use XML::PYX;

# initialize parser and generate PYX
my $parser = XML::PYX::Parser->new;
my $pyx;
if (defined ( $ARGV[0] )) {
  $pyx = $parser->parsefile( $ARGV[0] );
}

# filter out the tags
foreach( split( / /, $pyx )) {
  print $' if( /^-/ );
}

PYX is an interesting alternative to SAX and DOM for quick-and-dirty XML processing. It's useful for simple tasks like element counting, separating content from markup, and reporting simple events. However, it does lack sophistication, making it less attractive for complex processing.

Previous | Home | Next
4.4. Stream Applications | Book Index | 4.6. XML::Parser
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::Parser (Perl and XML)

 Perl & XMLSearch this book

4.6. XML::Parser

Another early parser is XML::Parser , the first fast and efficient parser to hit CPAN. We detailed its many-faceted interface in Chapter 3, "XML Basics: Reading and Writing". Its built-in stream mode is worth a closer look, though. Let's return to it now with a solid stream example.

We'll use XML::Parser to read a list of records encoded as an XML document. The records contain contact information for people, including their names, street addresses, and phone numbers. As the parser reads the file, our handler will store the information in its own data structure for later processing. Finally, when the parser is done, the program sorts the records by the person's name and outputs them as an HTML table.

The source document is listed in Example 4-3. It has a <list> element as the root, with four <entry> elements inside it, each with an address, a name, and a phone number.

Example 4-3. Address book file

<list>
  <entry>
    <name><first>Thadeus</first><last>Wrigley</last></name>
    <phone>716-505-9910</phone>
    <address>
      <street>105 Marsupial Court</street>
      <city>Fairport</city><state>NY</state><zip>14450</zip>
    </address>
  </entry>
  <entry>
    <name><first>Jill</first><last>Baxter</last></name>
    <address>
      <street>818 S. Rengstorff Avenue</street>
      <zip>94040</zip>
      <city>Mountainview</city><state>CA</state>
    </address>
    <phone>217-302-5455</phone>
  </entry>
  <entry>
    <name><last>Riccardo</last>
    <first>Preston</first></name>
    <address>
      <street>707 Foobah Drive</street>
      <city>Mudhut</city><state>OR</state><zip>32777</zip>
    </address>
    <phone>111-222-333</phone>
  </entry>
  <entry>
    <address>
      <street>10 Jiminy Lane</street>
      <city>Scrapheep</city><state>PA</state><zip>99001</zip>
    </address>
    <name><first>Benn</first><last>Salter</last></name>
    <phone>611-328-7578</phone>
  </entry>
</list>

This simple structure lends itself naturally to event processing. Each <entry> start tag signals the preparation of a new part of the data structure for storing data. An </entry> end tag indicates that all data for the record has been collected and can be saved. Similarly, start and end tags for <entry> subelements are cues that tell the handler when and where to save information. Each <entry> is self-contained, with no links to the outside, making it easy to process.

The program is listed in Example 4-4. At the top is code used to initialize the parser object with references to subroutines, each of which will serve as the handler for a single event. This style of event handling is called a callback because you write the subroutine first, and the parser then calls it back when it needs it to handle an event.

After the initialization, we declare some global variables to store information from XML elements for later processing. These variables give the handlers a memory, as mentioned earlier. Storing information for later retrieval is often called saving state because it helps the handlers preserve the state of the parsing up to the current point in the document.

After reading in the data and applying the parser to it, the rest of the program defines the handler subroutines. We handle five events: the start and end of the document, the start and end of elements, and character data. Other events, such as comments, processing instructions, and document type declarations, will all be ignored.

Example 4-4. Code for the address program

# initialize the parser with references to handler routines
#
use XML::Parser;
my $parser = XML::Parser->new( Handlers => {
    Init =>    \&handle_doc_start,
    Final =>   \&handle_doc_end,
    Start =>   \&handle_elem_start,
    End =>     \&handle_elem_end,
    Char =>    \&handle_char_data,
});

#
# globals
#
my $record;       # points to a hash of element contents
my $context;      # name of current element
my %records;      # set of address entries

#
# read in the data and run the parser on it
#
my $file = shift @ARGV;
if( $file ) {
    $parser->parsefile( $file );
} else {
    my $input = "";
    while( <STDIN> ) { $input .= $_; }
    $parser->parse( $input );
}
exit;

###
### Handlers
###

#
# As processing starts, output the beginning of an HTML file.
#
sub handle_doc_start {
    print "<html><head><title>addresses</title></head>\n";
    print "<body><h1>addresses</h1>\n";
}

#
# save element name and attributes
#
sub handle_elem_start {
    my( $expat, $name, %atts ) = @_;
    $context = $name;
    $record = {} if( $name eq 'entry' );
}

# collect character data into the recent element's buffer
#
sub handle_char_data {
    my( $expat, $text ) = @_;

    # Perform some minimal entitizing of naughty characters
    $text =~ s/&/&/g;
    $text =~ s/</&lt;/g;

    $record->{ $context } .= $text;
}

#
# if this is an <entry>, collect all the data into a record
#
sub handle_elem_end {
    my( $expat, $name ) = @_;
    return unless( $name eq 'entry' );
    my $fullname = $record->{'last'} . $record->{'first'};
    $records{ $fullname } = $record;
}

#
# Output the close of the file at the end of processing.
#
sub handle_doc_end {
    print "<table border='1'>\n";
    print "<tr><th>name</th><th>phone</th><th>address</th></tr>\n";
    foreach my $key ( sort( keys( %records ))) {
        print "<tr><td>" . $records{ $key }->{ 'first' } . ' ';
        print $records{ $key }->{ 'last' } . "</td><td>";
        print $records{ $key }->{ 'phone' } . "</td><td>";
        print $records{ $key }->{ 'street' } . ', ';
        print $records{ $key }->{ 'city' } . ', ';
        print $records{ $key }->{ 'state' } . ' ';
        print $records{ $key }->{ 'zip' } . "</td></tr>\n";
    }
    print "</table>\n</div>\n</body></html>\n";
}

To understand how this program works, we need to study the handlers. All handlers called by XML::Parser receive a reference to the expat parser object as their first argument, a courtesy to developers in case they want to access its data (for example, to check the input file's current line number). Other arguments may be passed, depending on the kind of event. For example, the start-element event handler gets the name of the element as the second argument, and then gets a list of attribute names and values.

Our handlers use global variables to store information. If you don't like global variables (in larger programs, they can be a headache to debug), you can create an object that stores the information internally. You would then give the parser your object's methods as handlers. We'll stick with globals for now because they are easier to read in our example.

The first handler is handle_doc_start, called at the start of parsing. This handler is a convenient way to do some work before processing the document. In our case, it just outputs HTML code to begin the HTML page in which the sorted address entries will be formatted. This subroutine has no special arguments.

The next handler, handle_elem_start, is called whenever the parser encounters the start of a new element. After the obligatory expat reference, the routine gets two arguments: $name, which is the element name, and %atts, a hash of attribute names and values. (Note that using a hash will not preserve the order of attributes, so if order is important to you, you should use an @atts array instead.) For this simple example, we don't use attributes, but we leave open the possibility of using them later.

This routine sets up processing of an element by saving the name of the element in a variable called $context. Saving the element's name ensures that we will know what to do with character data events the parser will send later. The routine also initializes a hash called %record, which will contain the data for each of <entry>'s subelements in a convenient look-up table.

The handler handle_char_data takes care of nonmarkup data -- basically all the character data in elements. This text is stored in the second argument, here called $text. The handler only needs to save the content in the buffer $record->{ $context }. Notice that we append the character data to the buffer, rather than assign it outright. XML::Parser has a funny quirk in which it calls the character handler after each line or newline-separated string of text.[23] Thus, if the content of an element includes a newline character, this will result in two separate calls to the handler. If you didn't append the data, then the last call would overwrite the one before it.

[23]This way of reading text is uniquely Perlish. XML purists might be confused about this handling of character data. XML doesn't care about newlines, or any whitespace for that matter; it's all just character data and is treated the same way.

Not surprisingly, handle_elem_end handles the end of element events. The second argument is the element's name, as with the start-element event handler. For most elements, there's not much to do here, but for <entry>, we have a final housekeeping task. At this point, all the information for a record has been collected, so the record is complete. We only have to store it in a hash, indexed by the person's full name so that we can easily sort the records later. The sorting can be done only after all the records are in, so we need to store the record for later processing. If we weren't interested in sorting, we could just output the record as HTML.

Finally, the handle_doc_end handler completes our set, performing any final tasks that remain after reading the document. It so happens that we do have something to do. We need to print out the records, sorted alphabetically by contact name. The subroutine generates an HTML table to format the entries nicely.

This example, which involved a flat sequence of records, was pretty simple, but not all XML is like that. In some complex document formats, you have to consider the parent, grandparent, and even distant ancestors of the current element to decide what to do with an event. Remembering an element's ancestry requires a more sophisticated state-saving structure, which we will show in a later example.

Previous | Home | Next
4.5. XML::PYX | Book Index | 5. SAX
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX (Perl and XML)

 Perl & XMLSearch this book

Chapter 5. SAX

Contents:

SAX Event Handlers
DTD Handlers
External Entity Resolution
Drivers for Non-XML Sources
A Handler Base Class
XML::Handler::YAWriter as a Base Handler Class
XML::SAX: The Second Generation

XML::Parser has done remarkably well as a multipurpose XML parser and stream generator, but it really isn't the future of Perl and XML. The problem is that we don't want one standard parser for all ends and purposes; we want to be able to choose from multiple parsers, each serving a different purpose. One parser might be written completely in Perl for portability, while another is accelerated with a core written in C. Or, you might want a parser that translates one format (such as a spreadsheet) into an XML stream. You simply can't anticipate all the things a parser might be called on to do. Even XML::Parser, with its many options and multiple modes of operation, can't please everybody. The future, then, is a multiplicity of parsers that cover any situation you encounter.

An environment with multiple parsers demands some level of consistency. If every parser had its own interface, developers would go mad. Learning one interface and being able to expect all parsers to comply to that is better than having to learn a hundred different ways to do the same thing. We need a standard interface between parsers and code: a universal plug that is flexible and reliable, free from the individual quirks of any particular parser.

The XML development world has settled on an event-driven interface called SAX. SAX evolved from discussions on the XML-DEV mailing list and, shepherded by David Megginson,[24] was quickly shaped into a useful specification. The first incarnation, called SAX Level 1 (or just SAX1), supports elements, attributes, and processing instructions. It doesn't handle some other things like namespaces or CDATA sections, so the second iteration, SAX2, was devised, adding support for just about any event you can imagine in generic XML.

[24]David Megginson maintains a web page about SAX at http://www.saxproject.org.

SAX has been a huge success. Its simplicity makes it easy to learn and work with. Early development with XML was mostly in the realm of Java, so SAX was codified as an interface construct. An interface construct is a special kind of class that declares an object's methods without implementing them, leaving the implementation up to the developer.

Enthusiasm for SAX soon infected the Perl community and implementations began to appear in CPAN, but there was a problem. Perl doesn't provide a rigorous way to define a standard interface like Java does. It has weak type checking and forgives all kinds of inconsistencies. Whereas Java compares argument types in functions with those defined in the interface construct at compile time, Perl quietly accepts any arguments you use. Thus, defining a standard in Perl is mostly a verbal activity, relying on the developer's experience and watchfulness to comply.

One of the first Perl implementations of SAX is Ken McLeod's XML::Parser::PerlSAX module. As a subclass of XML::Parser, it modifies the stream of events from Expat to repackage them as SAX events.

5.1. SAX Event Handlers

To use a typical SAX module in a program, you must pass it an object whose methods implement handlers for SAX events. Table 5-1 describes the methods in a typical handler object. A SAX parser passes a hash to each handler containing properties relevant to the event. For example, in this hash, an element handler would receive the element's name and a list of attributes.

Table 5-1. PerlSAX handlers

Method name | Event | Properties
start_document | The document processing has started (this is the first event) | (none defined)
end_document | The document processing is complete (this is the last event) | (none defined)
start_element | An element start tag or empty element tag was found | Name, Attributes
end_element | An element end tag or empty element tag was found | Name
characters | A string of nonmarkup characters (character data) was found | Data
processing_instruction | A parser encountered a processing instruction | Target, Data
comment | A parser encountered a comment | Data
start_cdata | The beginning of a CDATA section encountered (the following character data may contain reserved markup characters) | (none defined)
end_cdata | The end of an encountered CDATA section | (none defined)
entity_reference | An internal entity reference was found (as opposed to an external entity reference, which would indicate that a file needs to be loaded) | Name, Value

A few notes about handler methods:

  • For an empty element, both the start_element( ) and end_element( ) handlers are called, in that order. No handler exists specifically for empty elements.

  • The characters( ) handler may be called more than once for a string of contiguous character data, parceling it into pieces. For example, a parser might break text around an entity reference, which is often more efficient for the parser.

  • The characters( ) handler will be called for any whitespace between elements, even if it doesn't seem like significant data. In XML, all characters are considered part of data. It's simply more efficient not to make a distinction otherwise.

  • Handling of processing instructions, comments, and CDATA sections is optional. In the absence of handlers, the data from processing instructions and comments is discarded. For CDATA sections, calls are still made to the characters( ) handler as before so the data will not be lost.

  • The start_cdata( ) and end_cdata( ) handlers do not receive data. Instead, they merely act as signals to tell you whether reserved markup characters can be expected in future calls to the characters( ) handler.

  • In the absence of an entity_reference( ) handler, all internal entity references will be resolved automatically by the parser, and the resulting text or markup will be handled normally. If you do define an entity_reference( ) handler, the entity references will not be expanded and you can do what you want with them.

Let's show an example now. We'll write a program called a filter, a special processor that outputs a replica of the original document with a few modifications. Specifically, it makes these changes to a document:

  • Turns every XML comment into a <comment> element

  • Deletes processing instructions

  • Removes tags, but leaves the content, for <literal> elements that occur within <programlisting> elements at any level

The code for this program is listed in Example 5-1. Like the last program, we initialize the parser with a set of handlers, except this time they are bundled together in a convenient package: an object called MyHandler. Notice that we've implemented a few more handlers, since we want to be able to deal with comments, processing instructions, and the document prolog.

Example 5-1. Filter program

# initialize the parser
#
use XML::Parser::PerlSAX;
my $parser = XML::Parser::PerlSAX->new( Handler => MyHandler->new( ) );

if( my $file = shift @ARGV ) {
    $parser->parse( Source => {SystemId => $file} );
} else {
    my $input = "";
    while( <STDIN> ) { $input .= $_; }
    $parser->parse( Source => {String => $input} );
}
exit;

#
# global variables
#
my @element_stack;                # remembers element names
my $in_intset;                    # flag: are we in the internal subset?

###
### Document Handler Package
###
package MyHandler;

#
# initialize the handler package
#
sub new {
    my $type = shift;
    return bless {}, $type;
}

#
# handle a start-of-element event: output start tag and attributes
#
sub start_element {
    my( $self, $properties ) = @_;
    # note: the hash %{$properties} will lose attribute order

    # close internal subset if still open
    output( "]>\n" ) if( $in_intset );
    $in_intset = 0;

    # remember the name by pushing onto the stack
    push( @element_stack, $properties->{'Name'} );

    # output the tag and attributes UNLESS it's a <literal>
    # inside a <programlisting>
    unless( stack_top( 'literal' ) and
            stack_contains( 'programlisting' )) {
        output( "<" . $properties->{'Name'} );
        my %attributes = %{$properties->{'Attributes'}};
        foreach( keys( %attributes )) {
            output( " $_=\"" . $attributes{$_} . "\"" );
        }
        output( ">" );
    }
}

#
# handle an end-of-element event: output end tag UNLESS it's from a
# <literal> inside a <programlisting>
#
sub end_element {
    my( $self, $properties ) = @_;
    output( "</" . $properties->{'Name'} . ">" )
         unless( stack_top( 'literal' ) and
                stack_contains( 'programlisting' ));
    pop( @element_stack );
}

#
# handle a character data event
#
sub characters {
    my( $self, $properties ) = @_;
    # parser unfortunately resolves some character entities for us,
    # so we need to replace them with entity references again
    my $data = $properties->{'Data'};
    $data =~ s/\&/\&/;
    $data =~ s/</\&lt;/;
    $data =~ s/>/\&gt;/;
    output( $data );
}

#
# handle a comment event: turn into a <comment> element
#
sub comment {
    my( $self, $properties ) = @_;
    output( "<comment>" . $properties->{'Data'} . "</comment>" );
}

#
# handle a PI event: delete it
#
sub processing_instruction {
  # do nothing!
}

#
# handle internal entity reference (we don't want them resolved)
#
sub entity_reference {
    my( $self, $properties ) = @_;
    output( "&" . $properties->{'Name'} . ";" );
}

sub stack_top {
    my $guess = shift;
    return $element_stack[ $#element_stack ] eq $guess;
}

sub stack_contains {
    my $guess = shift;
    foreach( @element_stack ) {
        return 1 if( $_ eq $guess );
    }
    return 0;
}

sub output {
    my $string = shift;
    print $string;
}

Looking closely at the handlers, we see that one argument is passed, in addition to the obligatory object reference $self. This argument is a reference to a hash of properties about the event. This technique has one disadvantage: in the element start handler, the attributes are stored in a hash, which has no memory of the original attribute order. Semantically, this is not a big deal, since XML is supposed to be ignorant of attribute order. However, there may be cases when you want to replicate that order.[25]

[25]In the case of our filter, we might want to compare the versions from before and after processing using a utility such as the Unix program diff. Such a comparison would yield many false differences where the order of attributes changed. Instead of using diff, you should consider using the module XML::SemanticDiff by Kip Hampton. This module would ignore syntactic differences and compare only the semantics of two documents.

As a filter, this program preserves everything about the original document, except for the few details that have to be changed. The program preserves the document prolog, processing instructions, and comments. Even entity references should be preserved as they are instead of being resolved (as the parser may want to do). Therefore, the program has a few more handlers than in the last example, from which we were interested only in extracting very specific information.

Let's test this program now. Our input datafile is listed in Example 5-2.

Example 5-2. Data for the filter

<?xml version="1.0"?>
<!DOCTYPE book
  SYSTEM "/usr/local/prod/sgml/db.dtd"
[
  <!ENTITY thingy "hoo hah blah blah">
]>

<book id="mybook">
<?print newpage?>
  <title>GRXL in a Nutshell</title>
  <chapter id="intro">
    <title>What is GRXL?</title>
<!-- need a better title -->
    <para>
Yet another acronym.  That was our attitude at first, but then we saw
the amazing uses of this new technology called
<literal>GRXL</literal>.  Consider the following program:
    </para>
<?print newpage?>
    <programlisting>AH aof -- %%%%
{{{{{{ let x = 0 }}}}}}
  print!  <lineannotation><literal>wow</literal></lineannotation>
or not!</programlisting>
<!-- what font should we use? -->
    <para>
What does it do?  Who cares?  It's just lovely to look at.  In fact,
I'd have to say, "&thingy;".
    </para>
<?print newpage?>
  </chapter>
</book>

The result, after running the program on the data, is shown in Example 5-3.

Example 5-3. Output from the filter

<book id="mybook">
  <title>GRXL in a Nutshell</title>
  <chapter id="intro">
    <title>What is GRXL?</title>
<comment> need a better title </comment>
    <para>
Yet another acronym.  That was our attitude at first, but then we saw
the amazing uses of this new technology called
<literal>GRXL</literal>.  Consider the following program:
    </para>

    <programlisting>AH aof -- %%%%
{{{{{{ let x = 0 }}}}}}
  print!  <lineannotation>wow</lineannotation>
or not!</programlisting>
<comment> what font should we use? </comment>
    <para>
What does it do?  Who cares?  It's just lovely to look at.  In fact,
I'd have to say, "&thingy;".
    </para>

  </chapter>
</book>

Here's what the filter did right. It turned an XML comment into a <comment> element and deleted the processing instruction. The <literal> element in the <programlisting> was removed, with its contents left intact, while other <literal> elements were preserved. Entity references were left unresolved, as we wanted. So far, so good. But something's missing. The XML declaration, document type declaration, and internal subset are gone. Without the declaration for the entity thingy, this document is not valid. It looks like the handlers we had available to us were not sufficient.

Previous | Home | Next
4.6. XML::Parser | Book Index | 5.2. DTD Handlers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DTD Handlers (Perl and XML)

 Perl & XMLSearch this book

5.2. DTD Handlers

XML::Parser::PerlSAX supports another group of handlers used to process DTD events . It takes care of anything that appears before the root element, such as the XML declaration, doctype declaration, and the internal subset of entity and element declarations, which are collectively called the document prolog. If you want to output the document literally as you read it (e.g., in a filter program), you need to define some of these handlers to reproduce the document prolog. Defining these handlers is just what we needed in the previous example.

You can use these handlers for other purposes. For example, you may need to pre-load entity definitions for special processing rather than rely on the parser to do its default substitution for you. These handlers are listed in Table 5-2.

Table 5-2. PerlSAX DTD handlers

Method name | Event | Properties
entity_decl | The parser sees an entity declaration (internal or external, parsed or unparsed). | Name, Value, PublicId, SystemId, Notation
notation_decl | The parser found a notation declaration. | Name, PublicId, SystemId, Base
unparsed_entity_decl | The parser found a declaration for an unparsed entity (e.g., a binary data entity). | Name, PublicId, SystemId, Base
element_decl | An element declaration was found. | Name, Model
attlist_decl | An element's attribute list declaration was encountered. | ElementName, AttributeName, Type, Fixed
doctype_decl | The parser found the document type declaration. | Name, SystemId, PublicId, Internal
xml_decl | The XML declaration was encountered. | Version, Encoding, Standalone

The entity_decl( ) handler is called for all kinds of entity declarations unless a more specific handler is defined. Thus, unparsed entity declarations trigger the entity_decl( ) handler unless you've defined an unparsed_entity_decl( ), which will take precedence.

entity_decl( )'s parameters vary depending on the entity type. The Value parameter is set for internal entities, but not external ones. Likewise, PublicId and SystemId, parameters that tell an XML processor where to find the file containing the entity's value, is not set for internal entities, only external ones. Base tells the procesor what to use for a base URL if the SystemId contains a relative location.

Notation declarations are a special feature of DTDs that allow you to assign a special type identifier to an entity. For example, you could declare an entity to be of type "date" to tell the XML processor that the entity should be treated as that kind of data. It's not used very often in XML, so we won't go into it further.

The Model property of the element_decl( ) contains the content model, or grammar, for an element. This property describes what is allowed to go inside an element according to the DTD.

An attribute list declaration in a DTD can contain more than one attribute description. Fortunately, the parser breaks these descriptions up into individual calls to the attlist_decl( ) handler for each attribute.

The document type declaration is an optional part of the document at the top, just under the XML declaration. The parameter Name is the name of the root element in your document. PublicId and SystemId tell the processor where to find the external DTD. Finally, the Internal parameter contains the whole internal subset as a string, in case you want to skip the individual entity and element declaration handling.

As an example, let's say you wanted to add to the filter example code to output the document prolog exactly as it was encountered by the parser. You'd need to define handlers like the program in Example 5-4.

Example 5-4. A better filter

# handle xml declaration
#
sub xml_decl {
    my( $self, $properties ) = @_;
    output( "<?xml version=\"" . $properties->{'Version'} . "\"" );
    my $encoding = $properties->{'Encoding'};
    output( " encoding=\"$encoding\"" ) if( $encoding );
    my $standalone = $properties->{'Standalone'};
    output( " standalone=\"$standalone\"" ) if( $standalone );
    output( "?>\n" );
}

#
# handle doctype declaration:
# try to duplicate the original
#
sub doctype_decl {
    my( $self, $properties ) = @_;
    output( "\n<!DOCTYPE " . $properties->{'Name'} . "\n" );
    my $pubid = $properties->{'PublicId'};
    if( $pubid ) {
        output( "  PUBLIC \"$pubid\"\n" );
        output( "  \"" . $properties->{'SystemId'} . "\"\n" );
    } else {
        output( "  SYSTEM \"" . $properties->{'SystemId'} . "\"\n" );
    }
    my $intset = $properties->{'Internal'};
    if( $intset ) {
        $in_intset = 1;
        output( "[\n" );
    } else {
        output( ">\n" );
    }
}

#
# handle entity declaration in internal subset:
# recreate the original declaration as it was
#
sub entity_decl {
    my( $self, $properties ) = @_;
    my $name = $properties->{'Name'};
    output( "<!ENTITY $name " );
    my $pubid = $properties->{'PublicId'};
    my $sysid = $properties->{'SystemId'};
    if( $pubid ) {
        output( "PUBLIC \"$pubid\" \"$sysid\"" );
    } elsif( $sysid ) {
        output( "SYSTEM \"$sysid\"" );
    } else {
        output( "\"" . $properties->{'Value'} . "\"" );
    }
    output( ">\n" );
}

Now let's see how the output from our filter looks. The result is in Example 5-5.

Example 5-5. Output from the filter

<?xml version="1.0"?>

<!DOCTYPE book
  SYSTEM "/usr/local/prod/sgml/db.dtd"
[
<!ENTITY thingy "hoo hah blah blah">
]>
<book id="mybook">

  <title>GRXL in a Nutshell</title>
  <chapter id="intro">
    <title>What is GRXL?</title>
<comment> need a better title </comment>
    <para>
Yet another acronym.  That was our attitude at first, but then we saw
the amazing uses of this new technology called
<literal>GRXL</literal>.  Consider the following program:
    </para>

    <programlisting>AH aof -- %%%%
{{{{{{ let x = 0 }}}}}}
  print!  <lineannotation>wow</lineannotation>
or not!</programlisting>
<comment> what font should we use? </comment>
    <para>
What does it do?  Who cares?  It's just lovely to look at.  In fact,
I'd have to say, "&thingy;".
    </para>

  </chapter>
</book>

That's much better. Now we have a complete filter program. The basic handlers take care of elements and everything inside them. The DTD handlers deal with whatever happens outside of the root element.

Previous | Home | Next
5. SAX | Book Index | 5.3. External Entity Resolution
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


External Entity Resolution (Perl and XML)

 Perl & XMLSearch this book

5.3. External Entity Resolution

By default, the parser substitutes all entity references with their actual values for you. Usually that's what you want it to do, but sometimes, as in the case with our filter example, you'd rather keep the entity references in place. As we saw, keeping the entity references is pretty easy to do; just include an entity_reference( ) handler method to override that behavior by outputting the references again. What we haven't seen yet is how to override the default handling of external entity references. Again, the parser wants to replace the references with their values by locating the files and inserting their contents into the stream. Would you ever want to change that behavior, and if so, how would you do it?

Storing documents in multiple files is convenient, especially for really large documents. For example, suppose you have a big book to write in XML and you want to store each chapter in its own file. You can do so easily with external entities. Here's an example:

<?xml version="1.0"?>
<doctype book [
  <!ENTITY intro-chapter   SYSTEM "chapters/intro.xml">
  <!ENTITY pasta-chapter   SYSTEM "chapters/pasta.xml">
  <!ENTITY stirfry-chapter SYSTEM "chapters/stirfry.xml">
  <!ENTITY soups-chapter   SYSTEM "chapters/soups.xml"> ]>

<book>
  <title>The Bonehead Cookbook</title>
  &intro-chapter;
  &pasta-chapter;
  &stirfry-chapter;
  &soups-chapter;
</book>

The previous filter example would resolve the external entity references for you diligently and output the entire book in one piece. Your file separation scheme would be lost and you'd have to edit the resulting file to break it back into multiple files. Fortunately, we can override the resolution of external entity references using a handler called resolve_entity( ).

This handler has four properties: Name, the entity's name; SystemId and PublicId, identifiers that help you locate the file containing the entity's text; and Base, which helps resolve relative URLs, if any exist. Unlike the other handlers, this one should return a value to tell the parser what to do. Returning undef tells the parser to load the external entity as it normally would. Otherwise, you need to return a hash describing an alternative source from which the entity should be loaded. The hash is the same type you would use to give to the object's parse( ) method, with keys like SystemId to give it a filename or URL, or String to give it a string of text. For example:

sub resolve_entity {
  my( $self, $props ) = @_;
  if( exists( $props->{ SystemId }) and
      open( ENT, $props->{ SystemId })) {
    my $entval = '<?start-file ' . $props->{ SystemId } . '?>';
    while( <ENT> ) { $entval .= $_; }
    close ENT;
    $entval .= '<?end-file ' . $props->{ SystemId } . '?>';
    return { String => $entval };
  } else {
    return undef;
  }
}

This routine opens the entity resource, if it's in a file it can find, and gives it to the parser as a string. First, it attaches a processing instruction before and after the entity text, marking the boundary of the file. Later, you can write a routine to look for the PIs and separate the files back out again.

Previous | Home | Next
5.2. DTD Handlers | Book Index | 5.4. Drivers for Non-XML Sources
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Drivers for Non-XML Sources (Perl and XML)

 Perl & XMLSearch this book

5.4. Drivers for Non-XML Sources

The filter example used a file containing an XML document as an input source. This example shows just one of many ways to use SAX. Another popular use is to read data from a driver, which is a program that generates a stream of data from a non-XML source, such as a database. A SAX driver converts the data stream into a sequence of SAX events that we can process the way we did previously. What makes this so cool is that we can use the same code regardless of where the data came from. The SAX event stream abstracts the data and markup so we don't have to worry about it. Changing the program to work with files or other drivers would be trivial.

To see a driver in action, we will write a program that uses Ilya Sterin's module XML::SAXDriver::Excel to convert Microsoft Excel spreadsheets into XML documents. This example shows how a data stream can be processed in a pipeline fashion to ultimately arrive in the form we want it. A Spreadsheet::ParseExcel object reads the file and generates a generic data stream, which an XML::SAXDriver::Excel object translates into a SAX event stream. This stream is then output as XML by our program.

Here's a test Excel spreadsheet, represented as a table:

 | A | B
1 | baseballs | 55
2 | tennisballs | 33
3 | pingpong balls | 12
4 | footballs | 77

The SAX driver will create new elements for us, giving us the names in the form of arguments to handler method calls. We will just print them out as they come and see how the driver structures the document. Example 5-6 is a simple program that does this.

Example 5-6. Excel parsing program

use XML::SAXDriver::Excel;

# get the file name to process
die( "Must specify an input file" ) unless( @ARGV );
my $file = shift @ARGV;
print "Parsing $file...\n";

# initialize the parser
my $handler = new Excel_SAX_Handler;
my %props = ( Source => { SystemId => $file },
              Handler => $handler );
my $driver = XML::SAXDriver::Excel->new( %props );

# start parsing
$driver->parse( %props );

# The handler package we define to print out the XML
# as we receive SAX events.
package Excel_SAX_Handler;

# initialize the package
sub new {
    my $type = shift;
    my $self = {@_};
    return bless( $self, $type );
}

# create the outermost element
sub start_document {
    print "<doc>\n";
}

# end the document element
sub end_document {
    print "</doc>\n";
}

# handle any character data

sub characters {
    my( $self, $properties ) = @_;
    my $data = $properties->{'Data'};
    print $data if defined($data);
}

# start a new element, outputting the start tag
sub start_element {
    my( $self, $properties ) = @_;
    my $name = $properties->{'Name'};
    print "<$name>";
}

# end the new element
sub end_element {
    my( $self, $properties ) = @_;
    my $name = $properties->{'Name'};
    print "</$name>";
}

As you can see, the handler methods look very similar to those used in the previous SAX example. All that has changed is what we do with the arguments. Now let's see what the output looks like when we run it on the test file:

<doc>

<records>
        <record>
                <column1>baseballs</column1>
                <column2>55</column2>
        </record>
        <record>
                <column1>tennisballs</column1>
                <column2>33</column2>
        </record>
        <record>
                <column1>pingpong balls</column1>
                <column2>12</column2>
        </record>
        <record>
                <column1>footballs</column1>
                <column2>77</column2>
        </record>
        <record>
Use of uninitialized value in print at conv line 39.
                <column1></column1>
Use of uninitialized value in print at conv line 39.
                <column2></column2>
        </record>
</records></doc>

The driver did most of the work in creating elements and formatting the data. All we did was output the packages it gave us in the form of method calls. It wrapped the whole document in <records>, making our use of <doc> superfluous. (In the next revision of the code, we'll make the start_document( ) and end_document( ) methods output nothing.) Each row of the spreadsheet is encapsulated in a <record> element. Finally, the two columns are differentiated with <column1> and <column2> labels. All in all, not a bad job.

You can see that with a minimal amount of effort on our part, we have harnessed the power of SAX to do some complex work converting from one format to another. The driver actually automates the conversion, but it gives us enough flexibility in interpreting the events so that we can reject bad data (the empty row, for example) or rename elements. We can even perform complex processing, such as adding up values or sorting rows.

Previous | Home | Next
5.3. External Entity Resolution | Book Index | 5.5. A Handler Base Class
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Handler Base Class (Perl and XML)

 Perl & XMLSearch this book

5.5. A Handler Base Class

SAX doesn't distinguish between different elements; it leaves that burden up to you. You have to sort out the element name in the start_element( ) handler, and maybe use a stack to keep track of element hierarchy. Don't you wish there were some way to abstract that stuff? Ken MacLeod has done just that with his XML::Handler::Subs module.

This module defines an object that branches handler calls to more specific handlers. If you want a handler that deals only with <title> elements, you can write that handler and it will be called. The handler dealing with a start tag must begin with s_, followed by the element's name (replace special characters with an underscore). End tag handlers are the same, but start with e_ instead of s_.

That's not all. The base object also has a built-in stack and provides an accessor method to check if you are inside a particular element. The $self->{Names} variable refers to a stack of element names. Use the method in_element( $name ) to test whether the parser is inside an element named $name at any point in time.

To try this out, let's write a program that does something element-specific. Given an HTML file, the program outputs everything inside an <h1> element, even inline elements used for emphasis. The code, shown in Example 5-7, is breathtakingly simple.

Example 5-7. A program subclassing the handler base

use XML::Parser::PerlSAX;
use XML::Handler::Subs

#
# initialize the parser
#
use XML::Parser::PerlSAX;
my $parser = XML::Parser::PerlSAX->new( Handler => H1_grabber->new( ) );
$parser->parse( Source => {SystemId => shift @ARGV} );

## Handler object: H1_grabber
##
package H1_grabber;
use base( 'XML::Handler::Subs' );

sub new {
    my $type = shift;
    my $self = {@_};
    return bless( $self, $type );
}

#
# handle start of document
#
sub start_document {
  SUPER::start_document( );
  print "Summary of file:\n";
}

#
# handle start of <h1>: output bracket as delineator
#
sub s_h1 {
  print "[";
}

#
# handle end of <h1>: output bracket as delineator
#
sub e_h1 {
  print "]\n";
}

#
# handle character data
#
sub characters {
  my( $self, $props ) = @_;
  my $data = $props->{Data};
  print $data if( $self->in_element( h1 ));
}

Let's feed the program a test file:

<html>
  <head><title>The Life and Times of Fooby</title></head>
  <body>
    <h1>Fooby as a child</h1>
    <p>...</p>
    <h1>Fooby grows up</h1>
    <p>...</p>
    <h1>Fooby is in <em>big</em> trouble!</h1>
    <p>...</p>
  </body>
</html>

This is what we get on the other side:

Summary of file:
[Fooby as a child]
[Fooby grows up]
[Fooby is in big trouble!]

Even the text inside the <em> element was included, thanks to the call to in_element( ). XML::Handler::Subs is definitely a useful module to have when doing SAX processing.

Previous | Home | Next
5.4. Drivers for Non-XML Sources | Book Index | 5.6. XML::Handler::YAWriter as a Base Handler Class
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::Handler::YAWriter as a Base Handler Class (Perl and XML)

 Perl & XMLSearch this book

5.6. XML::Handler::YAWriter as a Base Handler Class

Michael Koehne's XML::Handler::YAWriter serves as the "yet another" XML writer it bills itself as, but in doing so also sets itself up as a handy base class for all sorts of SAX-related work.

If you've ever worked with Perl's various Tie::* base classes, the idea is similar: you start out with a base class with callbacks defined that don't do anything very exciting, but by their existence satisfy all the subroutine calls triggered by SAX events. In your own driver class, you simply redefine the subroutines that should do something special and let the default behavior rule for all the events you don't care much about.

The default behavior, in this case, gives you something nice, too: access to an array of strings (stored as an instance variable on the handler object) holding the XML document that the incoming SAX events built. This isn't necessarily very interesting if your data source was XML, but if you use a PerlSAXish driver to generate an event stream out of an unsuspecting data source, then this feature is lovely. It gives you an easy way to, for instance, convert a non-XML file into its XML equivalent and save it to disk.

The trade-off is that you must remember to invoke $self->SUPER::[methodname] with all your own event handler methods. Otherwise, your class may forget its roots and fail to add things to that internal strings array in its youthful naïveté, and thus leave embarrassing holes in the generated XML document.

Previous | Home | Next
5.5. A Handler Base Class | Book Index | 5.7. XML::SAX: The Second Generation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::SAX: The Second Generation (Perl and XML)

 Perl & XMLSearch this book

5.7. XML::SAX: The Second Generation

The proliferation of SAX parsers presents two problems: how to keep them all synchronized with the standard API and how to keep them organized on your system. XML::SAX, a marvelous team effort by Matt Sergeant, Kip Hampton, and Robin Berjon, solves both problems at once. As a bonus, it also includes support for SAX Level 2 that previous modules lacked.

"What," you ask, "do you mean about keeping all the modules synchronized with the API?" All along, we've touted the wonders of using a standard like SAX to ensure that modules are really interchangeable. But here's the rub: in Perl, there's more than one way to implement SAX. SAX was originally designed for Java, which has a wonderful interface type of class that nails down things like what type of argument to pass to which method. There's nothing like that in Perl.

This wasn't as much of a problem with the older SAX modules we've been talking about so far. They all support SAX Level 1, which is fairly simple. However, a new crop of modules that support SAX2 is breaking the surface. SAX2 is more complex because it introduces namespaces to the mix. An element event handler should receive both the namespace prefix and the local name of the element. How should this information be passed in parameters? Do you keep them together in the same string like foo:bar? Or do you separate them into two parameters?

This debate created a lot of heat on the perl-xml mailing list until a few members decided to hammer out a specification for "Perlish" SAX (we'll see in a moment how to use this new API for SAX2). To encourage others to adhere to this convention, XML::SAX includes a class called XML::SAX::ParserFactory. A factory is an object whose sole purpose is to generate objects of a specific type -- in this case, parsers. XML::SAX::ParserFactory is a useful way to handle housekeeping chores related to the parsers, such as registering their options and initialization requirements. Tell the factory what kind of parser you want and it doles out a copy to you.

XML::SAX represents a shift in the way XML and Perl work together. It builds on the work of the past, including all the best features of previous modules, while avoiding many of the mistakes. To ensure that modules are truly compatible, the kit provides a base class for parsers, abstracting out most of the mundane work that all parsers have to do, leaving the developer the task of doing only what is unique to the task. It also creates an abstract interface for users of parsers, allowing them to keep the plethora of modules organized with a registry that is indexed by properties to make it easy to find the right one with a simple query. It's a bold step and carries a lot of heft, so be prepared for a lot of information and detail in this section. We think it will be worth your while.

5.7.1. XML::SAX::ParserFactory

We start with the parser selection interface, XML::SAX::ParserFactory. For those of you who have used DBI, this class is very similar. It's a front end to all the SAX parsers on your system. You simply request a new parser from the factory and it will dig one up for you. Let's say you want to use any SAX parser with your handler package XML::SAX::MyHandler.

Here's how to fetch the parser and use it to read a file:

use XML::SAX::ParserFactory;
use XML::SAX::MyHandler;
my $handler = new XML::SAX::MyHandler;
my $parser = XML::SAX::ParserFactory->parser( Handler => $handler );
$parser->parse_uri( "foo.xml" );

The parser you get depends on the order in which you've installed the modules. The last one (with all the available features specified with RequiredFeatures, if any) will be returned by default. But maybe you don't want that one. No problem; XML::SAX maintains a registry of SAX parsers that you can choose from. Every time you install a new SAX parser, it registers itself so you can call upon it with ParserFactory. If you know you have the XML::SAX::BobsParser parser installed, you can require an instance of it by setting the variable $XML::SAX::ParserPackage as follows:

use XML::SAX::ParserFactory;
use XML::SAX::MyHandler;
my $handler = new XML::SAX::MyHandler;
$XML::SAX::ParserPackage = "XML::SAX::BobsParser( 1.24 )";
my $parser = XML::SAX::ParserFactory->parser( Handler => $handler );

Setting $XML::SAX:ParserPackage to XML::SAX::BobsParser( 1.24 ) returns an instance of the package. Internally, ParserFactory is require( )-ing that parser and calling its new( ) class method. The 1.24 in the variable setting specifies a minimum version number for the parser. If that version isn't on your system, an exception will be thrown.

To see a list of all the parsers available to XML::SAX, call the parsers( ) method:

use XML::SAX;

my @parsers = @{XML::SAX->parsers( )};

foreach my $p ( @parsers ) {
    print "\n", $p->{ Name }, "\n";
    foreach my $f ( sort keys %{$p->{ Features }} ) {
        print "$f => ", $p->{ Features }->{ $f }, "\n";
    }
}

It returns a reference to a list of hashes, with each hash containing information about a parser, including the name and a hash of features. When we ran the program above we were told that XML::SAX had two registered parsers, each supporting namespaces:

XML::LibXML::SAX::Parser
http://xml.org/sax/features/namespaces => 1

XML::SAX::PurePerl
http://xml.org/sax/features/namespaces => 1

At the time this book was written, these parsers were the only two parsers included with XML::SAX. XML::LibXML::SAX::Parser is a SAX API for the libxml2 library we use in Chapter 6, "Tree Processing". To use it, you'll need to have libxml2, a compiled, dynamically linked library written in C, installed on your system. It's fast, but unless you can find a binary or compile it yourself, it isn't very portable. XML::SAX::PurePerl is, as the name suggests, a parser written completely in Perl. As such, it's completely portable because you can run it wherever Perl is installed. This starter set of parsers already gives you some different options.

The feature list associated with each parser is important because it allows a user to select a parser based on a set of criteria. For example, suppose you wanted a parser that did validation and supported namespaces. You could request one by calling the factory's require_feature( ) method:

my $factory = new XML::SAX::ParserFactory;
$factory->require_feature( 'http://xml.org/sax/features/validation' );
$factory->require_feature( 'http://xml.org/sax/features/namespaces' );
my $parser = $factory->parser( Handler => $handler );

Alternatively, you can pass such information to the factory in its constructor method:

my $factory = new XML::SAX::ParserFactory(
             Required_features => {
                    'http://xml.org/sax/features/validation' => 1
                    'http://xml.org/sax/features/namespaces' => 1
             }
);
my $parser = $factory->parser( Handler => $handler );

If multiple parsers pass the test, the most recently installed one is used. However, if the factory can't find a parser to fit your requirements, it simply throws an exception.

To add more SAX modules to the registry, you only need to download and install them. Their installer packages should know about XML::SAX and automatically register the modules with it. To add a module of your own, you can use XML::SAX's add_parser( ) with a list of module names. Make sure it follows the conventions of SAX modules by subclassing XML::SAX::Base. Later, we'll show you how to write a parser, install it, and add it to the registry.

5.7.2. SAX2 Handler Interface

Once you've selected a parser, the next step is to code up a handler package to catch the parser's event stream, much like the SAX modules we've seen so far. XML::SAX specifies events and their properties in exquisite detail and in large numbers. This specification gives your handler considerable control while ensuring absolute conformance to the API.

The types of supported event handlers fall into several groups. The ones we are most familiar with include the content handlers, including those for elements and general document information, entity resolvers, and lexical handlers that handle CDATA sections and comments. DTD handlers and declaration handlers take care of everything outside of the document element, including element and entity declarations. XML::SAX adds a new group, the error handlers, to catch and process any exceptions that may occur during parsing.

One important new facet to this class of parsers is that they recognize namespaces. This recognition is one of the innovations of SAX2. Previously, SAX parsers treated a qualified name as a single unit: a combined namespace prefix and local name. Now you can tease out the namespaces, see where their scope begins and ends, and do more than you could before.

5.7.2.1. Content event handlers

Focusing on the content of the document, these handlers are the most likely ones to be implemented in a SAX handling program. Note the useful addition of a document locator reference, which gives the handler a special window into the machinations of the parser. The support for namespaces is also new.

set_document_locator( locator )

Called at the beginning of parsing, a parser uses this method to tell the handler where the events are coming from. The locator parameter is a reference to a hash containing these properties:

PublicID

The public identifier of the current entity being parsed.

SystemID

The system identifier of the current entity being parsed.

LineNumber

The line number of the current entity being parsed.

ColumnNumber

The last position in the line currently being parsed.

The hash is continuously updated with the latest information. If your handler doesn't like the information it's being fed and decides to abort, it can check the locator to construct a meaningful message to the user about where in the source document an error was found. A SAX parser isn't required to give a locator, though it is strongly encouraged to do so. You should check to make sure that you have a locator before trying to access it. Don't try to use the locator except inside an event handler, or you'll get unpredictable results.

start_document( document )

This handler routine is called right after set_document_locator( ), just as parsing on a document begins. The parameter, document, is an empty reference, as there are no properties for this event.

end_document( document )

This is the last handler method called. If the parser has reached the end of input or has encountered an error and given up, it sends notification of this event. The return value for this method is used as the value returned by the parser's parse( ) method. Again, the document parameter is empty.

start_element( element )

Whenever the parser encounters a new element start tag, it calls this method. The parameter element is a hash containing properties of the element, including:

Name

The string containing the name of the element, including its namespace prefix.

Attributes

The hash of attributes, in which each key is encoded as {NamespaceURI}LocalName. The value of each item in the hash is a hash of attribute properties.

NamespaceURI

The element's namespace.

Prefix

The prefix part of the qualified name.

LocalName

The local part of the qualified name.

Properties for attributes include:

Name

The qualified name (prefix + local).

Value

The attribute's value, normalized (leading and trailing spaces are removed).

NamespaceURI

The source of the namespace.

Prefix

The prefix part of the qualified name.

LocalName

The local part of the qualified name.

The properties NamespaceURI, LocalName, and Prefix are given only if the parser supports the namespaces feature.

end_element( element )

After all the content is processed and an element's end tag has come into view, the parser calls this method. It is even called for empty elements. The parameter element is a hash containing these properties:

Name

The string containing the element's name, including its namespace prefix.

NamespaceURI

The element's namespace.

Prefix

The prefix part of the qualified name.

LocalName

The local part of the qualified name.

The properties NamespaceURI, LocalName, and Prefix are given only if the parser supports the namespaces feature.

characters( characters )

The parser calls this method whenever it finds a chunk of plain text (character data). It might break up a chunk into pieces and deliver each piece separately, but the pieces must always be sent in the same order as they were read. Within a piece, all text must come from the same source entity. The characters parameter is a hash containing one property, Data, which is a string containing the characters from the document.

ignorable_whitespace( characters )

The term ignorable whitespace is used to describe space characters that appear in places where the element's content model declaration doesn't specifically call for character data. In other words, the newlines often used to make XML more readable by spacing elements apart can be ignored because they aren't really content in the document. A parser can tell if whitespace is ignorable only by reading the DTD, and it would do that only if it supports the validation feature. (If you don't understand this, don't worry; it's not important to most people.) The characters parameter is a hash containing one property, Data, containing the document's whitespace characters.

start_prefix_mapping( mapping )

This method is called when the parser detects a namespace coming into scope. For parsers that are not namespace-aware, this event is skipped, but element and attribute names still include the namespace prefixes. This event always occurs before the start of the element for which the scope holds. The parameter mapping is a hash with these properties:

Prefix

The namespace prefix.

NamespaceURI

The URI that the prefix maps to.

end_prefix_mapping( mapping )

This method is called when a namespace scope closes. This routine's parameter mapping is a hash with one property:

Prefix

The namespace prefix.

This event is guaranteed to come after the end element event for the element in which the scope is declared.

processing_instruction( pi )

This routine handles processing instruction events from the parser, including those found outside the document element. The pi parameter is a hash with these properties:

Target

The target for the processing instruction.

Data

The instruction's data (or undef if there isn't any).

skipped_entity( entity )

Nonvalidating parsers may skip entities rather than resolve them. For example, if they haven't seen a declaration, they can just ignore the entity rather than abort with an error. This method gives the handler a chance to do something with the entity, and perhaps even implement its own entity resolution scheme.

If a parser skips entities, it will have one or more of these features set:

  • Handle external parameter entities (feature-ID is http://xml.org/sax/features/external-parameter-entities)

  • Handle external general entities (feature-ID is http://xml.org/sax/features/external-general-entities)

(In XML, features are represented as URIs, which may or may not actually exist. See Chapter 10, "Coding Strategies" for a fuller explanation.)

The parameter entity is a hash with this property:

Name

The name of the entity that was skipped. If it's a parameter entity, the name will be prefixed with a percent sign (%).

5.7.2.2. Entity resolver

By default, XML parsers resolve external entity references without your program ever knowing they were there. You may want to override that behavior occasionally. For example, you may have a special way of resolving public identifiers, or the entities are entries in a database. Whatever the reason, if you implement this handler, the parser will call it before attempting to resolve the entity on its own.

The argument to resolve_entity( ) is a hash with two properties: PublicID, a public identifier for the entity, and SystemID, the system-specific location of the identity, such as a filesystem path or a URI. If the public identifier is undef, then none was given, but a system identifier will always be present.

5.7.2.3. Lexical event handlers

Implementation of this group of events is optional. You probably don't need to see these events, so not all parsers will give them to you. However, a few very complete ones will. If you want to be able to duplicate the original source XML down to the very comments and CDATA sections, then you need a parser that supports these event handlers.

They include:

  • start_dtd( ) and end_dtd( ), for marking the boundaries of the document type definition

  • start_entity( ) and end_entity( ), for delineating the region of a resolved entity reference

  • start_cdata( ) and end_cdata( ), to describe the range of a CDATA section

  • comment( ), announcing a lexical comment that would otherwise be ignored by parsers

5.7.2.4. Error event handlers and catching exceptions

XML::SAX lets you customize your error handling with this group of handlers. Each handler takes one argument, called an exception, that describes the error in detail. The particular handler called represents the severity of the error, as defined by the W3C recommendation for parser behavior. There are three types:

warning( )

This is the least serious of the exception handlers. It represents any error that is not bad enough to halt parsing. For example, an ID reference without a matching ID would elicit a warning, but allow the parser to keep grinding on. If you don't implement this handler, the parser will ignore the exception and keep going.

error( )

This kind of error is considered serious, but recoverable. A validity error falls in this category. The parser should still trundle on, generating events, unless your application decides to call it quits. In the absence of a handler, the parser usually continues parsing.

fatal_error( )

A fatal error might cause the parser to abort parsing. The parser is under no obligation to continue, but might just to collect more error messages. The exception could be a syntax error that makes the document into non-well-formed XML, or it might be an entity that can't be resolved. In any case, this example shows the highest level of error reporting provided in XML::SAX.

According to the XML specification, conformant parsers are supposed to halt when they encounter any kind of well-formedness or validity error. In Perl SAX, halting results in a call to die( ). That's not the end of story, however. Even after the parse session has died, you can raise it from the grave to continue where it left off, using the eval{} construct, like this:

eval{ $parser->parse( $uri ) };
if( $@ ) {
  # yikes! handle error here...
}

The $@ variable is a blessed hash of properties that piece together the story about why parsing failed.

These properties include:

Message

A text description about what happened

ColumnNumber

The number of characters into the line where the error occurred, if this error is a parse error

LineNumber

Which line the error happened on, if the exception was thrown while parsing

PublicID

A public identifier for the entity in which the error occurred, if this error is a parse error

SystemID

A system identifier pointing to the offending entity, if a parse error occurred

Not all thrown exceptions indicate that a failure to parse occurred. Sometimes the parser throws an exception because of a bad feature setting.

5.7.3. SAX2 Parser Interface

After you've written a handler package, you need to create an instance of the parser, set its features, and run it on the XML source. This section discusses the standard interface for XML::SAX parsers.

The parse( ) method, which gets the parsing process rolling, takes a hash of options as an argument. Here you can assign handlers, set features, and define the data source to be parsed. For example, the following line sets both the handler package and the source document to parse:

$parser->parse( Handler => $handler,
                 Source => { SystemId => "data.xml" });

The Handler property sets a generic set of handlers that will be used by default. However, each class of handlers has its own assignment slot that will be checked before Handler. These settings include: ContentHandler, DTDHandler, EntityResolver, and ErrorHandler. All of these settings are optional. If you don't assign a handler, the parser will silently ignore events and handle errors in its own way.

The Source parameter is a hash used by a parser to hold all the information about the XML being input. It has the following properties:

CharacterStream

This kind of filehandle works in Perl Version 5.7.2 and higher using PerlIO. No encoding translation should be necessary. Use the read( ) function to get a number of characters from it, or use sysread( ) to get a number of bytes. If the CharacterStream property is set, the parser ignores ByteStream or SystemId.

ByteStream

This property sets a byte stream to be read. If CharacterStream is set, this property is ignored. However, it supersedes SystemId. The Encoding property should be set along with this property.

PublicId

This property is optional, but if the application submits a public identifier, it is stored here.

SystemId

This string represents a system-specific location for a document, such as a URI or filesystem path. Even if the source is a character stream or byte stream, this parameter is still useful because it can be used as an offset for external entity references.

Encoding

The character encoding, if known, is stored here.

Any other options you want to set are in the set of features defined for SAX2. For example, you can tell a parser that you are interested in special treatment for namespaces. One way to set features is by defining the Features property in the options hash given to the parse( ) method. Another way is with the method set_feature( ). For example, here's how you would turn on validation in a validating parser using both methods:

$parser->parse( Features => { 'http://xml.org/sax/properties/validate' => 1 } );
$parser->set_feature( 'http://xml.org/sax/properties/validate', 1 );

For a complete list of features defined for SAX2, see the documentation at http://sax.sourceforge.net/apidoc/org/xml/sax/package-summary.html. You can also define your own features if your parser has special abilities others don't. To see what features your parser supports, get_features( ) returns a list and get_feature( ) with a name parameter reports the setting of a specific feature.

5.7.4. Example: A Driver

Making your own SAX parser is simple, as most of the work is handled by a base class, XML::SAX::Base. All you have to do is create a subclass of this object and override anything that isn't taken care of by default. Not only is it convenient to do this, but it will result in code that is much safer and more reliable than if you tried to create it from scratch. For example, checking if the handler package implements the handler you want to call is done for you automatically.

The next example proves just how easy it is to create a parser that works with XML::SAX. It's a driver, similar to the kind we saw in Section 5.4, "Drivers for Non-XML Sources", except that instead of turning Excel documents into XML, it reads from web server log files. The parser turns a line like this from a log file:

10.16.251.137 - - [26/Mar/2000:20:30:52 -0800] "GET /index.html HTTP/1.0" 200 16171

into this snippet of XML:

<entry>
<ip>10.16.251.137<ip>
<date>26/Mar/2000:20:30:52 -0800<date>
<req>GET /apache-modlist.html HTTP/1.0<req>
<stat>200<stat>
<size>16171<size>
<entry>

Example 5-8 implements the XML::SAX driver for web logs. The first subroutine in the package is parse( ). Ordinarily, you wouldn't write your own parse( ) method because the base class does that for you, but it assumes that you want to input some form of XML, which is not the case for drivers. Thus, we shadow that routine with one of our own, specifically trained to handle web server log files.

Example 5-8. Web log SAX driver

package LogDriver;

require 5.005_62;
use strict;
use XML::SAX::Base;
our @ISA = ('XML::SAX::Base');
our $VERSION = '0.01';

sub parse {
    my $self = shift;
    my $file = shift;
    if( open( F, $file )) {
        $self->SUPER::start_element({ Name => 'server-log' });
        while( <F> ) {
            $self->_process_line( $_ );
        }
        close F;
        $self->SUPER::end_element({ Name => 'server-log' });
    }
}

sub _process_line {
    my $self = shift;
    my $line = shift;

    if( $line =~
          /(\S+)\s\S+\s\S+\s\[([^\]]+)\]\s\"([^\"]+)\"\s(\d+)\s(\d+)/ ) {
        my( $ip, $date, $req, $stat, $size ) = ( $1, $2, $3, $4, $5 );

        $self->SUPER::start_element({ Name => 'entry' });

        $self->SUPER::start_element({ Name => 'ip' });
        $self->SUPER::characters({ Data => $ip });
        $self->SUPER::end_element({ Name => 'ip' });

        $self->SUPER::start_element({ Name => 'date' });
        $self->SUPER::characters({ Data => $date });
        $self->SUPER::end_element({ Name => 'date' });

        $self->SUPER::start_element({ Name => 'req' });
        $self->SUPER::characters({ Data => $req });
        $self->SUPER::end_element({ Name => 'req' });

        $self->SUPER::start_element({ Name => 'stat' });
        $self->SUPER::characters({ Data => $stat });
        $self->SUPER::end_element({ Name => 'stat' });

        $self->SUPER::start_element({ Name => 'size' });
        $self->SUPER::characters({ Data => $size });
        $self->SUPER::end_element({ Name => 'size' });

        $self->SUPER::end_element({ Name => 'entry' });
    }
}

1;

Since web logs are line oriented (one entry per line), it makes sense to create a subroutine that handles a single line, _process_line( ). All it has to do is break down the web log entry into component parts and package them in XML elements. The parse( ) routine simply chops the document into separate lines and feeds them into the line processor one at a time.

Notice that we don't call event handlers in the handler package directly. Rather, we pass the data through routines in the base class, using it as an abstract layer between the parser and the handler. This is convenient for you, the parser developer, because you don't have to check if the handler package is listening for that type of event. Again, the base class is looking out for us, making our lives easier.

Let's test the parser now. Assuming that you have this module already installed (don't worry, we'll cover the topic of installing XML::SAX parsers in the next section), writing a program that uses it is easy. Example 5-9 creates a handler package and applies it to the parser we just developed.

Example 5-9. A program to test the SAX driver

use XML::SAX::ParserFactory;
use LogDriver;
my $handler = new MyHandler;
my $parser = XML::SAX::ParserFactory->parser( Handler => $handler );
$parser->parse( shift @ARGV );

package MyHandler;

# initialize object with options
#
sub new {
    my $class = shift;
    my $self = {@_};
    return bless( $self, $class );
}

sub start_element {
    my $self = shift;
    my $data = shift;
    print "<", $data->{Name}, ">";
    print "\n" if( $data->{Name} eq 'entry' );
    print "\n" if( $data->{Name} eq 'server-log' );
}

sub end_element {
    my $self = shift;
    my $data = shift;
    print "<", $data->{Name}, ">\n";
}

sub characters {
    my $self = shift;
    my $data = shift;
    print $data->{Data};
}

We use XML::SAX::ParserFactory to demonstrate how a parser can be selected once it is registered. If you wish, you can define attributes for the parser so that subsequent queries can select it based on those properties rather than its name.

The handler package is not terribly complicated; it turns the events into an XML character stream. Each handler receives a hash reference as an argument through which you can access each object's properties by the appropriate key. An element's name, for example, is stored under the hash key Name. It all works pretty much as you would expect.

5.7.5. Installing Your Own Parser

Our coverage of XML::SAX wouldn't be complete without showing you how to create an installation package that adds a parser to the registry automatically. Adding a parser is very easy with the h2xs utility. Though it was originally made to facilitate extensions to Perl written in C, it is invaluable in other ways.

Here, we will use it to create something much like the module installers you've downloaded from CPAN.[26]

[26]For a helpful tutorial on using h2xs, see O'Reilly's The Perl Cookbook by Tom Christiansen and Nat Torkington.

First, we start a new project with the following command:

h2xs -AX -n LogDriver

h2xs automatically creates a directory called LogDriver, stocked with several files.

LogDriver.pm

A stub for our module, ready to be filled out with subroutines.

Makefile.PL

A Perl program that generates a Makefile for installing the module. (Look familiar, CPAN users?)

test.pl

A stub for adding test code to check on the success of installation.

Changes, MANIFEST

Other files used to aid in installation and give information to users.

LogDriver.pm, the module to be installed, doesn't need much extra code to make h2xs happy. It only needs a variable, $VERSION, since h2xs is (justifiably) finicky about that information.

As you know from installing CPAN modules, the first thing you do when opening an installer archive is run the command perl Makefile.PM. Running this command generates a file called Makefile, which configures the installer to your system. Then you can run make and make install to load the module in the right place.

Any deviation from the default behavior of the installer must be coded in the Makefile.PM program. Untouched, it looks like this:

use ExtUtils::MakeMaker;
WriteMakefile(
    'NAME'                => 'LogDriver',         # module name
    'VERSION_FROM'        => 'LogDriver.pm',      # finds version
 );

The argument to WriteMakeFile( ) is a hash of properties about the module, used in generating a Makefile file. We can add more properties here to make the installer do more sophisticated things than just copy a module onto the system. For our parser, we want to add this line:

'PREREQ_PM' => { 'XML::SAX' => 0 }

Adding this line triggers a check during installation to see if XML::SAX exists on the system. If not, the installation aborts with an error message. We don't want to install our parser until there is a framework to accept it.

This subroutine should also be added to Makefile.PM:

sub MY::install {
    package MY;
    my $script = shift->SUPER::install(@_);
    $script =~ s/install :: (.*)$/install :: $1 install_sax_driver/m;
    $script .= <<"INSTALL";

    install_sax_driver :
       \t\@\$(PERL) -MXML::SAX -e "XML::SAX->add_parser(q(\$(NAME)))->save_parsers( )"

INSTALL

    return $script;
}

This example adds the parser to the list maintained by XML::SAX. Now you can install your module.

Previous | Home | Next
5.6. XML::Handler::YAWriter as a Base Handler Class | Book Index | 6. Tree Processing
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Tree Processing (Perl and XML)

 Perl & XMLSearch this book

Chapter 6. Tree Processing

Contents:

XML Trees
XML::Simple
XML::Parser's Tree Mode
XML::SimpleObject
XML::TreeBuilder
XML::Grove

Having done just about all we can do with streams, it's time to move on to another style of XML processing. Instead of letting the XML fly past the program one tiny piece at a time, we will capture the whole document in memory and then start working on it. Having an in-memory representation built behind the scenes for us makes our job much easier, although it tends to require more memory and CPU cycles.

This chapter is an overview of programming with persistent XML objects, better known as tree processing. It looks at a variety of different modules and strategies for building and accessing XML trees, including the rigorous, standard Document Object Model (DOM), fast access to internal document parts with XPath, and efficient tree processing methods.

6.1. XML Trees

Every XML document can be represented as a collection of data objects linked in an acyclic structure called a tree. Each object, or node, is a small piece of the document, such as an element, a piece of text, or a processing instruction. One node, called the root, links to other nodes, and so on down to nodes that aren't linked to anything. Graph this image out and it looks like a big, bushy tree -- hence the name.

A tree structure representing a piece of XML is a handy thing to have. Since a tree is acyclic (it has no circular links), you can use simple traversal methods that won't get stuck in infinite loops. Like a filesystem directory tree, you can represent the location of a node easily in simple shorthand. Like real trees, you can break a piece off and treat it like a smaller tree -- a tree is just a collection of subtrees joined by a root node. Best of all, you have all the information in one place and search through it like a database.

For the programmer, a tree makes life much easier. Stream processing, you will recall, remembers fleeting details to use later in constructing another data structure or printing out information. This work is tedious, and can be downright horrible for very complex documents. If you have to combine pieces of information from different parts of the document, then you might go mad. If you have a tree containing the document, though, all the details are right in front of you. You only need to write code to sift through the nodes and pull out what you need.

Of course, you don't get anything good for free. There is a penalty for having easy access to every point in a document. Building the tree in the first place takes time and precious CPU cycles, and even more if you use object-oriented method calls. There is also a memory tax to pay, since each object in the tree takes up some space. With very large documents (trees with millions of nodes are not unheard of), you could bring your poor machine down to its knees with a tree processing program. On the average, though, processing trees can get you pretty good results (especially with a little optimizing, as we show later in the chapter), so don't give up just yet.

As we talk about trees, we will frequently use genealogical terms to describe relationships between nodes. A container node is said to be the parent of the nodes it branches to, each of which may be called a child of the container node. Likewise, the terms descendant, ancestor, and sibling mean pretty much what you think they would. So two sibling nodes share the same parent node, and all nodes have the root node as their ancestor.

There are several different species of trees, depending on the implementation you're talking about. Each species models the document in a slightly different way. For example, do you consider an entity reference to be a separate node from text, or would you include the reference in the same package? You have to pay attention to the individual scheme of each module. Table 6-1 shows a common selection of node types.

Table 6-1. Typical node type definitions

Type | Properties
Element | Name, attributes, references to children
Namespace | Prefix name, URI
Character data | String of characters
Processing instruction | Target, Data
Comment | String of characters
CDATA section | String of characters
Entity reference | Name, Replacement text (or System ID and/or Public ID)

In addition to this set, some implementations define node types for the DTD, allowing a programmer to access declarations for elements, entities, notations, and attributes. Nodes may also exist for the XML declaration and document type declarations.

Previous | Home | Next
5.7. XML::SAX: The Second Generation | Book Index | 6.2. XML::Simple
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::Simple (Perl and XML)

 Perl & XMLSearch this book

6.2. XML::Simple

The simplest tree model can be found in Grant McLean's module XML::Simple. It's designed to facilitate the job of reading and saving datafiles. The programmer doesn't have to know much about XML and parsers -- only how to access arrays and hashes, the data structures used to store a document.

Example 6-1 shows a simple datafile that a program might use to store information.

Example 6-1. A program datafile

<preferences>
  <font role="default">
    <name>Times New Roman</name>
    <size>14</size>
  </font>
  <window>
    <height>352</height>
    <width>417</width>
    <locx>100</locx>
    <locy>120</locy>
  </window>
</preferences>

XML::Simple makes accessing information in the datafile remarkably easy. Example 6-2 extracts default font information from it.

Example 6-2. Program to extract font information

use XML::Simple;

my $simple = XML::Simple->new( );             # initialize the object
my $tree = $simple->XMLin( './data.xml' );   # read, store document

# test access to the tree
print "The user prefers the font " . $tree->{ font }->{ name } . " at " .
    $tree->{ font }->{ size } . " points.\n";

First we initialize an XML::Simple object, then we trigger the parser with a call to its XMLin( ) method. This step returns a reference to the root of the tree, which is a hierarchical set of hashes. Element names provide keys to the hashes, whose values are either strings or references to other element hashes. Thus, we have a clear and concise way to access points deep in the document.

To illustrate this idea, let's look at the data structure, using Data::Dumper, a module that serializes data structures. Just add these lines at the end of the program:

use Data::Dumper;
print Dumper( $tree );

And here's the output:

$tree = {
          'font' => {
                      'size' => '14',
                      'name' => 'Times New Roman',
                      'role' => 'default'
                    },
          'window' => {
                        'locx' => '100',
                        'locy' => '120',
                        'height' => '352',
                        'width' => '417'
                      }
        };

The $tree variable represents the root element of the tree, <preferences>. Each entry in the hash it points to represents its child elements, <font> and <window>, accessible by their types. The entries point to hashes representing the third tier of elements. Finally, the values of these hash items are strings, the text found in the actual elements from the file. The whole document is accessible with a simple string of hash references.

This example was not very complex. Much of the success of XML::Simple's interface is that it relies on the XML to be simple. Looking back at our datafile, you'll note that no sibling elements have the same name. Identical names would be impossible to encode with hashes alone.

Fortunately, XML::Simple has an answer. If an element has two or more child elements with the same name, it uses a list to contain all the like-named children in a group. Consider the revised datafile in Example 6-3.

Example 6-3. A trickier program datafile

<preferences>
  <font role="console">
    <size>9</size>
    <fname>Courier</fname>
  </font>
  <font role="default">
    <fname>Times New Roman</fname>
    <size>14</size>
  </font>
  <font role="titles">
    <size>10</size>
    <fname>Helvetica</fname>
  </font>
</preferences>

We've thrown XML::Simple a curve ball. There are now three <font> elements in a row. How will XML::Simple encode that? Dumping the data structure gives us this output:

$tree = {
          'font' => [
                      {
                        'fname' => 'Courier',
                        'size' => '9',
                        'role' => 'console'
                      },
                      {
                        'fname' => 'Times New Roman',
                        'size' => '14',
                        'role' => 'default'
                      },
                      {
                        'fname' => 'Helvetica',
                        'size' => '10',
                        'role' => 'titles'
                      }
                    ]
        };

Now the font entry's value is a reference to a list of hashes, each modeling one of the <font> elements. To select a font, you must iterate through the list until you find the one you want. This iteration clearly takes care of the like-named sibling problem.

This new datafile also adds attributes to some elements. These attributes have been incorporated into the structure as if they were child elements of their host elements. Name clashes between attributes and child elements are possible, but this potential problem is resolved the same way as like-named sibling elements. It's convenient this way, as long as you don't mind if elements and attributes are treated the same.

We know how to input XML documents to our program, but what about writing files? XML::Simple also has a method that outputs XML documents, XML_Out( ). You can either modify an existing structure or create a new document from scratch by building a data structure like the ones listed above and then passing it to the XML_Out( ) method.

Our conclusion? XML::Simple works well with simple XML documents, but runs into trouble with more complex markup. It can't handle elements with both text and elements as children (mixed content). It doesn't pay attention to node types other than elements, attributes, and text (like processing instructions or CDATA sections). Because hashes don't preserve the order of items, the sequence of elements may be scrambled. If none of these problems matters to you, then use XML::Simple. It will serve your needs well, minimizing the pain of XML markup and keeping your data accessible.

Previous | Home | Next
6. Tree Processing | Book Index | 6.3. XML::Parser's Tree Mode
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::Parser's Tree Mode (Perl and XML)

 Perl & XMLSearch this book

6.3. XML::Parser's Tree Mode

We used XML::Parser in Chapter 4, "Event Streams" as an event generator to drive stream processing programs, but did you know that this same module can also generate tree data structures? We've modified our preference-reader program to use XML::Parser for parsing and building a tree, as shown in Example 6-4.

Example 6-4. Using XML::Parser to build a tree

# initialize parser and read the file
use XML::Parser;
$parser = new XML::Parser( Style => 'Tree' );
my $tree = $parser->parsefile( shift @ARGV );

# dump the structure
use Data::Dumper;
print Dumper( $tree );

When run on the file in Example 6-4, it gives this output:

$tree = [
          'preferences', [
            {}, 0, '\n',
            'font', [
              { 'role' => 'console' }, 0, '\n',
              'size', [ {}, 0, '9' ], 0, '\n',
              'fname', [ {}, 0, 'Courier' ], 0, '\n'
            ], 0, '\n',
            'font', [
              { 'role' => 'default' }, 0, '\n',
              'fname', [ {}, 0, 'Times New Roman' ], 0, '\n',
              'size', [ {}, 0, '14' ], 0, '\n'
            ], 0, '\n',
            'font', [
               { 'role' => 'titles' }, 0, '\n',
               'size', [ {}, 0, '10' ], 0, '\n',
               'fname', [ {}, 0, 'Helvetica' ], 0, '\n',
            ], 0, '\n',
          ]
        ];

This structure is more complicated than the one we got from XML::Simple; it tries to preserve everything, including node type, order of nodes, and mixed text. Each node is represented by one or two items in a list. Elements require two items: the element name followed by a list of its contents. Text nodes are encoded as the number 0 followed by their values in a string. All attributes for an element are stored in a hash as the first item in the element's content list. Even the whitespace between elements has been saved, represented as 0, \n. Because lists are used to contain element content, the order of nodes is preserved. This order is important for some XML documents, such as books or animations in which elements follow a sequence.

XML::Parser cannot output XML from this data structure like XML::Simple can. For a complete, bidirectional solution, you should try something object oriented.

Previous | Home | Next
6.2. XML::Simple | Book Index | 6.4. XML::SimpleObject
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::SimpleObject (Perl and XML)

 Perl & XMLSearch this book

6.4. XML::SimpleObject

Using built-in data types is fine, but as your code becomes more complex and hard to read, you may start to pine for the neater interfaces of objects. Doing things like testing a node's type, getting the last child of an element, or changing the representation of data without breaking the rest of the program is easier with objects. It's not surprising that there are more object-oriented modules for XML than you can shake a stick at.

Dan Brian's XML::SimpleObject starts the tour of object models for XML trees. It takes the structure returned by XML::Parser in tree mode and changes it from a hierarchy of lists into a hierarchy of objects. Each object represents an element and provides methods to access its children. As with XML::Simple, elements are accessed by their names, passed as arguments to the methods.

Let's see how useful this module is. Example 6-5 is a silly datafile representing a genealogical tree. We're going to write a program to parse this file into an object tree and then traverse the tree to print out a text description.

Example 6-5. A genealogical tree

<ancestry>
  <ancestor><name>Glook the Magnificent</name>
    <children>
      <ancestor><name>Glimshaw the Brave</name></ancestor>
      <ancestor><name>Gelbar the Strong</name></ancestor>
      <ancestor><name>Glurko the Healthy</name>
        <children>
          <ancestor><name>Glurff the Sturdy</name></ancestor>
          <ancestor><name>Glug the Strange</name>
            <children>
              <ancestor><name>Blug the Insane</name></ancestor>
              <ancestor><name>Flug the Disturbed</name></ancestor>
            </children>
          </ancestor>
        </children>
      </ancestor>
    </children>
  </ancestor>
</ancestry>

Example 6-6 is our program. It starts by parsing the file with XML::Parser in tree mode and passing the result to an XML::SimpleObject constructor. Next, we write a routine begat( ) to traverse the tree and output text recursively. At each ancestor, it prints the name. If there are progeny, which we find out by testing whether the child method returns a non-undef value, it descends the tree to process them too.

Example 6-6. An XML::SimpleObject program

use XML::Parser;
use XML::SimpleObject;

# parse the data file and build a tree object
my $file = shift @ARGV;
my $parser = XML::Parser->new( ErrorContext => 2, Style => "Tree" );
my $tree = XML::SimpleObject->new( $parser->parsefile( $file ));

# output a text description
print "My ancestry starts with ";
begat( $tree->child( 'ancestry' )->child( 'ancestor' ), '' );

# describe a generation of ancestry
sub begat {
    my( $anc, $indent ) = @_;

    # output the ancestor's name
    print $indent . $anc->child( 'name' )->value;

    # if there are children, recurse over them
    if( $anc->child( 'children' ) and $anc->child( 'children' )->children ) {
        print " who begat...\n";
        my @children = $anc->child( 'children' )->children;
        foreach my $child ( @children ) {
            begat( $child, $indent . '   ' );
        }
    } else {
        print "\n";
    }
}

To prove it works, here's the output. In the program, we added indentation to show the descent through generations:

My ancestry starts with Glook the Magnificent who begat...
   Glimshaw the Brave
   Gelbar the Strong
   Glurko the Healthy who begat...
      Glurff the Sturdy
      Glug the Strange who begat...
         Blug the Insane
         Flug the Disturbed

We used several different methods to access data in objects. child( ) returns a reference to an XML::SimpleObject object that represents a child of the source node. children( ) returns a list of such references. value( ) looks for a character data node inside the source node and returns a scalar value. Passing arguments in these methods restricts the search to just a few matching nodes. For example, child( 'name' ) specifies the <name> element among a set of children. If the search fails, the value undef is given.

This is a good start, but as its name suggests, it may be a little too simple for some applications. There are limited ways to access nodes, mostly by getting a child or list of children. Accessing elements by name doesn't work when more than one element has the same name.

Unfortunately, this module's objects lack a way to get XML back out, so outputting a document from this structure is not easy. However, for simplicity, this module is an easy OO solution to learn and use.

Previous | Home | Next
6.3. XML::Parser's Tree Mode | Book Index | 6.5. XML::TreeBuilder
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::TreeBuilder (Perl and XML)

 Perl & XMLSearch this book

6.5. XML::TreeBuilder

XML::TreeBuilder is a factory class that builds a tree of XML::Element objects. The XML::Element class inherits from the older HTML::Element class that comes with the HTML::Tree package. Thus, you can build the tree from a file with XML::TreeBuilder and use the XML::Element accessor methods to move around, grab data from the tree, and change the structure of the tree as needed. We're going to focus on that last thing: using accessor methods to assemble a tree of our own.

For example, we're going to write a program that manages a simple, prioritized "to-do" list that uses an XML datafile to store entries. Each item in the list has an "immediate" or "long-term" priority. The program will initialize the list if it's empty or the file is missing. The user can add items by using -i or -l (for "immediate" or "long-term," respectively), followed by a description. Finally, the program updates the datafile and prints it out on the screen.

The first part of the program, listed in Example 6-7, sets up the tree structure. If the datafile can be found, it is read and used to build the tree. Otherwise, the tree is built from scratch.

Example 6-7. To-do list manager, first part

use XML::TreeBuilder;
use XML::Element;
use Getopt::Std;

# command line options
# -i         immediate
# -l         long-term
#
my %opts;
getopts( 'il', \%opts );

# initialize tree
my $data = 'data.xml';
my $tree;

# if file exists, parse it and build the tree
if( -r $data ) {
    $tree = XML::TreeBuilder->new( );
    $tree->parse_file($data);

# otherwise, create a new tree from scratch
} else {
    print "Creating new data file.\n";
    my @now = localtime;
    my $date = $now[4] . '/' . $now[3];
    $tree = XML::Element->new( 'todo-list', 'date' => $date );
    $tree->push_content( XML::Element->new( 'immediate' ));
    $tree->push_content( XML::Element->new( 'long-term' ));
}

A few notes on initializing the structure are necessary. The minimal structure of the datafile is this:

<todo-list date="DATE">
  <immediate></immediate>
  <long-term></long-term>
</todo-list>

As long as the <immediate> and <long-term> elements are present, we have somewhere to put schedule items. Thus, we need to create three elements using the XML::Element constructor method new( ), which uses its argument to set the name of the element. The first call of this method also includes an argument 'date' => $date to create an attribute named "date." After creating element nodes, we have to connect them. The push_content( ) method adds a node to an element's content list.

The next part of the program updates the datafile, adding a new item if the user supplies one. Where to put the item depends on the option used (-i or -l). We use the as_XML method to output XML, as shown in Example 6-8.

Example 6-8. To-do list manager, second part

# add new entry and update file
if( %opts ) {
    my $item = XML::Element->new( 'item' );
    $item->push_content( shift @ARGV );
    my $place;
    if( $opts{ 'i' }) {
        $place = $tree->find_by_tag_name( 'immediate' );
    } elsif( $opts{ 'l' }) {
        $place = $tree->find_by_tag_name( 'long-term' );
    }
    $place->push_content( $item );
}
open( F, ">$data" ) or die( "Couldn't update schedule" );
print F $tree->as_XML;
close F;

Finally, the program outputs the current schedule to the terminal. We use the find_by_tag_name( ) method to descend from an element to a child with a given tag name. If more than one element match, they are supplied in a list. Two methods retrieve the contents of an element: attr_get_i( ) for attributes and as_text( ) for character data. Example 6-9 has the rest of the code.

Example 6-9. To-do list manager, third part

# output schedule
print "To-do list for " . $tree->attr_get_i( 'date' ) . ":\n";
print "\nDo right away:\n";
my $immediate = $tree->find_by_tag_name( 'immediate' );
my $count = 1;
foreach my $item ( $immediate->find_by_tag_name( 'item' )) {
    print $count++ . '. ' . $item->as_text . "\n";
}
print "\nDo whenever:\n";
my $longterm = $tree->find_by_tag_name( 'long-term' );
$count = 1;
foreach my $item ( $longterm->find_by_tag_name( 'item' )) {
    print $count++ . '. ' . $item->as_text . "\n";
}

To test the code, we created this datafile with several calls to the program (whitespace was added to make it more readable):

<todo-list date="7/3">
  <immediate>
    <item>take goldfish to the vet</item>
    <item>get appendix removed</item>
  </immediate>
  <long-term>
    <item>climb K-2</item>
    <item>decipher alien messages</item>
  </long-term>
</todo-list>

The output to the screen was this:

To-do list for 7/3:

Do right away:
1. take goldfish to the vet
2. get appendix removed

Do whenever:
1. climb K-2
2. decipher alien messages
Previous | Home | Next
6.4. XML::SimpleObject | Book Index | 6.6. XML::Grove
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::Grove (Perl and XML)

 Perl & XMLSearch this book

6.6. XML::Grove

The last object model we'll examine before jumping into standards-based solutions is Ken MacLeod's XML::Grove. Like XML::SimpleObject, it takes the XML::Parser output in tree mode and changes it into an object hierarchy. The difference is that each node type is represented by a different class. Therefore, an element would be mapped to XML::Grove::Element, a processing instruction to XML::Grove::PI, and so on. Text nodes are still scalar values.

Another feature of this module is that the declarations in the internal subset are captured in lists accessible through the XML::Grove object. Every entity or notation declaration is available for your perusal. For example, the following program counts the distribution of elements and other nodes, and then prints a list of node types and their frequency.

First, we initialize the parser with the style "grove" (to tell XML::Parser that it needs to use XML::Parser::Grove to process its output):

use XML::Parser;
use XML::Parser::Grove;
use XML::Grove;

my $parser = XML::Parser->new( Style => 'grove', NoExpand => '1' );
my $grove = $parser->parsefile( shift @ARGV );

Next, we access the contents of the grove by calling the contents( ) method. This method returns a list including the root element and any comments or PIs outside of it. A subroutine called tabulate( ) counts nodes and descends recursively through the tree. Finally, the results are printed:

# tabulate elements and other nodes
my %dist;
foreach( @{$grove->contents} ) {
  &tabulate( $_, \%dist );
}
print "\nNODES:\n\n";
foreach( sort keys %dist ) {
  print "$_: " . $dist{$_} . "\n";
}

Here is the subroutine that handles each node in the tree. Since each node is a different class, we can use ref( ) to get the type. Attributes are not treated as nodes in this model, but are available through the element class's method attributes( ) as a hash. The call to contents( ) allows the routine to continue processing the element's children:

# given a node and a table, find out what the node is, add to the count,
# and recurse if necessary
#
sub tabulate {
  my( $node, $table ) = @_;

  my $type = ref( $node );
  if( $type eq 'XML::Grove::Element' ) {
    $table->{ 'element' }++;
    $table->{ 'element (' . $node->name . ')' }++;
    foreach( keys %{$node->attributes} ) {
      $table->{ "attribute ($_)" }++;
    }
    foreach( @{$node->contents} ) {
      &tabulate( $_, $table );
    }

  } elsif( $type eq 'XML::Grove::Entity' ) {
    $table->{ 'entity-ref (' . $node->name . ')' }++;

  } elsif( $type eq 'XML::Grove::PI' ) {
    $table->{ 'PI (' . $node->target . ')' }++;

  } elsif( $type eq 'XML::Grove::Comment' ) {
    $table->{ 'comment' }++;

  } else {
    $table->{ 'text-node' }++
  }
}

Here's a typical result, when run on an XML datafile:

NODES:
PI (a): 1
attribute (date): 1
attribute (style): 12
attribute (type): 2
element: 30
element (category): 2
element (inventory): 1
element (item): 6
element (location): 6
element (name): 12
element (note): 3
text-node: 100
Previous | Home | Next
6.5. XML::TreeBuilder | Book Index | 7. DOM
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DOM (Perl and XML)

 Perl & XMLSearch this book

Chapter 7. DOM

Contents:

DOM and Perl
DOM Class Interface Reference
XML::DOM
XML::LibXML

In this chapter, we return to standard APIs with the Document Object Model (DOM). In Chapter 5, "SAX", we talked about the benefits of using standard APIs: increased compatibility with other software components and (if implemented correctly) a guaranteed complete solution. The same concept applies in this chapter: what SAX does for event streams, DOM does for tree processing.

7.1. DOM and Perl

DOM is a recommendation by the World Wide Web Consortium (W3C). Designed to be a language-neutral interface to an in-memory representation of an XML document, versions of DOM are available in Java, ECMAscript,[27] Perl, and other languages. Perl alone has several implementations of DOM, including XML::DOM and XML::LibXML.

[27]A standards-friendly language patterned after JavaScript.

While SAX defines an interface of handler methods, the DOM specification calls for a number of classes, each with an interface of methods that affect a particular type of XML markup. Thus, every object instance manages a portion of the document tree, providing accessor methods to add, remove, or modify nodes and data. These objects are typically created by a factory object, making it a little easier for programmers who only have to initialize the factory object themselves.

In DOM, every piece of XML (the element, text, comment, etc.) is a node represented by a Node object. The Node class is extended by more specific classes that represent the types of XML markup, including Element, Attr (attribute), ProcessingInstruction, Comment, EntityReference, Text, CDATASection, and Document. These classes are the building blocks of every XML tree in DOM.

The standard also calls for a couple of classes that serve as containers for nodes, convenient for shuttling XML fragments from place to place. These classes are NodeList, an ordered list of nodes, like all the children of an element; and NamedNodeMap, an unordered set of nodes. These objects are frequently required as arguments or given as return values from methods. Note that these objects are all live, meaning that any changes done to them will immediately affect the nodes in the document itself, rather than a copy.

When naming these classes and their methods, DOM merely specifies the outward appearance of an implementation, but leaves the internal specifics up to the developer. Particulars like memory management, data structures, and algorithms are not addressed at all, as those issues may vary among programming languages and the needs of users. This is like describing a key so a locksmith can make a lock that it will fit into; you know the key will unlock the door, but you have no idea how it really works. Specifically, the outward appearance makes it easy to write extensions to legacy modules so they can comply with the standard, but it does not guarantee efficiency or speed.

DOM is a very large standard, and you will find that implementations vary in their level of compliance. To make things worse, the standard has not one, but two (soon to be three) levels. DOM1 has been around since 1998, DOM2 emerged more recently, and they're already working on a third. The main difference between Levels 1 and 2 is that the latter adds support for namespaces. If you aren't concerned about namespaces, then DOM1 should be suitable for your needs.

Previous | Home | Next
6.6. XML::Grove | Book Index | 7.2. DOM Class Interface Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DOM Class Interface Reference (Perl and XML)

 Perl & XMLSearch this book

7.2. DOM Class Interface Reference

Since DOM is becoming the interface of choice in the Perl-XML world, it deserves more elaboration. The following sections describe class interfaces individually, listing their properties, methods, and intended purposes.

WARNING: The DOM specification calls for UTF-16 as the standard encoding. However, most Perl implementations assume a UTF-8 encoding. Due to limitations in Perl, working with characters of lengths other than 8 bits is difficult. This will change in a future version, and encodings like UTF-16 will be supported more readily.

7.2.1. Document

The Document class controls the overall document, creating new objects when requested and maintaining high-level information such as references to the document type declaration and the root element.

7.2.1.1. Properties

doctype

Document Type Declaration (DTD).

documentElement

The root element of the document.

7.2.1.2. Methods

createElement, createTextNode, createComment, createCDATASection, createProcessingInstruction, createAttribute, createEntityReference

Generates a new node object.

createElementNS, createAttributeNS (DOM2 only)

Generates a new element or attribute node object with a specified namespace qualifier.

createDocumentFragment

Creates a container object for a document's subtree.

getElementsByTagName

Returns a NodeList of all elements having a given tag name at any level of the document.

getElementsByTagNameNS (DOM2 only)

Returns a NodeList of all elements having a given namespace qualifier and local name. The asterisk character (*) matches any element or any namespace, allowing you to find all elements in a given namespace.

getElementById (DOM2 only)

Returns a reference to the node that has a specified ID attribute.

importNode (DOM2 only)

Creates a new node that is the copy of a node from another document. Acts like a "copy to the clipboard" operation for importing markup.

7.2.2. DocumentFragment

The DocumentFragment class is used to contain a document fragment. Its children are (zero or more) nodes representing the tops of XML trees. This class contrasts with Document, which has at most one child element, the document root, plus metadata like the document type. In this respect, DocumentFragment's content is not well-formed, though it must obey the XML well-formed rules in all other respects (no illegal characters in text, etc.)

No specific methods or properties are defined; use the generic node methods to access data.

7.2.3. DocumentType

This class contains all the information contained in the document type declaration at the beginning of the document, except the specifics about an external DTD. Thus, it names the root element and any declared entities or notations in the internal subset.

No specific methods are defined for this class, but the properties are public (but read-only).

7.2.3.1. Properties

name

The name of the root element.

entities

A NamedNodeMap of entity declarations.

notation

A NamedNodeMap of notation declarations.

internalSubset (DOM2 only)

The internal subset of the DTD represented as a string.

publicId (DOM2 only)

The external subset of the DTD's public identifier.

systemId (DOM2 only)

The external subset of the DTD's system identifier.

7.2.4. Node

All node types inherit from the class Node. Any properties or methods common to all node types can be accessed through this class. A few properties, such as the value of the node, are undefined for some node types, like Element. The generic methods of this class are useful in some programming contexts, such as when writing code that processes nodes of different types. At other times, you'll know in advance what type you're working with, and you should use the specific class's methods instead.

All properties but nodeValue and prefix are read-only.

7.2.4.1. Properties

nodeName

A property that is defined for elements, attributes, and entities. In the context of elements this property would be the tag's name.

nodeValue

A property defined for attributes, text nodes, CDATA nodes, PIs, and comments.

nodeType

One of the following types of nodes: Element, Attr, Text, CDATASection, EntityReference, Entity, ProcessingInstruction, Comment, Document, DocumentType, DocumentFragment, or Notation.

parentNode

A reference to the parent of this node.

childNodes

An ordered list of references to children of this node (if any).

firstChild, lastChild

References to the first and last of the node's children (if any).

previousSibling, nextSibling

The node immediately preceding or following this one, respectively.

attributes

An unordered list (NamedNodeMap) of nodes that are attributes of this one (if any).

ownerDocument

A reference to the object containing the whole document -- useful when you need to generate a new node.

namespaceURI (DOM2 only)

A namespace URI if this node has a namespace prefix; otherwise it is null.

prefix (DOM2 only)

The namespace prefix associated with this node.

7.2.4.2. Methods

insertBefore

Inserts a node before a reference child element.

replaceChild

Swaps a child node with a new one you supply, giving you the old one in return.

appendChild

Adds a new node to the end of this node's list of children.

hasChildNodes

True if there are children of this node; otherwise, it is false.

cloneNode

Returns a duplicate copy of this node. It provides an alternate way to generate nodes. All properties will be identical except for parentNode, which will be undefined, and childNodes, which will be empty. Cloned elements will all have the same attributes as the original. If the argument deep is set to true, then the node and all its descendants will be copied.

hasAttributes (DOM2 only)

Returns true if this node has defined attributes.

isSupported (DOM2 only)

Returns true if this implementation supports a specific feature.

7.2.5. NodeList

This class is a container for an ordered list of nodes. It is "live," meaning that any changes to the nodes it references will appear in the document immediately.

7.2.5.1. Properties

length

Returns an integer indicating the number of nodes in the list.

7.2.5.2. Methods

item

Given an integer value n, returns a reference to the nth node in the list, starting at zero.

7.2.6. NamedNodeMap

This unordered set of nodes is designed to allow access to nodes by name. An alternate access by index is also provided for enumerations, but no order is implied.

7.2.6.1. Properties

length

Returns an integer indicating the number of nodes in the list.

7.2.6.2. Methods

getNamedItem, setNamedItem

Retrieves or adds a node using the node's nodeName property as the key.

removeNamedItem

Takes a node with the specified name out of the set and returns it.

item

Given an integer value n, returns a reference to the nth node in the set. Note that this method does not imply any order and is provided only for unique enumeration.

getNamedItemNS (DOM2 only)

Retrieves a node based on a namespace-qualified name (a namespace prefix and local name).

removeNamedItemNS (DOM2 only)

Takes an item out of the list and returns it, based on its namespace-qualified name.

setNamedItemNS (DOM2 only)

Adds a node to the list using its namespace-qualified name.

7.2.7. CharacterData

This class extends Node to facilitate access to certain types of nodes that contain character data, such as Text, CDATASection, Comment, and ProcessingInstruction. Specific classes like Text inherit from this class.

7.2.7.1. Properties

data

The character data itself.

length

The number of characters in the data.

7.2.7.2. Methods

appendData

Appends a string of character data to the end of the data property.

substringData

Extracts and returns a segment of the data property from offset to offset + count.

insertData

Inserts a string inside the data property at the location given by offset.

deleteData

Sets the data property to an empty string.

replaceData

Changes the contents of data property with a new string that you provide.

7.2.8. Element

This is the most common type of node you will encounter. An element can contain other nodes and has attribute nodes.

7.2.8.1. Properties

tagname

The name of the element.

7.2.8.2. Methods

getAttribute, getAttributeNode

Returns the value of an attribute, or a reference to the attribute node, with a given name.

setAttribute, setAttributeNode

Adds a new attribute to the element's list or replaces an existing attribute of the same name.

removeAttribute, removeAttributeNode

Returns the value of an attribute and removes it from the element's list.

getElementsByTagName

Returns a NodeList of descendant elements who match a name.

normalize

Collapses adjacent text nodes. You should use this method whenever you add new text nodes to ensure that the structure of the document remains the same, without erroneous extra children.

getAttributeNS (DOM2 only)

Retrieves an attribute value based on its qualified name (the namespace prefix plus the local name).

getAttributeNodeNS (DOM2 only)

Gets an attribute's node by using its qualified name.

getElementsByTagNamesNS (DOM2 only)

Returns a NodeList of elements among this element's descendants that match a qualified name.

hasAttribute (DOM2 only)

Returns true if this element has an attribute with a given name.

hasAttributeNS (DOM2 only)

Returns true if this element has an attribute with a given qualified name.

removeAttributeNS (DOM2 only)

Removes and returns an attribute node from this element's list, based on its namespace-qualified name.

setAttributeNS (DOM2 only)

Adds a new attribute to the element's list, given a namespace-qualified name and a value.

setAttributeNodeNS (DOM2 only)

Adds a new attribute node to the element's list with a namespace-qualified name.

7.2.9. Attr

7.2.9.1. Properties

name

The attribute's name.

specified

If the program or the document explicitly set the attribute, this property is true. If it was set in the DTD as a default and not reset anywhere else, then it will be false.

value

The attribute's value, represented as a text node.

ownerElement (DOM2 only)

The element to which this attribute belongs.

7.2.10. Text

7.2.10.1. Methods

splitText

Breaks the text node into two adjacent text nodes, each with part of the original text content. Content in the first node is from the beginning of the original up to, but not including, a character whose position is given by offset. The second node has the rest of the original node's content. This method is useful for inserting a new element inside a span of text.

7.2.11. CDATASection

CDATA Section is like a text node, but protects its contents from being parsed. It may contain markup characters (<, &) that would be illegal in text nodes. Use generic Node methods to access data.

7.2.12. ProcessingInstruction

7.2.12.1. Properties

target

The target value for the node.

data

The data value for the node.

7.2.13. Comment

This is a class representing comment nodes. Use the generic Node methods to access the data.

7.2.14. EntityReference

This is a reference to an entity defined by an Entity node. Sometimes the parser will be configured to resolve all entity references into their values for you. If that option is disabled, the parser should create this node. No explicit methods force resolution, but some actions to the node may have that side effect.

7.2.15. Entity

This class provides access to an entity in the document, based on information in an entity declaration in the DTD.

7.2.15.1. Properties

publicId

A public identifier for the resource (if the entity is external to the document).

systemId

A system identifier for the resource (if the entity is external to the document).

notationName

If the entity is unparsed, its notation reference is listed here.

7.2.16. Notation

Notation represents a notation declaration appearing in the DTD.

7.2.16.1. Properties

publicId

A public identifier for the notation.

systemId

A system identifier for the notation.

Previous | Home | Next
7. DOM | Book Index | 7.3. XML::DOM
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::DOM (Perl and XML)

 Perl & XMLSearch this book

7.3. XML::DOM

Enno Derkson's XML::DOM module is a good place to start exploring DOM in Perl. It's a complete implementation of Level 1 DOM with a few extra features thrown in for convenience. XML::DOM::Parser extends XML::Parser to build a document tree installed in an XML::DOM::Document object whose reference it returns. This reference gives you complete access to the tree. The rest, we happily report, works pretty much as you'd expect.

Here's a program that uses DOM to process an XHTML file. It looks inside <p> elements for the word "monkeys," replacing every instance with a link to monkeystuff.com. Sure, you could do it with a regular expression substitution, but this example is valuable because it shows how to search for and create new nodes, and read and change values, all in the unique DOM style.

The first part of the program creates a parser object and gives it a file to parse with the call to parsefile( ):

use XML::DOM;

&process_file( shift @ARGV );

sub process_file {
    my $infile = shift;
    my $dom_parser = new XML::DOM::Parser;            # create a parser object
    my $doc = $dom_parser->parsefile( $infile );      # make it parse a file
    &add_links( $doc );                               # perform our changes
    print $doc->toString;                             # output the tree again
    $doc->dispose;                                    # clean up memory
}

This method returns a reference to an XML::DOM::Document object, which is our gateway to the nodes inside. We pass this reference along to a routine called add_links( ), which will do all the processing we require. Finally, we output the tree with a call to toString( ), and then dispose of the object. This last step performs necessary cleanup in case any circular references between nodes could result in a memory leak.

The next part burrows into the tree to start processing paragraphs:

sub add_links {
    my $doc = shift;

    # find all the <p> elements
    my $paras = $doc->getElementsByTagName( "p" );
    for( my $i = 0; $i < $paras->getLength; $i++ ) {
        my $para = $paras->item( $i );

        # for each child of a <p>, if it is a text node, process it
        my @children = $para->getChildNodes;
        foreach my $node ( @children ) {
            &fix_text( $node ) if( $node->getNodeType eq TEXT_NODE );
        }
    }
}

The add_links( ) routine starts with a call to the document object's getElementsByTagName( ) method. It returns an XML::DOM::NodeList object containing all matching <p>s in the document (multilevel searching is so convenient) from which we can select nodes by index using item( ).

The bit we're interested in will be hiding inside a text node inside the <p> element, so we have to iterate over the children to find text nodes and process them. The call to getChildNodes( ) gives us several child nodes, either in a generic Perl list (when called in an array context) or another XML::DOM::NodeList object; for variety's sake, we've selected the first option. For each node, we test its type with a call to getNodeType and compare the result to XML::DOM's constant for text nodes, provided by TEXT_NODE( ). Nodes that pass the test are sent off to a routine for some node massaging.

The last part of the program targets text nodes and splits them around the word "monkeys" to create a link:

sub fix_text {
    my $node = shift;
    my $text = $node->getNodeValue;
    if( $text =~ /(monkeys)/i ) {

        # split the text node into 2 text nodes around the monkey word
        my( $pre, $orig, $post ) = ( $`, $1, $' );
        my $tnode = $node->getOwnerDocument->createTextNode( $pre );
        $node->getParentNode->insertBefore( $tnode, $node );
        $node->setNodeValue( $post );

        # insert an <a> element between the two nodes
        my $link = $node->getOwnerDocument->createElement( 'a' );
        $link->setAttribute( 'href', 'http://www.monkeystuff.com/' );
        $tnode = $node->getOwnerDocument->createTextNode( $orig );
        $link->appendChild( $tnode );
        $node->getParentNode->insertBefore( $link, $node );

        # recurse on the rest of the text node
        # in case the word appears again
        fix_text( $node );
    }
}

First, the routine grabs the node's text value by calling its getNodeValue( ) method. DOM specifies redundant accessor methods used to get and set values or names, either through the generic Node class or through the more specific class's methods. Instead of getNodeValue( ), we could have used getData( ), which is specific to the text node class. For some nodes, such as elements, there is no defined value, so the generic getNodeValue( ) method would return an undefined value.

Next, we slice the node in two. We do this by creating a new text node and inserting it before the existing one. After we set the text values of each node, the first will contain everything before the word "monkeys", and the other will have everything after the word. Note the use of the XML::DOM::Document object as a factory to create the new text node. This DOM feature takes care of many administrative tasks behind the scenes, making the genesis of new nodes painless.

After that step, we create an <a> element and insert it between the text nodes. Like all good links, it needs a place to put the URL, so we set it up with an href attribute. To have something to click on, the link needs text, so we create a text node with the word "monkeys" and append it to the element's child list. Then the routine will recurse on the text node after the link in case there are more instances of "monkeys" to process.

Does it work? Running the program on this file:

<html>
<head><title>Why I like Monkeys</title></head>
<body><h1>Why I like Monkeys</h1>
<h2>Monkeys are Cute</h2>
<p>Monkeys are <b>cute</b>. They are like small, hyper versions of
ourselves. They can make funny facial expressions and stick out their
tongues.</p>
</body>
</html>

produces this output:

<html>
<head><title>Why I like Monkeys</title></head>
<body><h1>Why I like Monkeys</h1>
<h2>Monkeys are Cute</h2>
<p><a href="http://www.monkeystuff.com/">Monkeys</a>
are <b>cute</b>. They are like small, hyper versions of
ourselves. They can make funny facial expressions and stick out their
tongues.</p>
</body>
</html>
Previous | Home | Next
7.2. DOM Class Interface Reference | Book Index | 7.4. XML::LibXML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::LibXML (Perl and XML)

 Perl & XMLSearch this book

7.4. XML::LibXML

Matt Sergeant's XML::LibXML module is an interface to the GNOME project's LibXML library. It's quickly becoming a popular implementation of DOM, demonstrating speed and completeness over the older XML::Parser based modules. It also implements Level 2 DOM, which means it has support for namespaces.

So far, we haven't worked much with namespaces. A lot of people opt to avoid them. They add a new level of complexity to markup and code, since you have to handle both local names and prefixes. However, namespaces are becoming more important in XML, and sooner or later, we all will have to deal with them. The popular transformation language XSLT uses namespaces to distinguish between tags that are instructions and tags that are data (i.e., which elements should be output and which should be used to control the output).

You'll even see namespaces used in good old HTML. Namespaces provide a way to import specialized markup into documents, such as equations into regular HTML pages. The MathML language (http://www.w3.org/Math/) does just that. Example 7-1 incorporates MathML into it with namespaces.

Example 7-1. A document with namespaces

<html>
<body xmlns:eq="http://www.w3.org/1998/Math/MathML">
<h1>Billybob's Theory</h1>
<p>
It is well-known that cats cannot be herded easily. That is, they do
not tend to run in a straight line for any length of time unless they
really want to. A cat forced to run in a straight line against its
will has an increasing probability, with distance, of deviating from
the line just to spite you, given by this formula:</p>
<p>
  <!-- P = 1 - 1/(x^2) -->
  <eq:math>
    <eq:mi>P</eq:mi><eq:mo>=</eq:mo><eq:mn>1</eq:mn><eq:mo>-</eq:mo>
    <eq:mfrac>
      <eq:mn>1</eq:mn>
      <eq:msup>
        <eq:mi>x</eq:mi>
        <eq:mn>2</eq:mn>
      </eq:msup>
    </eq:mfrac>
  </eq:math>
</p>
</body>
</html>

The tags with eq: prefixes are part of a namespace identified by the URI http://www.w3.org/1998/Math/MathML, defined in an attribute in the <body> element. Using a namespace helps the browser discern between what is native to HTML and what is not. Browsers that understand MathML route the qualified elements to their equation formatter instead of the regular HTML formatter.

Some browsers are confused by the MathML tags and render unpredictable results. One particularly useful utility is a program that detects and removes namespace-qualified elements that would gum up an older HTML processor. The following example uses DOM2 to sift through a document and strip out all elements that have a namespace prefix.

The first step is to parse the file:

use XML::LibXML;

my $parser = XML::LibXML->new( );
my $doc = $parser->parse_file( shift @ARGV );

Next, we locate the document element and run a recursive subroutine on it to ferret out the namespace-qualified elements. Afterwards, we print out the document:

my $mathuri = 'http://www.w3.org/1998/Math/MathML';
my $root = $doc->getDocumentElement;
&amp;purge_nselems( $root );
print $doc->toString;

This routine takes an element node and, if it has a namespace prefix, removes it from its parent's content list. Otherwise, it goes on to process the descendants:

sub purge_nselems {
  my $elem = shift;
  return unless( ref( $elem ) =~ /Element/ );
  if( $elem->prefix ) {
    my $parent = $elem->parentNode;
    $parent->removeChild( $elem );
  } elsif( $elem->hasChildNodes ) {
    my @children = $elem->getChildnodes;
    foreach my $child ( @children ) {
      &purge_nselems( $child );
    }
  }
}

You might have noticed that this DOM implementation adds some Perlish conveniences over the recommended DOM interface. The call to getChildnodes, in an array context, returns a Perl list instead of a more cumbersome NodeList object. Called in a scalar context, it would return the number of child nodes for that node, so NodeLists aren't really used at all.

Simplifications like this are common in the Perl world, and no one really seems to mind. The emphasis is usually on ease of use over rigorous object-oriented protocol. Of course, one would hope that all DOM implementations in the Perl world adopt the same conventions, which is why many long discussions on the perl-xml mailing list try to decide the best way to adopt standards. A current debate discusses how to implement SAX2 (which supports namespaces) in the most logical, Perlish way.

Matt Sergeant has stocked the XML::LibXML package with other goodies. The Node class has a method called findnodes( ) , which takes an XPath expression as an argument, allowing retrieval of nodes in more flexible ways than permitted by the ordinary DOM interface. The parser has options that control how pedantically the parser runs, entity resolution, and whitespace significance. One can also opt to use special handlers for unparsed entities. Overall, this module is excellent for DOM programming.

Previous | Home | Next
7.3. XML::DOM | Book Index | 8. Beyond Trees: XPath, XSLT, and More
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Beyond Trees: XPath, XSLT, and More (Perl and XML)

 Perl & XMLSearch this book

Chapter 8. Beyond Trees: XPath, XSLT, and More

Contents:

Tree Climbers
XPath
XSLT
Optimized Tree Processing

In the last chapter, we introduced the concepts behind handling XML documents as memory trees. Our use of them was kind of primitive, limited to building, traversing, and modifying pieces of trees. This is okay for small, uncomplicated documents and tasks, but serious XML processing requires beefier tools. In this chapter, we examine ways to make tree processing easier, faster, and more efficient.

8.1. Tree Climbers

The first in our lineup of power tools is the tree climber. As the name suggests, it climbs a tree for you, finding the nodes in the order you want them, making your code simpler and more focused on per-node processing. Using a tree climber is like having a trained monkey climb up a tree to get you coconuts so you don't have to scrape your own skin on the bark to get them; all you have to do is drill a hole in the shell and pop in a straw.

The simplest kind of tree climber is an iterator (sometimes called a walker). It can move forward or backward in a tree, doling out node references as you tell it to move. The notion of moving forward in a tree involves matching the order of nodes as they would appear in the text representation of the document. The exact algorithm for iterating forward is this:

  • If there's no current node, start at the root node.

  • If the current node has children, move to the first child.

  • Otherwise, if the current node has a following sibling, move to it.

  • If none of these options work, go back up the list of the current node's ancestors and try to find one with an unprocessed sibling.

With this algorithm, the iterator will eventually reach every node in a tree, which is useful if you want to process all the nodes in a document part. You could also implement this algorithm recursively, but the advantage to doing it iteratively is that you can stop in between nodes to do other things. Example 8-1 shows how one might implement an iterator object for DOM trees. We've included methods for moving both forward and backward.

Example 8-1. A DOM iterator package

package XML::DOMIterator;

sub new {
  my $class = shift;
  my $self = {@_};
  $self->{ Node } = undef;
  return bless( $self, $class );
}

# move forward one node in the tree
#
sub forward {
  my $self = shift;

  # try to go down to the next level
  if( $self->is_element and
      $self->{ Node }->getFirstChild ) {
    $self->{ Node } = $self->{ Node }->getFirstChild;

  # try to go to the next sibling, or an acestor's sibling
  } else {
    while( $self->{ Node }) {
      if( $self->{ Node }->getNextSibling ) {
        $self->{ Node } = $self->{ Node }->getNextSibling;
        return $self->{ Node };
      }
      $self->{ Node } = $self->{ Node }->getParentNode;
    }
  }
}

# move backward one node in the tree
#
sub backward {
  my $self = shift;

  # go to the previous sibling and descend to the last node in its tree
  if( $self->{ Node }->getPreviousSibling ) {
    $self->{ Node } = $self->{ Node }->getPreviousSibling;
    while( $self->{ Node }->getLastChild ) {
      $self->{ Node } = $self->{ Node }->getLastChild;
    }

  # go up
  } else {
    $self->{ Node } = $self->{ Node }->getParentNode;
  }
  return $self->{ Node };
}

# return a reference to the current node
#
sub node {
  my $self = shift;
  return $self->{ Node };
}

# set the current node
#
sub reset {
  my( $self, $node ) = @_;
  $self->{ Node } = $node;
}

# test if current node is an element
#
sub is_element {
  my $self = shift;
  return( $self->{ Node }->getNodeType == 1 );
}

Example 8-2 is a test program for the iterator package. It prints out a short description of every node in an XML document tree -- first in forward order, then in backward order.

Example 8-2. A test program for the iterator package

use XML::DOM;

# initialize parser and iterator
my $dom_parser = new XML::DOM::Parser;
my $doc = $dom_parser->parsefile( shift @ARGV );
my $iter = new XML::DOMIterator;
$iter->reset( $doc->getDocumentElement );

# print all the nodes from start to end of a document
print "\nFORWARDS:\n";
my $node = $iter->node;
my $last;
while( $node ) {
  describe( $node );
  $last = $node;
  $node = $iter->forward;
}

# print all the nodes from end to start of a document
print "\nBACKWARDS:\n";
$iter->reset( $last );
describe( $iter->node );
while( $iter->backward ) {
  describe( $iter->node );
}

# output information about the node
#
sub describe {
  my $node = shift;
  if( ref($node) =~ /Element/ ) {
    print 'element: ', $node->getNodeName, "\n";
  } elsif( ref($node) =~ /Text/ ) {
    print "other node: \"", $node->getNodeValue, "\"\n";
  }
}

Many tree packages provide automated tree climbing capability. XML::LibXML::Node has a method iterator( ) that traverses a node's subtree, applying a subroutine to each node. Data::Grove::Visitor performs a similar function.

Example 8-3 shows a program that uses an automated tree climbing function to test processing instructions in a document.

Example 8-3. Processing instruction tester

use XML::LibXML;

my $dom = new XML::LibXML;
my $doc = $dom->parse_file( shift @ARGV );
my $docelem = $doc->getDocumentElement;
$docelem->iterator( \&find_PI );

sub find_PI {
  my $node = shift;
  return unless( $node->nodeType == &XML_PI_NODE );
  print "Found processing instruction: ", $node->nodeName, "\n";
}

Tree climbers are terrific for tasks that involve processing the whole document, since they automate the process of moving from node to node. However, you won't always have to visit every node. Often, you only want to pick out one from the bunch or get a set of nodes that satisfy a certain criterion, such as having a particular element name or attribute value. In these cases, you may want to try a more selective approach, as we will demonstrate in the next section.

Previous | Home | Next
7.4. XML::LibXML | Book Index | 8.2. XPath
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath (Perl and XML)

 Perl & XMLSearch this book

8.2. XPath

Imagine that you have an army of monkeys at your disposal. You say to them, "I want you to get me a banana frappe from the ice cream parlor on Massachusetts Avenue just north of Porter Square." Not being very smart monkeys, they go out and bring back every beverage they can find, leaving you to taste them all to figure out which is the one you wanted. To retrain them, you send them out to night school to learn a rudimentary language, and in a few months you repeat the request. Now the monkeys follow your directions, identify the exact item you want, and return with it.

We've just described the kind of problem XPath was designed to solve. XPath is one of the most useful technologies supporting XML. It provides an interface to find nodes in a purely descriptive way, so you don't have to write code to hunt them down yourself. You merely specify the kind of nodes that interest you and an XPath parser will retrieve them for you. Suddenly, XML goes from becoming a vast, confusing pile of nodes to a well-indexed filing cabinet of data.

Consider the XML document in Example 8-4.

Example 8-4. A preferences file

<plist>
  <dict>
    <key>DefaultDirectory</key>
    <string>/usr/local/fooby</string>
    <key>RecentDocuments</key>
    <array>
      <string>/Users/bobo/docs/menu.pdf</string>
      <string>/Users/slappy/pagoda.pdf</string>
      <string>/Library/docs/Baby.pdf</string>
    </array>
    <key>BGColor</key>
    <string>sage</string>
  </dict>
</plist>

This document is a typical preferences file for a program with a series of data keys and values. Nothing in it is too complex. To obtain the value of the key BGColor, you'd have to locate the <key> element containing the word "BGColor" and step ahead to the next element, a <string>. Finally, you would read the value of the text node inside. In DOM, you might do it as shown in Example 8-5.

Example 8-5. Program to get a preferred color

sub get_bgcolor {
    my @keys = $doc->getElementsByTagName( 'key' );
    foreach my $key ( @keys ) {
        if( $key->getFirstChild->getData eq 'BGColor' ) {
            return $key->getNextSibling->getData;
        }
    }
    return;
}

Writing one routine like this isn't too bad, but imagine if you had to do hundreds of queries like it. And this program was for a relatively simple document -- imagine how complex the code could be for one that was many levels deep. It would be nice to have a shorthand way of doing the same thing, say, on one line of code. Such a syntax would be much easier to read, write, and debug. This is where XPath comes in.

XPath is a language for expressing a path to a node or set of nodes anywhere in a document. It's simple, expressive, and standard (backed by the W3C, the folks who brought you XML).[28] You'll see it used in XSLT for matching rules to nodes, and in XPointer, a technology for linking XML documents to resources. You can also find it in many Perl modules, as we'll show you soon.

[28]The recommendation is on the Web at http://www.w3.org/TR/xpath.html.

An XPath expression is called a location path and consists of some number of path steps that extend the path a little bit closer to the goal. Starting from an absolute, known position (for example, the root of the document), the steps "walk" across the document tree to arrive at a node or set of nodes. The syntax looks much like a filesystem path, with steps separated by slash characters (/).

This location path shows how to find that color value in our last example:

/plist/dict/key[text()='BGColor']/following-sibling::*[1]/text( )

A location path is processed by starting at an absolute location in the document and moving to a new node (or nodes) with each step. At any point in the search, a current node serves as the context for the next step. If multiple nodes match the next step, the search branches and the processor maintains a set of current nodes. Here's how the location path shown above would be processed:

  • Start at the root node (one level above the root element).

  • Move to a <plist> element that is a child of the current node.

  • Move to a <dict> element that is a child of the current node.

  • Move to a <key> element that is a child of the current node and that has the value BGColor.

  • Find the next element after the current node.

  • Return any text nodes belonging to the current node.

Because node searches can branch if multiple nodes match, we sometimes have to add a test condition to a step to restrict the eligible candidates. Adding a test condition was necessary for the <key> sampling step where multiple nodes would have matched, so we added a test condition requiring the value of the element to be BGColor. Without the test, we would have received all text nodes from all siblings immediately following a <key> element.

This location path matches all <key> elements in the document:

/plist/dict/key

Of the many kinds of test conditions, all result in a boolean true/false answer. You can test the position (where a node is in the list), existence of children and attributes, numeric comparisons, and all kinds of boolean expressions using AND and OR operators. Sometimes a test consists of only a number, which is shorthand for specifying an index into a node list, so the test [1] says, "stop at the first node that matches."

You can link multiple tests inside the brackets with boolean operations. Alternatively, you can chain tests with multiple sets of brackets, functioning as an AND operator. Every path step has an implicit test that prunes the search tree of blind alleys. If at any point a step turns up zero matching nodes, the search along that branch terminates.

Along with boolean tests, you can shape a location path with directives called axes. An axis is like a compass needle that tells the processor which direction to travel. Instead of the default, which is to descend from the current node to its children, you can make it go up to the parent and ancestors or laterally among its siblings. The axis is written as a prefix to the step with a double colon (::). In our last example, we used the axis following-sibling to jump from the current node to its next-door neighbor.

A step is not limited to frolicking with elements. You can specify different kinds of nodes, including attributes, text, processing instructions, and comments, or leave it generic with a selector for any node type. You can specify the node type in many ways, some of which are listed here:

Symbol | Matches
node( ) | Any node
text( ) | A text node
element::foo | An element named foo
foo | An element named foo
attribute::foo | An attribute named foo
@foo | An attribute named foo
@* | Any attribute
* | Any element
. | This element
.. | The parent element
/ | The root node
/* | The root element
//foo | An element foo at any level

Since the thing you're most likely to select in a location path step is an element, the default node type is an element. But there are reasons why you should use another node type. In our example location path, we used text( ) to return just the text node for the <value> element.

Most steps are relative locators because they define where to go relative to the previous locator. Although locator paths are comprised mostly of relative locators, they always start with an absolute locator, which describes a definite point in the document. This locator comes in two flavors: id( ), which starts at an element with a given ID attribute, and root( ), which starts at the root node of the document (an abstract node that is the parent of the document element). You will frequently see the shorthand "/" starting a path indicating that root( ) is being used.

Now that we've trained our monkeys to understand XPath, let's give it a whirl with Perl. The XML::XPath module, written by Matt Sergeant of XML::LibXML fame, is a solid implementation of XPath. We've written a program in Example 8-6 that takes two command-line arguments: a file and an XPath locator path. It prints the text value of all nodes it finds that match the path.

Example 8-6. A program that uses XPath

use XML::XPath;
use XML::XPath::XMLParser;

# create an object to parse the file and field XPath queries
my $xpath = XML::XPath->new( filename => shift @ARGV );

# apply the path from the command line and get back a list matches
my $nodeset = $xpath->find( shift @ARGV );

# print each node in the list
foreach my $node ( $nodeset->get_nodelist ) {
  print XML::XPath::XMLParser::as_string( $node ) . "\n";
}

That example was simple. Now we need a datafile. Check out Example 8-7.

Example 8-7. An XML datafile

<?xml version="1.0"?>
<!DOCTYPE inventory [
  <!ENTITY poison "<note>danger: poisonous!</note>">
  <!ENTITY endang "<note>endangered species</note>">
]>
<!-- Rivenwood Arboretum inventory -->
<inventory date="2001.9.4">
  <category type="tree">
    <item id="284">
      <name style="latin">Carya glabra</name>
      <name style="common">Pignut Hickory</name>
      <location>east quadrangle</location>
      &endang;
    </item>
    <item id="222">
      <name style="latin">Toxicodendron vernix</name>
      <name style="common">Poison Sumac</name>
      <location>west promenade</location>
      &poison;
    </item>
  </category>
  <category type="shrub">
    <item id="210">
      <name style="latin">Cornus racemosa</name>
      <name style="common">Gray Dogwood</name>
      <location>south lawn</location>
    </item>
    <item id="104">
      <name style="latin">Alnus rugosa</name>
      <name style="common">Speckled Alder</name>
      <location>east quadrangle</location>
      &endang;
    </item>
  </category>
</inventory>

The first test uses the path /inventory/category/item/name:

> grabber.pl data.xml "/inventory/category/item/name"
<name style="latin">Carya glabra</name>
<name style="common">Pignut Hickory</name>
<name style="latin">Toxicodendron vernix</name>
<name style="common">Poison Sumac</name>
<name style="latin">Cornus racemosa</name>
<name style="common">Gray Dogwood</name>
<name style="latin">Alnus rugosa</name>
<name style="common">Speckled Alder</name>

Every <name> element was found and printed. Let's get more specific with the path /inventory/category/item/name[@style='latin']:

> grabber.pl data.xml "/inventory/category/item/name[@style='latin']"
<name style="latin">Carya glabra</name>
<name style="latin">Toxicodendron vernix</name>
<name style="latin">Cornus racemosa</name>
<name style="latin">Alnus rugosa</name>

Now let's use an ID attribute as a starting point with the path //item[@id='222']/note. (If we had defined the attribute id in a DTD, we'd be able to use the path id('222')/note. We didn't, but this alternate method works just as well.)

> grabber.pl data.xml "//item[@id='222']/note"
<note>danger: poisonous!</note>

How about ditching the element tags? To do so, use this:

> grabber.pl data.xml "//item[@id='222']/note/text( )"
danger: poisonous!

When was this inventory last updated?

> grabber.pl data.xml "/inventory/@date"
 date="2001.9.4"

With XPath, you can go hog wild! Here's the path a silly monkey might take through the tree:

> grabber.pl data.xml "//*[@id='104']/parent::*/preceding-sibling::*/child::*[2]/
name[not(@style='latin')]/node( )"
Poison Sumac

The monkey started on the element with the attribute id='104', climbed up a level, jumped to the previous element, climbed down to the second child element, found a <name> whose style attribute was not set to 'latin', and hopped on the child of that element, which happened to be the text node with the value Poison Sumac.

We have just seen how to use XPath expressions to locate and return a set of nodes. The implementation we are about to see is even more powerful. XML::Twig, an ingenious module by Michel Rodriguez, is quite Perlish in the way it uses XPath expressions. It uses a hash to map them to subroutines, so you can have functions called automatically for certain types of nodes.

The program in Example 8-8 shows how this works. When you initialize the XML::Twig object, you can set a bunch of handlers in a hash, where the keys are XPath expressions. During the parsing stage, as the tree is built, these handlers are called for appropriate nodes.

As you look at Example 8-8, you'll notice that at-sign (@) characters are escaped. This is because @ can cause a little confusion with XPath expressions living in a Perl context. In XPath, @foo refers to an attribute named foo, not an array named foo. Keep this distinction in mind when going over the XPath examples in this book and when writing your own XPath for Perl to use -- you must escape the @ characters so Perl doesn't try to interpolate arrays in the middle of your expressions.

If your code does so much work with Perl arrays and XPath attribute references that it's unclear which @ characters are which, consider referring to attributes in longhand, using the "attribute" XPath axis: attribute::foo. This raises the issue of the double colon and its different meanings in Perl and XPath. Since XPath has only a few hardcoded axes, however, and they're always expressed in lowercase, they're easier to tell apart at a glance.

Example 8-8. How twig handlers work

use XML::Twig;

# buffers for holding text
my $catbuf = '';
my $itembuf = '';

# initialize parser with handlers for node processing
my $twig = new XML::Twig( TwigHandlers => {
                             "/inventory/category"    => \&category,
                             "name[\@style='latin']"  => \&latin_name,
                             "name[\@style='common']" => \&common_name,
                             "category/item"          => \&item,
                                          });

# parse, handling nodes on the way
$twig->parsefile( shift @ARGV );

# handle a category element
sub category {
  my( $tree, $elem ) = @_;
  print "CATEGORY: ", $elem->att( 'type' ), "\n\n", $catbuf;
  $catbuf = '';
}

# handle an item element
sub item {
  my( $tree, $elem ) = @_;
  $catbuf .= "Item: " . $elem->att( 'id' ) . "\n" . $itembuf . "\n";
  $itembuf = '';
}

# handle a latin name
sub latin_name {
  my( $tree, $elem ) = @_;
  $itembuf .= "Latin name: " . $elem->text . "\n";
}

# handle a common name
sub common_name {
  my( $tree, $elem ) = @_;
  $itembuf .= "Common name: " . $elem->text . "\n";
}

Our program takes a datafile like the one shown in Example 8-7 and outputs a summary report. Note that since a handler is called only after an element is completely built, the overall order of handler calls may not be what you expect. The handlers for children are called before their parent. For that reason, we need to buffer their output and sort it out at the appropriate time.

The result comes out like this:

CATEGORY: tree

Item: 284
Latin name: Carya glabra
Common name: Pignut Hickory

Item: 222
Latin name: Toxicodendron vernix
Common name: Poison Sumac

CATEGORY: shrub

Item: 210
Latin name: Cornus racemosa
Common name: Gray Dogwood

Item: 104
Latin name: Alnus rugosa
Common name: Speckled Alder

XPath makes the task of locating nodes in a document and describing types of nodes for processing ridiculously simple. It cuts down on the amount of code you have to write because climbing around the tree to sample different parts is all taken care of. It's easier to read than code too. We're happy with it, and because it is a standard, we'll be seeing more uses for it in many modules to come.

Previous | Home | Next
8. Beyond Trees: XPath, XSLT, and More | Book Index | 8.3. XSLT
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT (Perl and XML)

 Perl & XMLSearch this book

8.3. XSLT

If you think of XPath as a regular expression syntax, then XSLT is its pattern substitution mechanism. XSLT is an XML-based programming language for describing how to transform one document type into another. You can do some amazing things with XSLT, such as describe how to turn any XML document into HTML or tabulate the sum of figures in an XML-formatted table. In fact, you might not need to write a line of code in Perl or any language. All you really need is an XSLT script and one of the dozens of transformation engines available for processing XSLT.

The Origin of XSLT

XSLT stands for XML Style Language: Transformations. The name means that it's a component of the XML Style Language (XSL), assigned to handle the task of converting input XML into a special format called XSL-FO (the FO stands for "Formatting Objects"). XSL-FO contains both content and instructions for how to make it pretty when displayed.

Although it's stuck with the XSL name, XSLT is more than just a step in formatting; it's an important XML processing tool that makes it easy to convert from one kind of XML to another, or from XML to text. For this reason, the W3C (yup, they created XSLT too) released the recommendation for it years before the rest of XSL was ready.

To read the specification and find links to XSLT tutorials, look at its home page at http://www.w3.org/TR/xslt.

An XSLT transformation script is itself an XML document. It consists mostly of rules called templates, each of which tells how to treat a specific type of node. A template usually does two things: it describes what to output and defines how processing should continue.

Consider the script in Example 8-9.

Example 8-9. An XSLT stylesheet

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:template match="html">
    <xsl:text>Title: </xsl:text>
    <xsl:value-of select="head/title"/>
    <xsl:apply-templates select="body"/>
  </xsl:template>

  <xsl:template match="body">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="h1 | h2 | h3 | h4">
    <xsl:text>Head: </xsl:text>
    <xsl:value-of select="."/>
  </xsl:template>

  <xsl:template match="p | blockquote | li">
    <xsl:text>Content: </xsl:text>
    <xsl:value-of select="."/>
  </xsl:template>
</xsl:stylesheet>

This transformation script converts an HTML document into ASCII with some extra text labels. Each <xsl:template> element is a rule that matches a part of an XML document. Its content consists of instructions to the XSLT processor describing what to output. Directives like <xsl:apply-templates> direct processing to other elements (usually descendants). We won't go into detail about XSLT syntax, as whole books on the subject are available. Our intent here is to show how you can combine XSLT with Perl to do powerful XML munching.

You might wonder, "Why do I need to use another language to transform XML when I can do that with the Perl I already know?" True, XSLT doesn't do anything you couldn't do in Perlish coding. Its value comes in the ease of learning the language. You can learn XSLT in few hours, but to do the same things in Perl would take much longer. In our experience writing software for XML, we found it convenient to use XSLT as a configuration file that nonprogrammers could maintain themselves. Thus, instead of viewing XSLT as competition for Perl, think of it more as a complementary technology that you can access through Perl when you need to.

How do Perl hackers employ the power of XSLT in their programs? Example 8-10 shows how to perform an XSLT transformation on a document using XML::LibXSLT, Matt Sergeant's interface to the super-fast GNOME library called LibXSLT, one of several XSLT solutions available from your CPAN toolbox.[29]

[29]Others that are currently available include the pure-Perl XML::XSLT module, and XML::Sablotron, based on the Expat and Sablotron C libraries (the latter of which is an XSLT library by the Ginger Alliance: http://www.gingerall.com).

Example 8-10. A program to run an XSLT transformation

use XML::LibXSLT;
use XML::LibXML;

# the arguments for this command are stylesheet and source files
my( $style_file, @source_files ) = @ARGV;

# initialize the parser and XSLT processor
my $parser = XML::LibXML->new( );
my $xslt = XML::LibXSLT->new( );
my $stylesheet = $xslt->parse_stylesheet_file( $style_file );

# for each source file: parse, transform, print out result
foreach my $file ( @source_files ) {
  my $source_doc = $parser->parse_file( $source_file );
  my $result = $stylesheet->transform( $source_doc );
  print $stylesheet->output_string( $result );
}

The nice thing about this program is that it parses the stylesheet only once, keeping it in memory for reuse with other source documents. Afterwards, you have the document tree to do further work, if necessary:

  • Postprocess or preprocess the text of the document with search-replace routines.

  • Pluck a piece of the document out to transform just that bit.

  • Run an iterator over the tree to handle some nodes that would be too difficult to process in XSLT.

The possibilities are endless and, as always in Perl, whatever you want to do, there's more than one way to do it.

Previous | Home | Next
8.2. XPath | Book Index | 8.4. Optimized Tree Processing
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Optimized Tree Processing (Perl and XML)

 Perl & XMLSearch this book

8.4. Optimized Tree Processing

The big drawback to using trees for XML crunching is that they tend to consume scandalous amounts of memory and processor time. This might not be apparent with small documents, but it becomes noticeable as documents grow to many thousands of nodes. A typical book of a few hundred pages' length could easily have tens of thousands of nodes. Each one requires the allocation of an object, a process that takes considerable time and memory.

Perhaps you don't need to build the entire tree to get your work done, though. You might only want a small branch of the tree and can safely do all the processing inside of it. If that's the case, then you can take advantage of the optimized parsing modes in XML::Twig (recall that we dealt with this module earlier in Section 8.2, "XPath"). These modes allow you to specify ahead of time what parts (or "twigs") of the tree you'll be working with so that only those parts are assembled. The result is a hybrid of tree and event processing with highly optimized performance in speed and memory.

XML::Twig has three modes of operation: the regular old tree mode, similar to what we've seen so far; "chunk" mode, which builds a whole tree, but has only a fraction of it in memory at a time (sort of like paged memory); and multiple roots mode, which builds only a few selected twigs from the tree.

Example 8-11 demonstrates the power of XML::Twig in chunk mode. The data to this program is a DocBook book with some <chapter> elements. These documents can be enormous, sometimes a hundred megabytes or more. The program breaks up the processing per chapter so that only a fraction of the space is needed.

Example 8-11. A chunking program

use XML::Twig;

# initalize the twig, parse, and output the revised twig
my $twig = new XML::Twig( TwigHandlers => { chapter => \&process_chapter });
$twig->parsefile( shift @ARGV );
$twig->print;

# handler for chapter elements: process and then flush up the chapter
sub process_chapter {
  my( $tree, $elem ) = @_;
  &process_element( $elem );
  $tree->flush_up_to( $elem );  # comment out this line to waste memory
}

# append 'foo' to the name of an element
sub process_element {
  my $elem = shift;
  $elem->set_gi( $elem->gi . 'foo' );
  my @children = $elem->children;
  foreach my $child ( @children ) {
    next if( $child->gi eq '#PCDATA' );
    &process_element( $child );
  }
}

The program changes element names to append the string "foo" to them. Changing names is just busy work to keep the program running long enough to check the memory usage. Note the line in the function process_chapter( ):

$tree->flush_up_to( $elem );

We get our memory savings from this command. Without it, the entire tree will be built and kept in memory until the document is finally printed out. But when it is called, the tree that has been built up to a given element is dismantled and its text is output (called flushing). The memory usage never rises higher than what is needed for the largest chapter in the book.

To test this theory, we ran the program on a 3 MB document, first without and then with the line shown above. Without flushing, the program's heap space grew to over 30 MB. It's staggering to see how much memory an object-oriented tree processor needs -- in this case ten times the size of the file. But with flushing enabled, the program hovered around only a few MB of memory usage, a savings of about 90 percent. In both cases, the entire tree is eventually built, so the total processing time is about the same. To save CPU cycles as well as memory, we need to use multiple roots mode.

Multiple roots mode works by specifying before parsing the roots of the twigs that you want built. You will save significant time and memory if the twigs are much smaller than the document as a whole. In our chunk mode example, we probably can't do much to speed up the process, since the sum of <chapter> elements is about the same as the size of the document. So let's focus on an example that fits the profile.

The program in Example 8-12 reads in DocBook documents and outputs the titles of chapters -- a table of contents of sorts. To get this information, we don't need to build a tree for the whole chapter; only the <title> element is necessary. So for roots, we specify titles of chapters, expressed in the XPath notation chapter/title.

Example 8-12. A many-twigged program

use XML::Twig;

my $twig = new XML::Twig( TwigRoots => { 'chapter/title' => \&output_title });
$twig->parsefile( shift @ARGV );

sub output_title {
  my( $tree, $elem ) = @_;
  print $elem->text, "\n";
}

The key line here is the one with the keyword TwigRoots. It's set to a hash of handlers and works very similarly to TwigHandlers that we saw earlier. The difference is that instead of building the whole document tree, the program builds only trees whose roots are <title> elements. This is a small fraction of the whole document, so we can expect time and memory savings to be high.

How high? Running the program on the same test data, we saw memory usage barely reach 2 MB, and the total processing time was 13 seconds. Compare that to 30 MB memory usage (the size required to build the whole tree) and a full minute to grind out the titles. This conservation of resources is significant for both memory and CPU time.

XML::Twig can give you a big performance boost for your tree processing programs, but you have to know when chunking and multiple roots will help. You won't save much time if the sum of twigs is almost as big as the document itself. Chunking is not useful unless the chunks are significantly smaller than the document.

Previous | Home | Next
8.3. XSLT | Book Index | 9. RSS, SOAP, and Other XML Applications
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


RSS, SOAP, and Other XML Applications (Perl and XML)

 Perl & XMLSearch this book

Chapter 9. RSS, SOAP, and Other XML Applications

Contents:

XML Modules
XML::RSS
XML Programming Tools
SOAP::Lite

In the next couple of chapters, we'll cover, at long last, what happens when we pull together all the abstract tools and strategies we've discussed and start having XML dance for us. This is the land of the XML application, where parsers all have a bone to pick, picking up documents with a goal in mind. No longer satisfied with picking out the elements and attributes and calling it a day, these higher-level tools look for meaning in all that structure, according to directives that have been programmed into it.

When we say XML application, we are specifically referring to XML-based document formats, not the computer programs (applications of another sort) that do stuff with them. You may run across statements such as "GreenMonkeyML is an XML application that provides semantic markup for green monkeys." Visiting the project's home page at http://www.greenmonkey-markup.com, we might encounter documentation describing how this specific format works, example documents, suggested uses for it, a DTD or schema used to validate GreenMonkeyML documents, and maybe an online validation tool. This content would all fit into the definition of an XML application.

This chapter looks at XML applications that already have a strong presence in the Perl world, by way of publicly available Perl modules that know how to handle them.

9.1. XML Modules

The term XML modules narrows us down from the Perl modules on CPAN that send mail, process images, and play games, but it still leaves us with a very broad cross section. So far in this book, we have exhaustively covered Perl extensions that can perform general XML processing, but none that perform more targeted functions based on general processing. In the end, they hand you a plate of XML chunklets, free of any inherent meaning, and leave it to you to decide what happens next. In many of the examples we've provided so far in this book, we have written programs that do exactly this: invoke an XML parser to chew up a document and then cook up something interesting out of the elements and attributes we get back.

However, the modules we're thinking about here give you more than the generic parse-and-process module family by building on one of the parsers and abstracting the processing in a specific direction. They then provide an API that, while it might still contain hooks into the raw XML, concentrates on methods and routines particular to the XML application that they implement.

We can divide these XML application-mangling Perl modules into three types. We'll examine an example of each in this chapter, and in the next chapter, we'll try to make some for ourselves.

XML application helpers

Helper modules are the humblest of the lot. In practice, they are often little more than wrappers around raw XML processors, but sometimes that's all you need. If you find yourself writing several programs that need to read from and write to a specific XML-based document format, a helper module can provide common methods, freeing the programmer from worrying about the application's exact document format or its well-formedness in generated output. The module will take care of all that.

Programming helpers that use XML

This small but growing category describes Perl extensions that use XML to do cool stuff in your program, even if your program's input or output has little to do with XML. Currently, the most prominent examples involve the terrifying, DBI-like powers of XML::SAX, the whole PerlSAX2 family, and individual tools like the XML::Generator::DBI module, which crossbreeds existing Perl modules for database manipulation and SAX processing.

Full-on applications that use XML

Finally, we have software that uses XML, but has so many layers of abstraction between its intended purpose and the underlying XML that calling it an XML application is like calling Microsoft Word a C application. For example, working with SOAP::Lite involves documents that are barely human-readable and exist only in memory until they're shot over the Internet via HTTP; the role of XML in SOAP is completely transparent.

Previous | Home | Next
8.4. Optimized Tree Processing | Book Index | 9.2. XML::RSS
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML::RSS (Perl and XML)

 Perl & XMLSearch this book

9.2. XML::RSS

By helper modules, we mean more focused versions of the XML processors we've already pawed through in our Perl and XML toolbox. In a way, XML::Parser and its ilk are helper applications since they save you from approaching each XML-chomping job with Perl's built-in file-reading functions and regular expressions by turning documents into immediately useful objects or event streams. Also, XML::Writer and friends replace plain old print statements with a more abstract and safer way to create XML documents.

However, the XML modules we cover now offer their services in a very specific direction. By using one of these modules in your program, you establish that you plan to use XML, but only a small, clearly defined subsection of it. By submitting to this restriction, you get to use (and create) software modules that handle all the toil of working with raw XML, presenting the main part of your code with methods and routines specific only to the application at hand.

For our example, we'll look at XML::RSS -- a little number by Jonathan Eisenzopf.

9.2.1. Introduction to RSS

RSS (short for Rich Site Summary or Really Simple Syndication, depending upon whom you ask) is one of the first XML applications whose use became rapidly popular on a global scale, thanks to the Web. While RSS itself is little more than an agreed-upon way to summarize web page content, it gives the administrators of news sites, web logs, and any other frequently updated web site a standard and sweat-free way of telling the world what's new. Programs that can parse RSS can do whatever they'd like with this document, perhaps telling its masters by mail or by web page what interesting things it has learned in its travels. A special type of RSS program is an aggregator, a program that collects RSS from various sources and then knits it together into new RSS documents combining the information, so that lazier RSS-parsing programs won't have to travel so far.

Current popular aggregators include Netscape, by way of its customizable my.netscape.com site (which was, in fact, the birthplace of the earliest RSS versions) and Dave Winer's http://www.scripting.com (whose aggregator has a public frontend at http://aggregator.userland.com/register). These aggregators, in turn, share what they pick up as RSS, turning them into one-stop RSS shops for other interested entities. Web sites that collect and present links to new stuff around the Web, such as the O'Reilly Network's Meerkat (http://meerkat.oreillynet.com), hit these aggregators often to get information on RSS-enabled web sites, and then present it to the site's user.

9.2.2. Using XML::RSS

The XML::RSS module is useful whether you're coming or going. It can parse RSS documents that you hand it, or it can help you write your own RSS documents. Naturally, you can combine these abilities to parse a document, modify it, and then write it out again; the module uses a simple and well-documented object model to represent documents in memory, just like the tree-based modules we've seen so far. You can think of this sort of XML helper module as a tricked-out version of a familiar general XML tool.

In the following examples, we'll work with a notional web log, a frequently updated and Web-readable personal column or journal. RSS lends itself to web logs, letting them quickly summarize their most recent entries within a single RSS document.

Here are a couple of web log entries (admittedly sampling from the shallow end of the concept's notional pool, but it works for short examples). First, here is how one might look in a web browser:

Oct 18, 2002 19:07:06

Today I asked lab monkey 45-X how he felt about his recent chess
victory against Dr. Baker. He responded by biting my kneecap. (The
monkey did, I mean.) I
think this could lead to a communications breakthrough. As well as
painful swelling, which is unfortunate.

Oct 27, 2002 22:56:11

On a tangential note, Dr. Xing's research of purple versus green monkey
trans-sociopolitical impact seems to be stalled, having gained no
ground for several weeks. Today she learned that her lab assistant
never mentioned on his job application that he was colorblind. Oh well.

Here it is again, as an RSS v1.0 document:

<?xml version="1.0" encoding="UTF-8"?>

<rdf:RDF
 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 xmlns="http://purl.org/rss/1.0/"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/"
 xmlns:syn="http://purl.org/rss/1.0/modules/syndication/"
>

<channel rdf:about="http://www.jmac.org/linklog/">
<title>Link's Log</title>
<link>http://www.jmac.org/linklog/</link>
<description>Dr. Lance Link's online research journal</description>
<dc:language>en-us</dc:language>
<dc:rights>Copright 2002 by Dr. Lance Link</dc:rights>
<dc:date>2002-10-27T23:59:15+05:00</dc:date>
<dc:publisher>llink@jmac.org</dc:publisher>
<dc:creator>llink@jmac.org</dc:creator>
<dc:subject>llink</dc:subject>
<syn:updatePeriod>daily</syn:updatePeriod>
<syn:updateFrequency>1</syn:updateFrequency>
<syn:updateBase>2002-03-03T00:00:00+05:00</syn:updateBase>
<items>
 <rdf:Seq>
  <rdf:li rdf:resource="http://www.jmac.org/linklog?2002-10-27#22:56:11" />
  <rdf:li rdf:resource="http://www.jmac.org/linklog?2002-10-18#19:07:06" />
 </rdf:Seq>
</items>
</channel>

<item rdf:about="http://www.jmac.org/linklog?2002-10-27#22:56:11">
<title>2002-10-27 22:56:11</title>
<link>http://www.jmac.org/linklog?2002-10-27#22:56:11</link>
<description>
Today I asked lab monkey 45-X how he felt about his recent chess
victory against Dr. Baker. He responded by biting my kneecap. (The
monkey did, I mean.) I
think this could lead to a communications breakthrough. As well as
painful swelling, which is unfortunate.
</description>
</item>

<item rdf:about="http://www.jmac.org/linklog?2002-10-18#19:07:06">
<title>2002-10-18 19:07:06</title>
<link>http://www.jmac.org/linklog?2002-10-18#19:07:06</link>
<description>
On a tangential note, Dr. Xing's research of purple versus green monkey
trans-sociopolitical impact seems to be stalled, having gained no
ground for several weeks. Today she learned that her lab assistant
never mentioned on his job application that he was colorblind. Oh well.
</description>
</item>

</rdf:RDF>

Note RSS 1.0's use of various metadata-enabling namespaces before it gets into the meat of laying out the actual content.[30] The curious may wish to point their web browsers at the URIs with which they identify themselves, since they are good little namespaces who put their documentation where their mouth is. ("dc" is the Dublin Core, a standard set of elements for describing a document's source. "syn" points to a syndication namespace -- itself a sub-project by the RSS people -- holding a handful of elements that state how often a source refreshes itself with new content.) Then the whole document is wrapped up in an RDF element.

[30]I am careful to specify the RSS version here because RSS Version .9 and 0.91 documents are much simpler in structure, eschewing namespaces and RDF-encapsulated metadata in favor of a simple list of <item> elements wrapped in an <rss> element. For this reason, many people prefer to use pre-1.0 RSS, and socially astute RSS software can read from and write to all these versions. XML::RSS can do this, and as a side effect, allows easy conversion between these different versions (given a single original document).

9.2.2.1. Parsing

Using XML::RSS to read an existing document ought to look familiar if you've read the preceding chapters, and is quite simple:

use XML::RSS;

# Accept file from user arguments
my @rss_docs = @ARGV;

# For now, we'll assume they're all files on disk...
foreach my $rss_doc (@rss_docs) {

  # First, create a new RSS object that will represent the parsed doc
  my $rss = XML::RSS->new;

  # Now parse that puppy
  $rss->parsefile($rss_doc);

  # And that's all. Do whatever else we may want here.
}

9.2.2.2. Inheriting from XML::Parser

If that parsefile method looked familiar, it had good reason: it's the same one used by grandpappy XML::Parser, both in word and deed.

XML::RSS takes direct advantage of XML::Parser's inheritability right off the bat, placing this module into its @ISA array before getting down to business with all that map definition.

It shouldn't surprise those familiar with object-oriented Perl programming that, while it chooses to define its own new method, it does little more than invoke SUPER::new. In doing so, it lets XML::Parser initialize itself as it sees fit. Let's look at some code from that module itself -- specifically its constructor, new, which we invoked in our example:

sub new {
    my $class = shift;
    my $self = $class->SUPER::new(Namespaces    => 1,
                                  NoExpand      => 1,
                                  ParseParamEnt => 0,
                                  Handlers      => { Char    => \&handle_char,
                                                     XMLDecl => \&handle_dec,
                                                     Start   => \&handle_start})
;
    bless ($self,$class);
    $self->_initialize(@_);
    return $self;
}

Note how the module calls its parent's new with very specific arguments. All are standard and well-documented setup instructions in XML::Parser's public interface, but by taking these parameters out of the user's hands and into its own, the XML::RSS module knows exactly what it's getting -- in this case, a parser object with namespace processing enabled, but not expansion or parsing of parameter entities -- and defines for itself what its handlers are.

The result of calling SUPER::new is an XML::Parser object, which this module doesn't want to hand back to its users -- doing so would diminish the point of all this abstraction! Therefore, it reblesses the object (at this point, deemed to be a new $self for this class) using the Perl-itically correct two-argument method, so that the returned object claims fealty to XML::RSS, not XML::Parser.

9.2.3. The Object Model

Since we can see that XML::RSS is not very unique in terms of parser object construction and document parsing, let's look at where it starts to cut an edge of its own: through the shape of the internal data structure it builds and to which it applies its method-based API.

XML::RSS's code is made up mostly of accessors -- methods that read and write to predefined places in the structure it's building. Using nothing more complex than a few Perl hashes, XML::RSS builds maps of what it expects to see in the document, made of nested hash references with keys named after the elements and attributes it might encounter, nested to match the way one might find them in a real RSS XML document. The module defines one of these maps for each version of RSS that it handles. Here's the simplest one, which covers RSS Version 0.9:

my %v0_9_ok_fields = (
    channel => {
        title       => '',
        description => '',
        link        => '',
        },
    image  => {
        title => '',
        url   => '',
        link  => ''
        },
    textinput => {
        title       => '',
        description => '',
        name        => '',
        link        => ''
        },
    items => [],
    num_items => 0,
    version         => '',
    encoding        => ''
);

This model is not entirely made up of hash references, of course; the top-level "items" key holds an empty array reference, and otherwise, all the end values for all the keys are scalars -- all empty strings. The exception is num_items, which isn't among RSS's elements. Instead, it serves the role of convenience, making a small trade-off of structural elegance for the sake of convenience (presumably so the code doesn't have to keep explicitly dereferencing the items array reference and then getting its value in scalar context).

On the other hand, this example risks going out of sync with reality if what it describes changes and the programmer doesn't remember to update the number when that happens. However, this sort of thing often comes down to programming style, which is far beyond the bounds of this book.

There's good reason for this arrangement, besides the fact that hash values have to be set to something (or undef, which is a special sort of something). Each hash doubles as a map for the module's subroutines to follow and a template for the structures themselves. With that in mind, let's see what happens when an XML::Parser item is constructed via this module's new class method.

9.2.4. Input: User or File

After construction, an XML::RSS is ready to chew through an RSS document, thanks to the parsing powers afforded to it by its proud parent, XML::Parser. A user only needs to call the object's parse or parsefile methods, and off it goes -- filling itself up with data.

Despite this, many of these objects will live long[31] and productive lives without sinking their teeth into an existing XML document. Often RSS users would rather have the module help build a document from scratch -- or rather, from the bits of text that programs we write will feed to it. This is when all those accessors come in handy.

[31]Well, a few hundredths of a second on a typical whizbang PC, but we mean long in the poetic sense.

Thus, let's say we have a SQL database somewhere that contains some web log entries we'd like to RSS-ify. We could write up this little script:

#!/usr/bin/perl

# Turn the last 15 entries of Dr. Link's Weblog into an RSS 1.0 document,
# which gets pronted to STDOUT.

use warnings;
use strict;

use XML::RSS;
use DBIx::Abstract;

my $MAX_ENTRIES = 15;

my ($output_version) = @ARGV;
$output_version ||= '1.0';
unless ($output_version eq '1.0' or $output_version eq '0.9'
                                 or $output_version eq '0.91') {
  die "Usage: $0 [version]\nWhere [version] is an RSS version to output:
0.9, 0 .91, or 1.0\nDefault is 1.0\n";
}

my $dbh = DBIx::Abstract->connect({dbname=>'weblog',
                                   user=>'link',
                                   password=>'dirtyape'})
  or die "Couln't connect to database.\n";

my ($date) = $dbh->select('max(date_added)',
                          'entry')->fetchrow_array;
my ($time) = $dbh->select('max(time_added)',
                          'entry')->fetchrow_array;

my $time_zone = "+05:00"; # This happens to be where I live. :)
my $rss_time = "${date}T$time$time_zone";
# base time is when I started the blog, for the syndication info
my $base_time = "2001-03-03T00:00:00$time_zone";

# I'll choose to use RSS version 1.0 here, which stuffs some meta-information into
# 'modules' that go into their own namespaces, such as 'dc' (for Dublin Core) or
# 'syn' (for RSS Syndication), but fortunately it doesn't make defining the document
# any more complex, as you can see below...

my $rss = XML::RSS->new(version=>'1.0', output=>$output_version);

$rss->channel(
              title=>'Dr. Links Weblog',
              link=>'http://www.jmac.org/linklog/',
              description=>"Dr. Link's weblog and online journal",
              dc=> {
                    date=>$rss_time,
                    creator=>'llink@jmac.org',
                    rights=>'Copyright 2002 by Dr. Lance Link',
                    language=>'en-us',
                   },
              syn=> {
                     updatePeriod=>'daily',
                     updateFrequency=>1,
                     updateBase=>$base_time,
                    },
             );

$dbh->query("select * from entry order by id desc limit $MAX_ENTRIES");
while (my $entry = $dbh->fetchrow_hashref) {
  # Replace XML-naughty characters with entities
  $$entry{entry} =~ s/&/&/g;
  $$entry{entry} =~ s/</&lt;/g;
  $$entry{entry} =~ s/'/&apos;/g;
  $$entry{entry} =~ s/"/&quot;/g;
  $rss->add_item(
         title=>"$$entry{date_added} $$entry{time_added}",
         link=>"http://www.jmac.org/weblog?$$entry{date_added}#$$entry{time_added}",
         description=>$$entry{entry},
                );
}

# Just throw the results into standard output. :)
print $rss->as_string;

Did you see any XML there? We didn't. Well, OK, we did have to give the truth of the matter a little nod by tossing in those entity-escape regexes, but other than that, we were reading from a database and then stuffing what we found into an object by way of a few method calls (or rather, a single, looped call to its add_item method). These calls accepted, as their sole argument, a hash made of some straightforward strings. While we (presumably) wrote this program to let our web log take advantage of everything RSS has to offer, no actual XML was munged in the production of this file.

9.2.5. Off-the-Cuff Output

By the way, XML::RSS doesn't use XML-generation-helper modules such as XML::Writer to product its output; it just builds one long scalar based on what the map-hash looks like, running through ordinary if, else, and elsif blocks, each of which tend to use the .= self-concatenation operator. If you think you can get away with it in your own XML-generating modules, you might try this approach, building up the literal document-to-be in memory and printing it to a filehandle; that way, you'll save a lot of overhead and gain control, but give up some safety in the process. Just be sure to test your output thoroughly for well-formedness. (If you're making a dual-purpose parser/generator like XML::RSS, you might try to have the module parse some of its own output and make sure everything looks as you'd expect.)

Previous | Home | Next
9. RSS, SOAP, and Other XML Applications | Book Index | 9.3. XML Programming Tools
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Programming Tools (Perl and XML)

 Perl & XMLSearch this book

9.3. XML Programming Tools

Now we'll cover software that performs a somewhat inverse role compared to the ground we just covered. Instead of giving you Perl-lazy ways to work with XML documents, it uses XML standards to make things easier for a task that doesn't explicitly involve XML. Recently, some key folk in the community from the perl-xml mailing list have been seeking a mini-platform of universal data handling in Perl with SAX at its core. Some very interesting (and useful) examples have been born from this research, including Ilya Sterin's XML::SAXDriver::Excel and XML::SAXDriver::CSV, and Matt Sergeant's XML::Generator::DBI. All three modules share the ability to take a data format -- Microsoft Excel files, Comma-Separated Value files, and SQL databases, respectively -- and wrap a SAX API around it (the same sort covered in Chapter 5, "SAX", so that any programmer can merrily pretend that the format is as well behaved and manageable as all the other XML documents they've seen (even if the underlying module is quietly performing acrobatics akin to medicating cats).

We'll look more closely at one of these tools, as its subject matter has some interesting implications involving recent developments, before we move on to this chapter's final section.

9.3.1. XML::Generator::DBI

XML::Generator::DBI is a fine example of a glue module, a simple piece of software whose only job is to take two existing (but not entirely unrelated) pieces of software and let them talk to one another. In this case, when you construct an object of this class, you hand it your additional objects: a DBI-flavored database handle and a SAX-speaking handler object.

XML::Generator::DBI does not know or care how or where the objects came from, but only trusts that they respond to the standard method calls of their respective families (either DBI, SAX, or SAX2). Then you can call an execute method on the XML::Generator::DBI object with an ordinary SQL statement, much as you would with a DBI-created database handle.

The following example shows this module in action. The SAX handler in question is an instance of Michael Koehne's XML::Handler::YAWriter module, a pleasantly configurable module that turns SAX events into textual output. Using this program, we can turn, say, a SQL table of CDs into well-formed XML and then have it printed to standard output:

#!/usr/bin/perl

use warnings;
use strict;

use XML::Generator::DBI;
use XML::Handler::YAWriter;

use DBI;

my $ya = XML::Handler::YAWriter->new(AsFile => "-");
my $dbh = DBI->connect("dbi:mysql:dbname=test", "jmac", "");
my $generator = XML::Generator::DBI->new(
                               Handler => $ya,
                               dbh => $dbh
                               );
my $sql = "select * from cds";

$generator->execute($sql);

The result is this:

<?xml version="1.0" encoding="UTF-8"?><database>
 <select query="select * from cds">
  <row>
   <id>1</id>
   <artist>Donald and the Knuths</artist>
   <title>Greatest Hits Vol. 3.14159</title>
   <genre>Rock</genre>
  </row>
  <row>
   <id>2</id>
   <artist>The Hypnocrats</artist>
   <title>Cybernetic Grandmother Attack</title>
   <genre>Electronic</genre>
  </row>
  <row>
   <id>3</id>
   <artist>The Sam Handwich Quartet</artist>
   <title>Handwich a la Yogurt</title>
   <genre>Jazz</genre>
  </row>
 </select>
</database>

This example isn't very interesting, but it looks good in print. The point is that we didn't have to use YAWriter. We could have used any SAX handler Perl package on our system, including ones we wrote ourselves, and tossed them into the mix when baking a new XML::Generator::DBI object. Given the same database table as the example above used, when the $genenerator object's execute method is called, it would act as if it had just parsed the previous XML document (modulo the whitespace that YAWriter inserted to make things more human-readable). It would act this way even though the actual source isn't an XML document at all, but a database table.

9.3.2. Further Ruminations on DBI and SAX

While we're on the subject, let's digress down the path of DBI and SAX, which may have more in common than mutual utility in data management.

The main reason why the Perl DBI earned its position as the preeminent Perl database interface involves its architecture. When installing DBI, one must obtain two separate pieces: DBI.pm contains all the code behind the DBI API and its documentation, but it alone won't let you drive a database with Perl; you also need at least one DBD module that is suitable to the type of database you plan to use. CPAN has many of these modules to choose from, DBD::MySQL, DBD::Oracle, and DBD::Pg for Postgres. While the programmer interacts only with the DBI module, feeding it SQL queries and receiving results from it, the appropriate DBD module communicates directly with the actual database. The DBD module turns the abstract DBI methods into highly specific and platform-dependent database commands. It does this far underneath the level at which the DBI user works, so that any Perl program using DBI will work on any database for which somebody has made available a DBD driver.[32]

[32]Assuming, of course, that the programmer took care not to have the program rely on any queries unique to a given database. $sth->query('select last_insert_id( ) from foo') might work well when hacking on a MySQL database, but cause your friends using Postgres great pain. Consult O'Reilly's Programming the Perl DBI by Alligator Descartes and Tim Bunce for more information.

A similar movement is on the ascent in the Perl and XML world, which started in 2001 with the SAX drivers mentioned at the start of this section and ended up with the XML::SAX module, a SAX2 implementation that works like DBI. Tell it you want a SAX parser, optionally specifying the SAX features your program's gotta have, and it roots around on your system to find the best tool for the job, which it instantiates and hands back to you. Then you plug in the SAX handler package of your choice (much as with XML::Generator::DBI) and go to town.

Instead of a variety of DBD drivers that let you use a standard interface to pull data from a variety of databases, PerlSAX handlers let you use a standard interface to pull data from any imaginable data source. As with DBI, it requires only one intrepid hacker to wade through the data format in question, and suddenly other Perl programmers with a clue about SAX hacking can find themselves using a standard API to handle this once-alien format.

Previous | Home | Next
9.2. XML::RSS | Book Index | 9.4. SOAP::Lite
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SOAP::Lite (Perl and XML)

 Perl & XMLSearch this book

9.4. SOAP::Lite

Finally, we come to the category of Perl and XML software that is so ridiculously abstracted from the book's topic that it's almost not worth covering, but it's definitely much more worth showing off. This category describes modules and extensions that are similar to the XML::RSS class helper modules; they help you work with a specific variety of XML documents, but set themselves apart by the level of aggression they employ to keep programmers separated from the raw, element-encrusted data flowing underneath it. They involve enough layers of abstraction to make you forget that you're even dealing with XML in the first place.

Of course, they're perfectly valid in doing so; for example, if we want to write a program that uses the SOAP or XML-RPC protocols to use remote code, nothing could be further from our thoughts than XML. It's all a magic carpet, as far as we're concerned -- we just want our program to work! (And when we do care, a good module lets us peek at the raw XML, if we insist.)

The Simple Object Access Protocol (SOAP) gives you the power of object-oriented web services[33] by letting you construct and use objects whose class definitions exist at the other end of a URI. You don't even need to know what programming language they use because the protocol magically turns the object's methods into a common, XML-based API. As long as the class is documented somewhere, with more details of the available class and object methods, you can hack away as if the class was simply another file on your hard drive, despite the fact that it actually exists on a remote machine.

[33]Despite the name, web services don't have to involve the World Wide Web per se; a web service is simply a piece of software that listens patiently on a port to which a URI points, and, upon receiving a request, concocts a reply that makes sense to the requesting entity. A plain old HTTP-trafficking web server is the most common sort of web service, but the concept's more recent hype centers around its newfound ability to provide persistent access to objects and procedures (so that a programmer can use bits of code that exist on remote servers, tying them seamlessly into locally stored software).

At this point it's entirely too easy to forget that we're working with XML. At least with RSS, the method names of the object API more or less match those of the resulting output document; in this case, We don't even want to see the horrible machine-readable-only document any more than we'd want to see the numeric codes representing keystrokes that are sent to our machine's CPU.

SOAP::Lite's name refers to the amount of work you have to apply when you wish to use it, and does not reflect its own weight. When you install it on your system, it makes a long list of Perl packages available to you, many of which provide a plethora of transportation styles,[34] a mod_perl module to assist with SOAPy web serving, and a whole lot of documentation and examples. Then it does most of this all over again with a set of modules providing similar APIs for XML-RPC, SOAP's non-object-oriented cousin.[35] SOAP::Lite is one of those seminal all-singing, all-dancing tools for Perl programmers, doing for web service programming what CGI.pm does for dynamic web site programming.

[34]HTTP is the usual way to SOAP objects around, but if you want to use raw TCP, SMTP, or even Jabber, SOAP::Lite is ready for you

[35]And whose relationship with Perl is covered in depth in O'Reilly's Programming Web Services with XML-RPC by Simon St.Laurent, Joe Johnston, and Edd Dumbill.

Let's get our hands dirty with SOAP.

9.4.1. First Example: A Temperature Converter

Every book about programming needs some temperature-conversion code in it somewhere, right? Well, we don't quite have that here. In this example, lovingly ripped off from the SYNOPSIS section of the documentation for SOAP::Lite, we write a program whose main function, f2c, lives on whatever machine answers to the URI http://services.soaplite.com/Temperatures.

         use SOAP::Lite;
         print SOAP::Lite
           -> uri('http://www.soaplite.com/Temperatures')
           -> proxy('http://services.soaplite.com/temper.cgi')
           -> f2c(32)
           -> result;

Executing this program as a Perl script (on a machine with SOAP::Lite properly installed) gives the correct response: 0.

9.4.2. Second Example: An ISBN Lookup Engine

This example, which uses a little module residing on one of the author's personal web servers, is somewhat more object oriented. It takes an ISBN number and returns Dublin Core XML for almost any book that might match it:

  my ($isbn_number) = @ARGV;
  use SOAP::Lite +autodispatch=>
      uri=>'http://www.jmac.org/ISBN',
      proxy=>'http://www.jmac.org/projects/bookdb/isbn/lookup.pl';
  my $isbn_obj = ISBN->new;

  # The 'get_dc' method fetches Dublin Core information
  my $result = $isbn_obj->get_dc($isbn_number);

The magic here is that the module on the host machine, ISBN.pm, isn't unusual in any way; it's a pretty straightforward Perl module that you could use in the usual fashion, if you happened to have a local copy. In other words, we can get the same results by logging into the machine and hammering out a little program like this:

  my ($isbn_number) = @ARGV;
  use ISBN; # This line replaces the long 'use SOAP::Lite' line
  my $isbn_obj = ISBN->new;

  # The 'get_dc' method fetches Dublin Core information
  my $result = $isbn_obj->get_dc($isbn_number);

But, by invoking SOAP::Lite and mumbling a few extra incantations to aim our sights at a remote machine that's listening for SOAP-ish requests, you don't need a copy of that Perl module on your end to enjoy the benefits of its API. And, if we eventually went insane and reimplemented the module in Java, you'd probably never know it, since we'd keep the interface the same. In the language-independent world of web services, that's all that matters.

Where is the XML? We can switch on a valve and peek at the raw stuff roaring beneath this pleasant veneer. Let's see what actually happens with that ISBN class constructor call after we activate SOAP::Lite's outputxml option:

  my ($isbn_number) = @ARGV;
  use SOAP::Lite +autodispatch=>
      uri=>'http://www.jmac.org/ISBN', outputxml=>1,
      proxy=>'http://www.jmac.org/projects/bookdb/isbn/lookup.pl';
  my $isbn_xml = ISBN->new;
  print "$isbn_xml\n";

What we get back is something like this:

<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENC="http://
schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.
org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:
xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/
XMLSchema" xmlns:namesp1="http://www.jmac.org/ISBN"><SOAP-ENV:Body><namesp2:
newResponse xmlns:namesp2="http://www.jmac.org/ISBN"><ISBN xsi:type="namesp1:ISBN"/>
</namesp2:newResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

The second bit of example code had to stop short, of course, since it returned a scalar containing a pile of XML (which we then printed) instead of an object belonging to the SOAP::Lite class family. We can't well continue calling methods on it. We can fix this problem by passing the blob to the magic SOAP::Deserializer class, which turns SOAPy XML back into objects:

# Continuing from the previous snippet...
my $deserial = SOAP::Deserializer->new;
my $isbn_obj = $deserial->deserialize($isbn_xml);
# Now we can continue as with the first example.

A little extra work, then, nets us the raw XML as well as the black boxes of the SOAP::Lite objects. As you may expect, this feature has uses far beyond interesting book examples, as getting the raw XML in hand opens up the door to all kinds of interesting mischief on our end.

While SOAP::Lite the Perl module is magic in diverse ways, SOAP the protocol is just, well, a protocol, and all the strange namespaces, elements, and attributes seen in the XML generated by this module are compliant to the world-readable SOAP specification.[36] This compliance allows you to apply a cunning plan to your SOAP-using application, with which you let the SOAP::Lite module do its usual magic -- but then your program leaps in, captures the raw XML, does something strange and wonderful with it (it can be parsed with any method we've covered so far), and then perhaps return control back to SOAP::Lite.

[36]For Version 1.2, see http://www.w3.org/TR/soap12-part1/.

Admittedly, most of SOAP::Lite doesn't require a fingernail's width of knowledge about XML processing in Perl, as most applications will probably be content with its prepackaged functionality. If you want to get really tricky with it, though, it welcomes your meddling. Knowledge is power, my friend.

That's all for our sampling of Perl and XML applications. Next, we'll talk about some strategies for building our own applications.

Previous | Home | Next
9.3. XML Programming Tools | Book Index | 10. Coding Strategies
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Coding Strategies (Perl and XML)

 Perl & XMLSearch this book

Chapter 10. Coding Strategies

Contents:

Perl and XML Namespaces
Subclassing
Converting XML to HTML with XSLT
A Comics Index

This chapter sends you off by bringing this book's topics full circle. We return to many of the themes about XML processing in Perl that we introduced in Chapter 3, "XML Basics: Reading and Writing", but in the context of all the detailed material that we've covered in the interceding chapters. Our intent is to take you on one concluding tour through the world of Perl and XML, with its strategies and its gotchas, before sending you on your way.

10.1. Perl and XML Namespaces

You've seen XML namespaces used since we first mentioned this concept back in Chapter 2, "An XML Recap". Many XML applications, such as XSLT, insist that all their elements claim fealty to a certain namespace. The deciding factor here usually involves how symbiotic the application is in its usual use: does it usually work on its own, with a one-document-per-application style, or does it tend to mix with other sorts of XML?

DocBook XML, for example, is not very symbiotic. An instance of DocBook is almost always a whole XML document, defining a book or an article, and all the elements within such a document that aren't explicitly tied to some other namespace are found in the official DocBook documentation.[37] However, within a DocBook document, you might encounter a clump of MathML elements making their home in a rather parasitic fashion, nestled in among the folds of the DocBook elements, from which it derives nourishing context.

[37]See http://www.docbook.org or O'Reilly's DocBook: The Definitive Guide.

This sort of thing is useful for two reasons: first, DocBook, while its element spread tries to cover all kinds of things you might find in a piece of technical documentation,[38] doesn't have the capacity to richly describe everything that might go into a mathematical equation. (It does have <equation> elements, but they are often used to describe the nature of the graphic contained within them.) By adding MathML into the mix, you can use all the tags defined by that markup language's specification inside of a DocBook document, tucked away safely in their own namespace. (Since MathML and DocBook work so well together, the DocBook DTD allows a user to plug in a "MathML module," which adds a <mml:math> element to the mix. Within this mix, everything is handled by MathML's own DTD, which the module imports (along with DocBook's main DTD) into the whole DTD-space when validating.)

[38]Some would say, in fact, that it tries a little too hard; hence the existence of trimmed-down variants such as Simplified DocBook.

Second, and perhaps more interesting from the parser's point of view, tags existing in a given namespace work like embassies; while you stand on its soil (or in its scope), all that country's rules and regulations apply to you, despite the embassy's location in a foreign land. XML namespaces are also similar to Perl namespaces, which let you invoke variables, subroutines, and other symbols that live inside Some::Other::Package, though you may not have defined them within the default main package (or whatever package you are working in).

In other words, the presence of a namespace often indicates that another, separate XML application is invoked within the current one. Thus, if you are writing a processor to handle a type of XML application and you know that a certain namespace will probably pop up within it, you can save yourself a lot of work by passing off the work to another Perl module that knows how to handle things in that other application.

URI Identifiers

Many XML technologies, such as XML namespaces, SAX2, and SOAP, rely on URIs as unique identifiers -- strings that differentiate features or properties to prevent ideological conflicts. Any processor that reads it can be absolutely sure that it's referring to the technology intended by the author. URIs used in this way often look like URLs, usually of the http:// variety, which implies that typing them into a web browser will cause something to happen. However, sometimes the only result is a disappointing HTTP 404 response. URIs, unlike URLs, don't have to point to an actual resource; they only have to be globally unique.

Developers who need to assign a new URI to something often base them on URLs leading to web sites they have some control over. For example, if you have exclusive control over http://www.greenmonkey-markup.com/~jmac, then you can assign URIs based on it, such as http://www.greenmonkey-markup/~jmac/monkeyml/. Even without a response, you are still guaranteed that nobody else will ever use that URI. However, polite developers tend to put something at these URIs -- preferably documentation about the technology that uses them.

Another popular solution involves using a service such as http://purl.org (no relation to Perl), which can put a layer of indirection between a URI you use as a namespace and the location that houses its documentation, letting you change the latter at will while keeping the former constant.

Sometimes a URI does convey information besides mere uniqueness. For example, many XML application processors are sticklers about URIs used to declare XML namespaces, with good reason. XSLT processors, for example, usually don't care that all stylesheet XSLT elements have the usual xsl: prefix, as much as they care what URI that prefix is bound to, in the appropriate xmlns:-prefixed attribute. Knowing what URI the prefix is bound to assures the processor that you're using, for example, the W3C's most recent version of XSLT, and not a pre-1.0 version that some bleeding-edge processor adopted (that has its own namespace).

Robin Berjon's XML::NamespaceSupport module, available on CPAN, can help you process XML documents that use namespaces and manage their prefix-to-URI mappings.

For example, let's say that on your machine you have an XML file whose document keeps a list of the monkeys living in your house. Much of this file contains elements of your own design, but because you are both crafty and lazy, your document also uses the Monkey Markup Language, a standard way to describe monkeys with XML. Because it's designed for use in larger documents, it defines its own namespace:

 <?xml version="1.0">
<monkey-list>
 <monkey>
  <description xmlns:mm="http://www.jmac.org/projects/monkeys/mm/">
   <mm:monkey> <!-- start of monkey section -->
    <mm:name>Virtram</mm:name>
    <mm:color>teal</mm:color>
    <mm:favorite-foods>
     <mm:food>Banana</mm:food> <mm:food>Walnut</mm:food>
    </mm:favorite-foods>
    <mm:personality-matrix>
     F6 30 00 0A 1B E7 9C 20
    </mm:personality-matrix>
   </mm:monkey>
  </description>
  <location>Living Room</location>
  <job>Scarecrow</job>
 </monkey>
 <!-- Put more monkeys here later -->
</monkey-list>

Luckily, we have a Perl module on our system, XML::MonkeyML, that can parse a MonkeyML document into an object. This module is useful because the XML::MonkeyML class contains code for handling MonkeyML's personality-matrix element, which condenses a monkey's entire personality down to a short hexadecimal code. Let's write a program that predicts how all our monkeys will react in a given situation:

#!/usr/bin/perl

# This program takes an action specified on the command line, and
# applies it to every monkey listed in a monkey-list XML document
# (whose filename is also supplied on the command line)

use warnings;
use strict;

use XML::LibXML;
use XML::MonkeyML;

my ($filename, $action) = @ARGV;

unless (defined ($filename) and defined ($action)) {
  die "Usage: $0 monkey-list-file action\n";
}

my $parser = XML::LibXML->new;
my $doc = $parser->parse_file($filename);

# Get all of the monkey elements
my @monkey_nodes = $parser->documentElement->findNodes("//monkey/description/mm:monkey");

foreach (@monkey_nodes) {
  my $monkeyml = XML::MonkeyML->parse_string($_->toString);
  my $name = $monkeyml->name . " the " . $monkeyml->color . " monkey";
  print "$name would react in the following fashion:\n";
  # The magic MonkeyML 'action' object method takes an English
  # description of an action performed on this monkey, and returns a
  # phrase describing the monkey's reaction.
  print $monkeyml->action($action); print "\n";
}

Here is the output:

$ ./money_action.pl monkeys.xml "Give it a banana"

Virtram the teal monkey would react in the following fashion:
Take the banana. Eat it. Say "Ook".

Speaking of laziness, let's look at how a programmer might create a helper module like XML::MonkeyML.

Previous | Home | Next
9.4. SOAP::Lite | Book Index | 10.2. Subclassing
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Subclassing (Perl and XML)

 Perl & XMLSearch this book

10.2. Subclassing

When writing XML-hacking Perl modules, another path to laziness involves standing on (and reading over) the shoulders of giants by subclassing general XML parsers as a quick way to build application-specific modules.

You don't have to use object inheritance; the least complicated way to accomplish this sort of thing involves constructing a parser object in the usual way, sticking it somewhere convenient, and turning around whenever you want to do something XMLy. Here is some bogus code for you:

package XML::MyThingy;

use strict; use warnings;
use XML::SomeSortOfParser;

sub new {
  # Ye Olde Constructor
  my $invocant = shift;
  my $self = {};
  if (ref($invocant)) {
    bless ($self, ref($invocant));
  } else {
    bless ($self, $invocant);
  }

  # Now we make an XML parser...
  my $parser = XML::SomeSortOfParser->new
      or die "Oh no, I couldn't make an XML parser. How very sad.";

  # ...and stick it on this object, for later reference.
  $self->{xml} = $parser;
  return $self;
}

sub parse_file {

  # We'll just pass on the user's request to our parser object (which
  # just happens to have a method named parse_file)...
  my $self = shift;
  my $result = $self->{xml}->parse_file;

  # What happens now depends on whatever a XML::SomeSortOfParser
  # object does when it parses a file. Let's say it modifies itself and
  # returns a success code, so we'll just keep hold of the now-modified
  # object under this object's 'xml' key, and return the code.
  return $result;
}

Choosing to subclass a parser has some bonuses, though. First, it gives your module the same basic user API as the module in question, including all the methods for parsing, which can be quite lazily useful -- especially if the module you're writing is an XML application helper module. Second, if you're using a tree-based parser, you can steal -- er, I mean embrace and extend -- that parser's data structure representation of the parsed document and then twist it to better serve your own nefarious goal while doing as little extra work as possible. This step is possible through the magic of Perl's class blessing and inheritance functionality.

10.2.1. Subclassing Example: XML::ComicsML

For this example, we're going to set our notional MonkeyML aside in favor of the grim reality of ComicsML, a markup language for describing online comics.[39] It shares a lot of features and philosophies with RSS, providing, among other things, a standard way for comics to share web-syndication information, so a ComicsML helper module might be a boon for any Perl hacker who wishes to write programs that work with syndicated web comics.

[39]See http://comicsml.jmac.org/.

We will go down a DOMmish path for this example and pull XML::LibXML down as our internal mechanism of choice, since it's (mostly) DOM compliant and is a fast parser. Our goal is to create a fully object-oriented API for manipulating ComicsML documents and all the major child elements within them:

use XML::ComicsML;

# parse an existing ComicsML file
my $parser = XML::ComicsML::Parser->new;
my $comic = $parser->parsefile('my_comic.xml');

my $title = $comic->title;
print "The title of this comic is $title\n";

my @strips = $comic->strips;
print "It has ".scalar(@strips)." strips associated with it.\n";

Without further ado, let's start coding.

package XML::ComicsML;

# A helper module for parsing and generating ComicsML documents.

use XML::LibXML;
use base qw(XML::LibXML);

# PARSING

# We catch the output of all XML::LibXML parsing methods in our hot
# little hands, then proceed to rebless selected nodes into our own
# little clasees

sub parse_file {
  # Parse as usual, but then rebless the root element and return it.
  my $self = shift;
  my $doc = $self->SUPER::parse_file(@_);
  my $root = $doc->documentElement;
  return $self->rebless($root);
}

sub parse_string {
  # Parse as usual, but then rebless the root element and return it.
  my $self = shift;
  my $doc = $self->SUPER::parse_string(@_);
  my $root = $doc->documentElement;
  return $self->rebless($root);
}

What exactly are we doing, here? So far, we declared the package to be a child of XML::LibXML (by way of the use base pragma), but then we write our own versions of its three parsing methods. All do the same thing, though: they call XML::LibXML's own method of the same name, capture the root element of the returned document tree object, and then pass it to these internal methods:

sub rebless {

  # Accept  some kind of XML::LibXML::Node (or a subclass
  # thereof) and, based on its name, rebless it into one of
  # our ComicsML classes.
  my $self = shift;
  my ($node) = @_;

  # Define a has of interesting element types. (hash for easier searching.)
  my %interesting_elements = (comic=>1,
                              person=>1,
                              panel=>1,
                              panel-desc=>1,
                              line=>1,
                              strip=>1,
                             );

  # Toss back this node unless it's an Element, and Interesting. Else,
  # carry on.
    my $name = $node->getName;
    return $node unless ( (ref($node) eq 'XML::LibXML::Element')
        and (exists($interesting_elements{$name})) );

    # It is an interesting element! Figure out what class it gets, and rebless it.
    my $class_name = $self->element2class($name);
    bless ($node, $class_name);
  return $node;
}

sub element2class {

  # Munge an XML element name into something resembling a class name.
  my $self = shift;
  my ($class_name) = @_;
  $class_name = ucfirst($class_name);
  $class_name =~ s/-(.?)/uc($1)/e;
  $class_name = "XML::ComicsML::$class_name";
}

The rebless method takes an element node, peeks at its name, and sees if it appears on a hardcoded list it has of "interesting" element names. If it appears on the list, it chooses a class name for it (with the help of that silly element2class method) and reblesses it into that class.

This behavior may seem irrational until you consider the fact that XML::LibXML objects are not very persistent, due to the way they are bound with the low-level, C-based structures underneath the Perly exterior. If I get a list of objects representing some node's children, and then ask for the list again later, I might not get the same Perl objects, though they'll both work (being APIs to the same structures on the C library-produced tree). This lack of persistence prevents us from, say, crawling the whole tree as soon as the document is parsed, blessing the "interesting" elements into our own ComicsML-specific classes, and calling it done.

To get around this behavior, we do a little dirty work, quietly turning the Element objects that XML::LibXML hands us into our own kinds of objects, where applicable. The main advantage of this, beyond the egomaniacal glee of putting our own (class) name on someone else's work, is the fact that these reblessed objects are now subject to having some methods of our own design called on them. Now we can finally define these classes.

First, we will taunt you by way of the AUTOLOAD method that exists in XML::ComicsML::Element, a virtual base class from which our "real" element classes all inherit. This glop of code lords it over all our element classes' basic child-element and attribute accessors; when called due to the invocation of an undefined method (as all AUTOLOAD methods answer to), it first checks to see if the method exists in that class's hardcoded list of legal child elements and attributes (available through the element() and attribute() methods, respectively); failing that, if the method had a name like add_foo or remove_foo, it enters either constructor or destructor mode:

package XML::ComicsML::Element;

# This is an abstract class for all ComicsML Node types.
use base qw(XML::LibXML::Element);
use vars qw($AUTOLOAD @elements @attributes);

sub AUTOLOAD {
  my $self = shift;
  my $name = $AUTOLOAD;
  $name =~ s/^.*::(.*)$/$1/;
  my @elements = $self->elements;
  my @attributes = $self->attributes;
  if (grep (/^$name$/, @elements)) {

    # This is an element accessor.
    if (my $new_value = $_[0]) {
      # Set a value, overwriting that of any current element of this type.
      my $new_node = XML::LibXML::Element->new($name);
      my $new_text = XML::LibXML::Text->new($new_value);
      $new_node->appendChild($new_text);
      my @kids = $new_node->childNodes;
      if (my ($existing_node) = $self->findnodes("./$name")) {
        $self->replaceChild($new_node, $existing_node);
      } else {
        $self->appendChild($new_node);
      }
    }

    # Return the named child's value.
    if (my ($existing_node) = $self->findnodes("./$name")) {
      return $existing_node->firstChild->getData;
    } else {
      return '';
    }

  } elsif (grep (/^$name$/, @attributes)) {
    # This is an attribute accessor.
    if (my $new_value = $_[0]) {
      # Set a value for this attribute.
      $self->setAttribute($name, $new_value);
    }

    # Return the names attribute's value.
    return $self->getAttribute($name) || '';

    # These next two could use some error-checking.
  } elsif ($name =~ /^add_(.*)/) {
    my $class_to_add = XML::ComicsML->element2class($1);
    my $object = $class_to_add->new;
    $self->appendChild($object);
    return $object;

  } elsif ($name =~ /^remove_(.*)/) {
    my ($kid) = @_;
    $self->removeChild($kid);
    return $kid;
  }

}

# Stubs

sub elements {
  return ();
}

sub attributes {
  return ();
}

package XML::ComicsML::Comic;
use base qw(XML::ComicsML::Element);

sub elements {
  return qw(version title icon description url);
}

sub new {
  my $class = shift;
  return $class->SUPER::new('comic');
}

sub strips {
  # Return a list of all strip objects that are children of this comic.
  my $self = shift;
  return map {XML::ComicsML->rebless($_)}  $self->findnodes("./strip");
}

sub get_strip {
  # Given an ID, fetch a strip with that 'id' attribute.
  my $self = shift;
  my ($id) = @_;
  unless ($id) {
    warn "get_strip needs a strip id as an argument!";
    return;
  }
  my (@strips) = $self->findnodes("./strip[attribute::id='$id']");
  if (@strips > 1) {
    warn "Uh oh, there is more than one strip with an id of $id.\n";
  }
  return XML::ComicsML->rebless($strips[0]);
}

Many more element classes exist in the real-life version of ComicsML -- ones that deal with people, strips within a comic, panels within a strip, and so on. Later in this chapter, we'll take what we've written here and apply it to an actual problem.

Previous | Home | Next
10. Coding Strategies | Book Index | 10.3. Converting XML to HTML with XSLT
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Converting XML to HTML with XSLT (Perl and XML)

 Perl & XMLSearch this book

10.3. Converting XML to HTML with XSLT

If you've done any web hacking with Perl before, then you've kinda-sorta used XML, since HTML isn't too far off from the well-formedness goals of XML, at least in theory. In practice, HTML is used more frequently as a combination of markup, punctuation, embedded scripts, and a dozen other things that make web pages act nutty (with most popular web browsers being rather forgiving about syntax).

Currently, and probably for a long time to come, the language of the Web remains HTML. While you can use bona fide XML in your web pages by clinging to the W3C's XHTML,[40] it's far more likely that you'll need to turn it into HTML when you want to apply your XML to the Web.

[40]XHTML comes in two flavors. We prefer the less pendantic "transitional" flavor, which chooses to look the other way when one commits egregious sins (such as using the <font> tag instead of the preferred method of applying cascading stylesheets).

You can go about this in many ways. The most sledgehammery of these involves parsing your document and tossing out the results in a CGI script. This example reads a local MonkeyML file of my pet monkeys' names, and prints a web page to standard output (using Lincoln Stein's ubiquitous CGI module to add a bit of syntactic sugar):

#!/usr/bin/perl

use warnings;
use strict;
use CGI qw(:standard);
use XML::LibXML;

my $parser = XML::XPath;
my $doc = $parser->parse_file('monkeys.xml');

print header;
print start_html("My Pet Monkeys");
print h1("My Pet Monkeys");
print p("I have the following monkeys in my house:");
print "<ul>\n";
foreach my $name_node ($doc->documentElement->findnodes("//mm:name")) {
    print "<li>" . $name_node->firstChild->getData ."</li>\n";
}

print end_html;

Another approach involves XSLT.

XSLT is used to translate one type of XML into another. XSLT factors in strongly here because using XML and the Web often requires that you extract all the presentable pieces of information from an XML document and wrap them up in HTML. One very high-level XML-using application, Matt Sergeant's AxKit (http://www.axkit.org), bases an entire application server framework around this notion, letting you set up a web site that uses XML as its source files, but whose final output to web browsers is HTML (and whose final output to other devices is whatever format best applies to them).

10.3.1. Example: Apache::DocBook

Let's make a little module that converts DocBook files into HTML on the fly. Though our goals are not as ambitious as AxKit's, we'll still take a cue from that program by basing our code around the Apache mod_perl module. mod_perl drops a Perl interpreter inside the Apache web server, and thus allows one to write Perl code that makes all sorts of interesting things happen with requests to the server.

We'll use a couple of mod_perl's basic features here by writing a Perl module with a handler subroutine, the standard name for mod_perl callbacks; it will be passed an object representing the Apache request, and from this object, we'll determine what (if anything) the user sees.

WARNING: A frequent source of frustration for people running Perl and XML programs in an Apache environment comes from Apache itself, or at least the way it behaves if it's not given a few extra configuration directives when compiled. The standard Apache distribution comes with a version of the Expat C libraries, which it will bake into its binary if not explicitly told otherwise. Unfortunately, these libraries often conflict with XML::Parser's calls to Expat libraries elsewhere on the system, resulting in nasty errors (such as segmentation faults on Unix) when they collide.

The Apache development community has reportedly considered quietly removing this feature in future releases, but currently, it may be necessary for Perl hackers wishing to invoke Expat (usually by way of XML::Parser) to recompile Apache without it (by setting the EXPAT configuration option to no).

The cheaper workaround involves using a low-level parsing module that doesn't use Expat, such as XML::LibXML or members of the newer XML::SAX family.

We begin by doing the "starting to type in the module" dance, and then digging into that callback sub:

package Apache::DocBook;

use warnings;
use strict;

use Apache::Constants qw(:common);

use XML::LibXML;
use XML::LibXSLT;

our $xml_path;                        # Document source directory
our $base_path;                        # HTML output directory
our $xslt_file;                        # path to DocBook-to-HTML XSLT stylesheet
our $icon_dir;                         # path to icons used in index pages

sub handler {
  my $r = shift;                # Apache request object
  # Get config info from Apache config
  $xml_path = $r->dir_config('doc_dir') or die "doc_dir variable not set.\n";
  $base_path = $r->dir_config('html_dir') or die "html_dir variable not set.\n";
  $icon_dir = $r->dir_config('icon_dir') or die "icon_dir variable not set.\n";
  unless (-d $xml_path) {
    $r->log_reason("Can't use an xml_path of $xml_path: $!", $r->filename);
    die;
  }
  my $filename = $r->filename;

    $filename =~ s/$base_path\/?//;
    # Add in path info (the file might not actually exist... YET)
  $filename .= $r->path_info;

  $xslt_file = $r->dir_config('xslt_file') or die "xslt_file Apache variable not set.\n";

  # The subroutines we'll call after this will take care of printing
  # stuff at the client.

  # Is this an index request?
    if ( (-d "$xml_path/$filename") or ($filename =~ /index.html?$/) ) {
    # Why yes! We whip up an index page from the floating aethers.
    my ($dir) = $filename =~ /^(.*)(\/index.html?)?$/;
    # Semi-hack: stick trailing slash on URI, maybe.
    if (not($2) and $r->uri !~ /\/$/) {
      $r->uri($r->uri . '/');
    }
    make_index_page($r, $dir);
    return $r->status;
  } else {
    # No, it's a request for some other page.
    make_doc_page($r, $filename);
    return $r->status;
  }
  return $r->status;

}

This subroutine performs the actual XSLT transformation, given a filename of the original XML source and another filename to which it should write the transformed HTML output:

sub transform {
  my ($filename, $html_filename) = @_;

  # make sure there's a home for this file.
  maybe_mkdir($filename);

  my $parser = XML::LibXML->new;
  my $xslt = XML::LibXSLT->new;

  # Because libxslt seems a little broken, we have to chdir to the
  # XSLT file's directory, else its file includes won't work. ;b
  use Cwd;                      # so we can get the current working dir
  my $original_dir = cwd;
  my $xslt_dir = $xslt_file;
  $xslt_dir =~ s/^(.*)\/.*$/$1/;
  chdir($xslt_dir) or die "Can't chdir to $xslt_dir: $!";

  my $source = $parser->parse_file("$xml_path/$filename");
  my $style_doc = $parser->parse_file($xslt_file);

  my $stylesheet = $xslt->parse_stylesheet($style_doc);

  my $results = $stylesheet->transform($source);

  open (HTML_OUT, ">$base_path/$html_filename");
  print HTML_OUT $stylesheet->output_string($results);
  close (HTML_OUT);

  # Go back to original dir
  chdir($original_dir) or die "Can't chdir to $original_dir: $!";

}

Now we have a pair of subroutines to generate index pages. Unlike the document pages, which are the product of an XSLT transformation, we make the index pages from scratch, the bulk of its content being a table filled with information we grab from the document via XPath (looking first in the appropriate metadata element if present, and falling back to other bits of information if not).

sub make_index_page {
  my ($r, $dir) = @_;

  # If there's no corresponding dir in the XML source, the request
  # goes splat

  my $xml_dir = "$xml_path/$dir";
  unless (-r $xml_dir) {
    unless (-d $xml_dir) {
      # Whoops, this ain't a directory.
      $r->status( NOT_FOUND );
      return;
    }
    # It's a directory, but we can't read it. Whatever.
    $r->status( FORBIDDEN );
    return;
  }

  # Fetch mtimes of this dir and the index.html in the corresponding
  # html dir
  my $index_file = "$base_path/$dir/index.html";

  my $xml_mtime = (stat($xml_dir))[9];
  my $html_mtime = (stat($index_file))[9];

  # If the index page is older than the XML dir, or if it simply
  # doesn't exist, we generate a new one.
    if ((not($html_mtime)) or ($html_mtime <= $xml_mtime)) {
    generate_index($xml_dir, "$base_path/$dir", $r->uri);
    $r->filename($index_file);
    send_page($r, $index_file);
    return;
  } else {
    # The cached index page is fine. Let Apache serve it.
    $r->filename($index_file);
    $r->path_info('');
    send_page($r, $index_file);
    return;
  }

}

sub generate_index {
  my ($xml_dir, $html_dir, $base_dir) = @_;

  # Snip possible trailing / from base_dir
  $base_dir =~ s|/$||;

  my $index_file = "$html_dir/index.html";

  my $local_dir;
  if ($html_dir =~ /^$base_path\/*(.*)/) {
    $local_dir = $1;
  }

  # make directories, if necessary
  maybe_mkdir($local_dir);
  open (INDEX, ">$index_file") or die "Can't write to $index_file: $!";

  opendir(DIR, $xml_dir) or die "Couldn't open directory $xml_dir: $!";
  chdir($xml_dir) or die "Couldn't chdir to $xml_dir: $!";

  # Set icon files
  my $doc_icon = "$icon_dir/generic.gif";
  my $dir_icon = "$icon_dir/folder.gif";

  # Make the displayable name of $local_dir (probably the same)
  my $local_dir_label = $local_dir || 'document root';

  # Print start of page
  print INDEX <<END;
<html>
<head><title>Index of $local_dir_label</title></head>
<body>
<h1>Index of $local_dir_label</h1>
<table width="100%">
END

  # Now print one row per file in this dir

  while (my $file = readdir(DIR)) {
    # ignore dotfiles & directories & stuff
    if (-f $file && $file !~ /^\./) {
      # Create parser objects
      my $parser = XML::LibXML->new;

      # Check for well-formedness, skip if yukky:
      eval {$parser->parse_file($file);};
      if ($@) {
        warn "Blecch, not a well-formed XML file.";
        warn "Error was: $@";
        next;
      }

      my $doc = $parser->parse_file($file);

      my %info;                   # Will hold presentable info

      # Determine root type
      my $root = $doc->documentElement;
      my $root_type = $root->getName;

      # Now try to get an appropriate info node, which is named $FOOinfo
      my ($info) = $root->findnodes("${root_type}info");
      if ($info) {

        # Yay, an info element for us. Fill it %info with it.
        if (my ($abstract) = $info->findnodes('abstract')) {
          $info{abstract} = $abstract->string_value;
        } elsif ($root_type eq 'reference') {

          # We can usee first refpurpose as our abstract instead.
          if ( ($abstract) = $root->findnodes('/reference/refentry/refnamediv/refpurpose')) {
            $info{abstract} = $abstract->string_value;
          }
        }
        if (my ($date) = $info->findnodes('date')) {
          $info{date} = $date->string_value;
        }
      }
      if (my ($title) = $root->findnodes('title')) {
        $info{title} = $title->string_value;
      }

      # Fill in %info stuff we don't need the XML for...
      unless ($info{date}) {
        my $mtime = (stat($file))[9];
        $info{date} = localtime($mtime);
      }
      $info{title} ||= $file;

      # That's enough info. Let's build an HTML table row with it.
      print INDEX "<tr>\n";

      # Figure out a filename to link to -- foo.html
      my $html_file = $file;
      $html_file =~ s/^(.*)\..*$/$1.html/;
      print INDEX "<td>";
      print INDEX "<img src=\"$doc_icon\">" if $doc_icon;
      print INDEX "<a href=\"$base_dir/$html_file\">$info{title}</a></td> ";
      foreach (qw(abstract date)) {
        print INDEX "<td>$info{$_}</td> " if $info{$_};
      }
      print INDEX "\n</tr>\n";
    } elsif (-d $file) {

      # Just make a directory link.
      # ...unless it's an ignorable directory.
      next if grep (/^$file$/, qw(RCS CVS .)) or ($file eq '..' and not $local_dir);
      print INDEX "<tr>\n<td>";
      print INDEX "<a href=\"$base_dir/$file\"><img src=\"$dir_icon\">" if $dir_icon;
      print INDEX "$file</a></td>\n</tr>\n";
    }
  }

  # Close the table and end the page
  print INDEX <<END;
</table>
</body>
</html>
END

  close(INDEX) or die "Can't close $index_file: $!";
  closedir(DIR) or die "Can't close $xml_dir: $!";
}

These subroutines build on the transformation subroutine by generating the pages. Note the use of caching by comparing the timestamps of the source DocBook file and the destination HTML file and rewriting the latter only if it's older than the former. (Of course, if there's no HTML at all, then it always creates a new web page.)

sub make_doc_page {
  my ($r, $html_filename) = @_;

  # Generate a source filename by replacing existing extension with .xml
  my $xml_filename = $html_filename;
  $xml_filename =~ s/^(.*)(?:\..*)$/$1.xml/;

  # If there's a problem reading the source XML file, so it goes with
  # the result HTML.
  unless (-r "$xml_path/$xml_filename") {
    unless (-e "$xml_path/$xml_filename") {
      $r->status( NOT_FOUND );
      return;
    } else {
      # Exists, but no read permissions, shrug.
      $r->status( FORBIDDEN );
      return;
    }
  }

  # Fetch mtimes of this file and corresponding html file
  my $xml_mtime = (stat("$xml_path/$xml_filename"))[9];
  my $html_mtime = (stat("$base_path/$html_filename"))[9];
  # If the html file is older than the xml XML file, or if it simply
  # doesn't exist, generate a new one

  if ((not($html_mtime)) or ($html_mtime <= $xml_mtime)) {
    transform($xml_filename, $html_filename);
    $r->filename("$base_path/$html_filename");
    $r->status( DECLINED );
    return;
  } else {
    # It's cached. Let Apache serve up the existing file.
    $r->status( DECLINED );
  }
}

sub send_page {
  my ($r, $html_filename) = @_;
  # Problem here: if we're creating the file, we can't just write it
  # and say 'DECLINED', cuz the default server handle hits us with a
  # file-not-found. Until I find a better solution, I'll just spew
  # the file, and DECLINE only known cache-hits.
  $r->status( OK );
  $r->send_http_header('text/html');

  open(HTML, "$html_filename") or die "Couldn't read $base_path/$html_filename: $!";
  while (<HTML>) {
    $r->print($_);
  }
  close(HTML) or die "Couldn't close $html_filename: $!";
  return;
}

Finally, we have a utility subroutine to help us with a tedious task: copying the structure of subdirectories in the cache directory that mirrors that of the source XML directory:

sub maybe_mkdir {
  # Given a path, make sure directories leading to it exist, mkdir-ing
  # any that dont.
  my ($filename) = @_;
  my @path_parts = split(/\//, $filename);
  # if the last one is a filename, toss it out.
  pop(@path_parts) if -f $filename;
  my $traversed_path = $base_path;
  foreach (@path_parts) {
    $traversed_path .= "/$_";
    unless (-d $traversed_path) {
      mkdir ($traversed_path) or die "Can't mkdir $traversed_path: $!";
    }
  }
  return 1;
}
Previous | Home | Next
10.2. Subclassing | Book Index | 10.4. A Comics Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Comics Index (Perl and XML)

 Perl & XMLSearch this book

10.4. A Comics Index

XSLT is one thing, but the potential for Perl, XML, and the Web working together is as unlimited as, well, anything else you might choose to do with Perl and the Web. Sometimes you can't just toss refactored XML at your clients, but must write Perl that wrings interesting information out of XML documents and builds something Webbish out of the results. We did a little of that in the previous example, mixing the raw XSLT usage when transforming the DocBook documents with index page generation.

Since we've gone through all the trouble of covering syndication-enabling XML technologies such as RSS and ComicsML in this chapter and Chapter 9, "RSS, SOAP, and Other XML Applications ", let's write a little program that uses web syndication. To prove (or perhaps belabor) a point, we'll construct a simple CGI program that builds an index of the user's favorite online comics (which, in our fantasy world, all have ComicsML documents associated with them):

#!/usr/bin/perl

# A very simple ComicsML muncher; given a list of URLs pointing to
# ComicsML documents, fetch them, flatten their strips into one list,
# and then build a web page listing, linking to, and possibly
# displaying these strips, sorted with newest first.

use warnings;
use strict;

use XML::ComicsML;                # ...so that we can build ComicsML objects
use CGI qw(:standard);
use LWP;
use Date::Manip;             # Cuz we're too bloody lazy to do our own date math

# Let's assume that the URLs of my favorite Internet funnies' ComicsML
# documents live in a plaintext file on disk, with one URL per line
# (What, no XML? For shame...)

my $url_file = $ARGV[0] or die "Usage: $0 url-file\n";

my @urls;                        # List of ComicsML URLs
open (URLS, $url_file) or die "Can't read $url_file: $!\n";
while (<URLS>) { chomp; push @urls, $_; }
close (URLS) or die "Can't close $url_file: $!\n";

# Make an LWP user agent
my $ua = LWP::UserAgent->new;
my $parser = XML::ComicsML->new;

my @strips; # This will hold objects representing comic strips

foreach my $url (@urls) {
  my $request = HTTP::Request->new(GET=>$url);
  my $result = $ua->request($request);
  my $comic;                        # Will hold the comic we'll get back
  if ($result->is_success) {
    # Let's see if the ComicsML parser likes it.
    unless ($comic = $parser->parse_string($result->content)) {
      # Doh, this is not a good XML document.
      warn "The document at $url is not good XML!\n";
      next;
    }
  } else {
    warn "Error at $url: " . $result->status_line . "\n";
    next;
  }
  # Now peel all the strips out of the comic, pop each into a little
  # hashref along with some information about the comic itself.
  foreach my $strip ($comic->strips) {
    push (@strips, {strip=>$strip, comic_title=>$comic->title, comic_url=>$comic->url});
  }
}

# Sort the list of strips by date.  (We use Date::Manip's exported
# UnixDate function here, to turn their unweildy Gregorian calendar
# dates into nice clean Unixy ones)
my @sorted = sort {UnixDate($$a{strip}->date, "%s") <=> UnixDate($$b{strip}->date, "%s")} @strips;

# Now we build a web page!

print header;
print start_html("Latest comix");
print h1("Links to new comics...");

# Go through the sorted list in reverse, to get the newest at the top.
foreach my $strip_info (reverse(@sorted)) {
  my ($title, $url, $svg);
  my $strip = $$strip_info{strip};
  $title = join (" - ", $strip->title, $strip->date);
  # Hyperlink the title to a URL, if there is one provided
  if ($url = $strip->url) {
    $title = "<a href='$url'>$title</a>";
  }

  # Give similar treatment to the comics' title and URL
  my $comic_title = $$strip_info{comic_title};
  if ($$strip_info{comic_url}) {
    $comic_title = "<a href='$$strip_info{comic_url}'>$comic_title</a>";
  }

  # Print the titles
  print p("<b>$comic_title</b>: $title");

  print "<hr />";
}

print end_html;

Given the trouble we went through with that Apache::DocBook trifle a little earlier, this program might seem a tad too simple; it performs no caching, it contains no governors for how many strips it will process, and its sense of web page layout isn't much to write home about. For a quick hack, though, it works great and demonstrates the benefit of using helper modules like XML::ComicsML.

Our whirlwind tour of the world of Perl and XML ends here. As we said at the start of this book, the relationship between these two technologies is still young, and it only just started to reach for its full potential while we were writing this book, as new parsers like XML::LibXML and philosophies like PerlSAX2 emerged onto the scene during that time. We hope that we have given you enough information and encouragement to become part of this scene, as it will continue to unfold in increasingly interesting directions in the coming years.

<aloha>Happy hacking!</aloha>

Previous | Home | Next
10.3. Converting XML to HTML with XSLT | Book Index | Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Perl & XML: Index

Perl & XMLSearch this book

Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Symbols & Numbers

Perl & XMLSearch this book

Index: Symbols & Numbers

- (hyphen) in PYX notation: 4.5. XML::PYX
( ) (parenthesis) in PYX notation: 4.5. XML::PYX
[ ] (square brackets): 2.5. Entities
@ (at-sign) characters: 8.2. XPath
:: (double colon), using boolean tests in XPath: 8.2. XPath
? (question mark) in PYX notation: 4.5. XML::PYX
; (semicolons): 3.1. XML Parsers
/ (slashes): 2.9. Free-Form XML and Well-Formed Documents
well-formed documents and: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
XPath expressions: 8.2. XPath
& (ampersands): 2.5. Entities
3.8. XML::Writer
parsers and: 3.1. XML Parsers
well-formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
<! (delimiters): 3.7.1. DTDs
< > (angle brackets): 2.2. Markup, Elements, and Structure
2.9. Free-Form XML and Well-Formed Documents
parsers and: 3.1. XML Parsers
well-formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

A (attribute) in PYX notation: 4.5. XML::PYX
absolute locator in XPath: 8.2. XPath
ActiveState: 1.4. A Myriad of Modules
add_links( ) method: 7.3. XML::DOM
7.3. XML::DOM
add_parser( ) function: 5.7.1. XML::SAX::ParserFactory
addresses (code points): 3.9.2. Unicode Encodings
American National Standards Institute (ANSI): 2.1. A Brief History of XML
ampersands (&): 2.5. Entities
3.8. XML::Writer
parsers and: 3.1. XML Parsers
well-formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
ancestor nodes: 6.1. XML Trees
angle brackets (< >): 2.2. Markup, Elements, and Structure
2.9. Free-Form XML and Well-Formed Documents
parsers and: 3.1. XML Parsers
well-formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
ANSI (American National Standards Institute): 2.1. A Brief History of XML
appendChild method for Node class (DOM): 7.2.4.2. Methods
appendData method for CharacterData class (DOM): 7.2.7.2. Methods
application helpers (XML): 9.1. XML Modules
arrays in Perl: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
ASCII character encodings: 1.6. XML Gotchas
2.6. Unicode, Character Sets, and Encodings
at-sign characters (@): 8.2. XPath
attlist_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers
Attr class (DOM): 7.2.9.1. Properties
attribute list declarations: 2.10. Declaring Elements and Attributes
attributes
in PYX notation: 4.5. XML::PYX
in XML: 2.2. Markup, Elements, and Structure
declaring: 2.10. Declaring Elements and Attributes
attributes property for Node class (DOM): 7.2.4.1. Properties
awk program working with XML::PYX: 4.5. XML::PYX

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

base alphabetical glyphs: 3.9.2. Unicode Encodings
base classes (handlers): 5.5. A Handler Base Class
XML::Handler::YAWriter module and: 5.6. XML::Handler::YAWriter as a Base Handler Class
Berjon, Robin: 5.7. XML::SAX: The Second Generation
best matching rules: 2.12. Transformations
BOM (byte order mark): 3.9.4.3. Byte order marks
boolean operations in XPath: 8.2. XPath
Brian, Dan: 6.4. XML::SimpleObject
byte order mark (BOM): 3.9.4.3. Byte order marks

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

C programming language: 1.1. Why Use Perl with XML?
parsers, writing in: 3.2. XML::Parser
XML::LibXML module and: 3.5. XML::LibXML
call-back functions, using Subs parsing styles: 3.2.2. Parsing Styles
CDATA (Character DATA) section: 2.8. Processing Instructions and Other Markup
6.1. XML Trees
CDATASection class (DOM): 7.2.11. CDATASection
Character data node type: 6.1. XML Trees
character encodings: 1.6. XML Gotchas
2.6. Unicode, Character Sets, and Encodings
3.9. Character Sets and Encodings
character entities: 2.5. Entities
CharacterData class (DOM): 7.2.7. CharacterData
characters( ) method: 3.8. XML::Writer
5.7.2.1. Content event handlers
SAX event handlers: 5.1. SAX Event Handlers
child( ) method: 6.4. XML::SimpleObject
childNodes property for Node class (DOM): 7.2.4.1. Properties
children( ) method: 6.4. XML::SimpleObject
Chinese characters: 2.6. Unicode, Character Sets, and Encodings
Clark, James: 3.2. XML::Parser
cloneNode method for Node class (DOM): 7.2.4.2. Methods
code points in Unicode: 3.9.2. Unicode Encodings
comment( ) method: 3.8. XML::Writer
5.7.2.3. Lexical event handlers
Comment class (DOM): 7.2.13. Comment
Comment node type: 6.1. XML Trees
comment SAX method: 5.1. SAX Event Handlers
comments for XML: 2.8. Processing Instructions and Other Markup
Comprehensive Perl Archive Network (see CPAN)
container nodes in XML trees: 6.1. XML Trees
content handlers for SAX: 5.7.2. SAX2 Handler Interface
content models: 2.10. Declaring Elements and Attributes
context lists: 3.4. Putting Parsers to Work
conversions (encoding): 3.9.4. Encoding Conversion
converter program for stream processing: 4.4. Stream Applications
CPAN (Comprehensive Perl Archive Network): 0.1. Assumptions
1.1. Why Use Perl with XML?
modules, finding: 3.2. XML::Parser
XML programming tool and: 9.3.2. Further Ruminations on DBI and SAX
createAttribute method: 7.2.1.2. Methods
createAttributeNS method: 7.2.1.2. Methods
createCDATASection method: 7.2.1.2. Methods
createComment method: 7.2.1.2. Methods
createDocumentFragment method: 7.2.1.2. Methods
createElement method: 7.2.1.2. Methods
createElementNS method: 7.2.1.2. Methods
createEntityReference method: 7.2.1.2. Methods
createProcessingInstruction method: 7.2.1.2. Methods
createTextNode method: 7.2.1.2. Methods
current nodes in XPath expressions: 8.2. XPath
custom parsing style: 3.2.2. Parsing Styles

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

data parameters: 2.8. Processing Instructions and Other Markup
data property
CharacterData class (DOM): 7.2.7.1. Properties
ProcessingInstruction class (DOM): 7.2.12.1. Properties
Database Interface (DBI): 9.3.2. Further Ruminations on DBI and SAX
dataElement( ) Writer method: 3.8. XML::Writer
Data::Grove::Visitor module: 8.1. Tree Climbers
DBD::MySQL module: 9.3.2. Further Ruminations on DBI and SAX
DBD::Oracle module: 9.3.2. Further Ruminations on DBI and SAX
DBD::Pg module: 9.3.2. Further Ruminations on DBI and SAX
DBI (Database Interface): 9.3.2. Further Ruminations on DBI and SAX
debug parsing style: 3.2.2. Parsing Styles
declaration handlers for SAX: 5.7.2. SAX2 Handler Interface
declarations (XML): 1.6. XML Gotchas
2.7. The XML Declaration
elements: 2.10. Declaring Elements and Attributes
deleteData method for CharacterData class (DOM): 7.2.7.2. Methods
delimiters (<!): 3.7.1. DTDs
depth-first order in stacks: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
Derkson, Enno: 7.3. XML::DOM
descendant nodes: 6.1. XML Trees
destination formats: 2.1. A Brief History of XML
diacritical marks: 3.9.2. Unicode Encodings
<dict> element: 8.2. XPath
DocBook: 2.1. A Brief History of XML
8.4. Optimized Tree Processing
10.1. Perl and XML Namespaces
converting XML to HTML: 2.12. Transformations
4.4. Stream Applications
tree-based processing and: 8.4. Optimized Tree Processing
doctype( ) Writer method: 3.8. XML::Writer
DOCTYPE keyword: 2.5. Entities
doctype property: 7.2.1.1. Properties
doctype_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers
Document class in DOM: 7.2.1. Document
document elements: 2.5. Entities
2.9. Free-Form XML and Well-Formed Documents
document instance: 3.7. Document Validation
Document Object Model (see DOM)
document prologs in DTD handlers: 5.2. DTD Handlers
Document Type Descriptions (see DTDs)
document validation: 3.7. Document Validation
documentElement property: 7.2.1.1. Properties
DocumentFragment class (DOM): 7.2.2. DocumentFragment
documents (XML): 1.5. Keep in Mind...
DocumentType class (DOM), properties of: 7.2.3. DocumentType
DOM (Document Object Model): 3.5. XML::LibXML
7. DOM
class interface reference: 7.2. DOM Class Interface Reference
XML::DOM module: 7.3. XML::DOM
double colon (::), using boolean tests in XPath: 8.2. XPath
drivers for non-XML sources: 5.4. Drivers for Non-XML Sources
DTDs (Document Type Descriptions): 2. An XML Recap
2.5. Entities
3.7.1. DTDs
doctype property: 7.2.1.1. Properties
handlers: 5.2. DTD Handlers
5.7.2. SAX2 Handler Interface
well formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

ECMAscript: 7.1. DOM and Perl
Element node: 6.1. XML Trees
7.2.8. Element
element_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers
elements: 2.2. Markup, Elements, and Structure
declaring: 2.10. Declaring Elements and Attributes
types: 2.2. Markup, Elements, and Structure
emptyTag( ) Writer method: 3.8. XML::Writer
encoding conversions: 3.9.4. Encoding Conversion
encodings (character): 2.6. Unicode, Character Sets, and Encodings
3.9. Character Sets and Encodings
end( ) Writer methods: 3.8. XML::Writer
end_cdata( ) event handler: 5.1. SAX Event Handlers
5.7.2.3. Lexical event handlers
end_document( ) event handler: 5.1. SAX Event Handlers
5.7.2.1. Content event handlers
end_dtd( ) method: 5.7.2.3. Lexical event handlers
end_element( ) event handler: 5.1. SAX Event Handlers
5.7.2.1. Content event handlers
end_entity( ) event handler: 5.7.2.3. Lexical event handlers
end_prefix_mapping( ) method: 5.7.2.1. Content event handlers
endTag( ) Writer method: 3.8. XML::Writer
entities: 1.6. XML Gotchas
2.5. Entities
DocumentType class (DOM): 7.2.3.1. Properties
external resolution: 5.3. External Entity Resolution
parsers and: 3.1. XML Parsers
resolve_entity( ) module and: 5.7.2.2. Entity resolver
well formed documents, checking: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
Entity node: 6.1. XML Trees
7.2.14. EntityReference
entity references: 3.1. XML Parsers
entity_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers
EntityReference class: 7.2.14. EntityReference
entity_reference SAX method: 5.1. SAX Event Handlers
error( ) exception handler: 5.7.2.4. Error event handlers and catching exceptions
error handlers for SAX: 5.7.2. SAX2 Handler Interface
eval block, using XML::Parser module: 3.2.1. Example: Well-Formedness Checker Revisited
event handlers (see handlers)
event processing (parser): 3.3. Stream-Based Versus Tree-Based Processing
stream parsing styles and: 3.2.2. Parsing Styles
event streams: 4. Event Streams
applications: 4.4. Stream Applications
handlers: 4.2. Events and Handlers
working with: 4.1. Working with Streams
XML processing strategies and: 3.3. Stream-Based Versus Tree-Based Processing
XML::Parser module and: 4.6. XML::Parser
events: 4.2. Events and Handlers
Excel spreadsheets, converting to XML: 5.4. Drivers for Non-XML Sources
Expat parser library: 3.2. XML::Parser
using parsers and: 3.4. Putting Parsers to Work
expressions (XPath): 8.2. XPath
Extensible Markup Language (see XML)
external entity resolution: 5.3. External Entity Resolution
external subsets: 2.5. Entities

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

facets in element type descriptions: 2.11. Schemas
factory objects: 5.7. XML::SAX: The Second Generation
7.1. DOM and Perl
fatal_error( ) exception handler: 5.7.2.4. Error event handlers and catching exceptions
filters for stream processing: 4.4. Stream Applications
findnodes( ) method of Node class: 7.4. XML::LibXML
firstChild property for Node class (DOM): 7.2.4.1. Properties
flushing data: 8.4. Optimized Tree Processing
free-form XML: 2.9. Free-Form XML and Well-Formed Documents

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

GCA (Graphic Communications Association): 2.1. A Brief History of XML
general entities: 2.5. Entities
Generalized Markup Language (GML): 2.1. A Brief History of XML
generic coding in markup language: 2.1. A Brief History of XML
getAttribute method for Element node: 7.2.8.2. Methods
getAttributeNode method for Element node: 7.2.8.2. Methods
getAttributeNodeNS method for Element node: 7.2.8.2. Methods
getAttributeNS method for Element node: 7.2.8.2. Methods
getChildNodes( ) method: 3.5. XML::LibXML
7.3. XML::DOM
getElementById method: 7.2.1.2. Methods
getElementsByTagName method: 7.2.1.2. Methods
7.2.8.2. Methods
7.3. XML::DOM
getElementsByTagNameNS method: 7.2.1.2. Methods
for Element node: 7.2.8.2. Methods
getNamedItem class for Node class (DOM): 7.2.6.2. Methods
getNamedItemNS class for Node class (DOM): 7.2.6.2. Methods
getNodeValue( ) method: 7.3. XML::DOM
GML (Generalized Markup Language): 2.1. A Brief History of XML
GNOME project: 3.5. XML::LibXML
Goldfarb, Charles: 2.1. A Brief History of XML
Graphic Communications Association (GCA): 2.1. A Brief History of XML
grep program working with XML::PYX: 4.5. XML::PYX

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

Hampton, Kip: 5.7. XML::SAX: The Second Generation
Han Chinese characters: 2.6. Unicode, Character Sets, and Encodings
handlers: 3.4. Putting Parsers to Work
4.2. Events and Handlers
base classes for: 5.5. A Handler Base Class
DTD: 5.2. DTD Handlers
lexical event: 5.7.2.3. Lexical event handlers
for SAX: 5.1. SAX Event Handlers
5.7.2. SAX2 Handler Interface
stream parsing styles, using: 3.2.2. Parsing Styles
XML::Parser module and: 4.6. XML::Parser
hasAttribute method for Element node: 7.2.8.2. Methods
hasAttributeNS method for Element node: 7.2.8.2. Methods
hasAttributes method for Node class (DOM): 7.2.4.2. Methods
hasChildNodes method for Node class (DOM): 7.2.4.2. Methods
helper modules: 9.2. XML::RSS
hexadecimal character entities: 2.5. Entities
HTML (Hypertext Markup Language): 2.1. A Brief History of XML
converting: 2.12. Transformations
8.3. XSLT
10.3. Converting XML to HTML with XSLT
well-formed documents and: 3.1. XML Parsers
Hypertext Markup Language (see HTML)
hyphen (-) in PYX notation: 4.5. XML::PYX

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

iconv library: 3.9.4.1. iconv and Text::Iconv
id( ) module: 8.2. XPath
ideographic glyphs: 3.9.2. Unicode Encodings
ignorable_whitespace( ): 5.7.2.1. Content event handlers
importNode method: 7.2.1.2. Methods
input, when using XML::RSS module: 9.2.4. Input: User or File
insertBefore method for Node class (DOM): 7.2.4.2. Methods
insertData method for CharacterData class (DOM): 7.2.7.2. Methods
instances (document): 3.7. Document Validation
internal subsets: 2.5. Entities
internalSubset for DocumentType class (DOM): 7.2.3.1. Properties
Internet (see World Wide Web)
invalid XML documents: 3.1. XML Parsers
ISO Latin-1 characters: 2.6. Unicode, Character Sets, and Encodings
isSupported method for Node class (DOM): 7.2.4.2. Methods
item( ) method: 7.3. XML::DOM
item class for Node class (DOM): 7.2.6.2. Methods
item method for NodeList class (DOM): 7.2.5.2. Methods
iterators (tree climbers): 8.1. Tree Climbers

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

Java programming language: 1.1. Why Use Perl with XML?
5. SAX
7.1. DOM and Perl
JavaScript programming language: 1.1. Why Use Perl with XML?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: K

<key> element: 8.2. XPath

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

last-in, first-out (LIFO): 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
lastChild property for Node class (DOM): 7.2.4.1. Properties
length property
CharacterData class (DOM): 7.2.7.1. Properties
NameNodeMap class (DOM): 7.2.6.1. Properties
NodeList class (DOM): 7.2.5.1. Properties
lexical handlers: 5.7.2. SAX2 Handler Interface
5.7.2.3. Lexical event handlers
libxml2: 3.5. XML::LibXML
5.7.1. XML::SAX::ParserFactory
LibXML library: 7.4. XML::LibXML
LIFO (last-in, first-out): 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
location paths in XPath expressions: 8.2. XPath
locators in XPath: 8.2. XPath
LogDriver directory, installing parsers: 5.7.5. Installing Your Own Parser
loops, checking XML markup objects: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
Lorie, Raymond: 2.1. A Brief History of XML

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

MacLeod, Ken: 6.6. XML::Grove
Makefile.PL, installing parsers: 5.7.5. Installing Your Own Parser
markup languages: 2. An XML Recap
elements and structure for: 2.2. Markup, Elements, and Structure
MathML elements: 10.1. Perl and XML Namespaces
McLean, Grant: 1.2. XML Is Simple with XML::Simple
6.2. XML::Simple
Megginson, David: 5. SAX
methods (DOM): 7.2.6.2. Methods
Document class: 7.2.1.2. Methods
Elements node: 7.2.8.2. Methods
Node class: 7.2.4.2. Methods
NodeList class: 7.2.5.2. Methods
<mml\:math> element: 10.1. Perl and XML Namespaces
modules: 1.4. A Myriad of Modules
CPAN and: 1.1. Why Use Perl with XML?
XML: 9.1. XML Modules
Mosher, Edward: 2.1. A Brief History of XML

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

name property
Attr class (DOM): 7.2.9.1. Properties
DocumentType class (DOM): 7.2.3.1. Properties
NamedNodeMap class (DOM): 7.2.6. NamedNodeMap
Namespace node type: 6.1. XML Trees
namespaces: 1.6. XML Gotchas
2.3. Namespaces
10.1. Perl and XML Namespaces
namespaceURI property for Node class (DOM): 7.2.4.1. Properties
Netscape: 9.2.1. Introduction to RSS
new( ) XML::SAX::ParserFactory class method: 5.7.1. XML::SAX::ParserFactory
nextSibling property for Node class (DOM): 7.2.4.1. Properties
node( ) method in XPath: 8.2. XPath
Node class (DOM): 7.1. DOM and Perl
7.2.4. Node
7.3. XML::DOM
7.4. XML::LibXML
NodeList class (DOM): 7.2.5. NodeList
nodeName property for Node class (DOM): 7.2.4.1. Properties
nodes
XML trees: 6.1. XML Trees
XPath: 8.2. XPath
nodeType property for Node class (DOM): 7.2.4.1. Properties
nodeValue property for Node class (DOM): 7.2.4.1. Properties
normalize method for Element node: 7.2.8.2. Methods
Notation class (DOM): 7.2.16. Notation
notation for DocumentType class (DOM): 7.2.3.1. Properties
notation_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers
notationName property for Entity node: 7.2.15.1. Properties
numeric character entities: 2.5. Entities

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

OASIS-Open: 3.7.2. Schemas
object parsing style: 3.2.2. Parsing Styles
output, when using XML::RSS module: 9.2.5. Off-the-Cuff Output
ownerDocument property for Node class (DOM): 7.2.4.1. Properties
ownerElement property of Attr class (DOM): 7.2.9.1. Properties

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

parent nodes: 6.1. XML Trees
parenthesis (( )) in PYX notation: 4.5. XML::PYX
parentNode property for Node class (DOM): 7.2.4.1. Properties
parse( ) method: 3.2.1. Example: Well-Formedness Checker Revisited
5.7.3. SAX2 Parser Interface
parsefile( ) method: 3.2.2. Parsing Styles
7.3. XML::DOM
XML::Parser module, using: 3.2.1. Example: Well-Formedness Checker Revisited
parsers: 1.5. Keep in Mind...
3.1. XML Parsers
event streams: 4.3. The Parser as Commodity
parsing styles: 3.2. XML::Parser
3.2.2. Parsing Styles
SAX 2 interface: 5.7.3. SAX2 Parser Interface
installing: 5.7.5. Installing Your Own Parser
using: 3.4. Putting Parsers to Work
UTF-32 encoding and: 3.9.2.3. UTF-32
well-formedness checkers: 2.9. Free-Form XML and Well-Formed Documents
writing: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
XML::Parser module and: 3.2. XML::Parser
XML::RSS module and: 9.2.2.1. Parsing
parsing styles: 3.2. XML::Parser
3.2.2. Parsing Styles
Perl, using with XML: 1.1. Why Use Perl with XML?
namespaces: 10.1. Perl and XML Namespaces
perl-xml mailing list: 0.3.1. The perl-xml Mailing List
1.4. A Myriad of Modules
XML programming tools: 9.3. XML Programming Tools
PerlSAX: 3.4. Putting Parsers to Work
PerlSax handlers: 5.2. DTD Handlers
pi( ) Writer method: 3.8. XML::Writer
PIs (see Processing Instructions)
<plist> element: 8.2. XPath
POD (Plain Old Documentation): 2.8. Processing Instructions and Other Markup
prefix property for Node class (DOM): 7.2.4.1. Properties
prefixes in namespace: 2.3. Namespaces
presentational markup languages: 2.1. A Brief History of XML
markup, elements and structure and: 2.2. Markup, Elements, and Structure
previousSibling property for Node class (DOM): 7.2.4.1. Properties
Processing Instructions (PIs): 2.8. Processing Instructions and Other Markup
node type: 6.1. XML Trees
processing_instruction( ) method: 5.1. SAX Event Handlers
5.7.2.1. Content event handlers
ProcessingInstruction class (DOM): 7.2.12.1. Properties
processors (XML): 1.3. XML Processors
whitespace and: 1.6. XML Gotchas
programming tools (XML): 9.3. XML Programming Tools
properties
DOM: 7.2. DOM Class Interface Reference
Attr class: 7.2.9.1. Properties
CharacterData class: 7.2.7.1. Properties
Document class: 7.2.1.1. Properties
DocumentType class: 7.2.3.1. Properties
Element node: 7.2.8.1. Properties
NamedNodeMap class: 7.2.6.1. Properties
Node class: 7.2.4.1. Properties
NodeList class: 7.2.5.1. Properties
Notation class: 7.2.16.1. Properties
ProcessingInstruction class: 7.2.12.1. Properties
publicId property
DocumentType class: 7.2.3.1. Properties
Entity node: 7.2.15.1. Properties
Notation class: 7.2.16.1. Properties
PYX module: 4.5. XML::PYX

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

qualified names: 2.3. Namespaces
question mark (?) in PYX notation: 4.5. XML::PYX

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

Really Simple Syndication (RSS): 9.2.1. Introduction to RSS
recursion when processing XML: 3.5. XML::LibXML
references (entity): 2.5. Entities
relative locators in XPath: 8.2. XPath
RelaxNG: 3.7.2. Schemas
removeAttribute method for Element node: 7.2.8.2. Methods
removeAttributeNode method for Element node: 7.2.8.2. Methods
removeAttributeNS method for Element node: 7.2.8.2. Methods
removeNamedItem class for Node class (DOM): 7.2.6.2. Methods
removeNamedItemNS class for Node class (DOM): 7.2.6.2. Methods
replaceChild method for Node class (DOM): 7.2.4.2. Methods
replaceData method for CharacterData class (DOM): 7.2.7.2. Methods
resolve_entity( ) method: 5.7.2.2. Entity resolver
Rich Site Summary (RSS): 9.2.1. Introduction to RSS
root( ) module: 8.2. XPath
root elements: 2.5. Entities
RSS (Rich Site Summary): 9.2.1. Introduction to RSS

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

SAX (Simple API for XML): 3.4. Putting Parsers to Work
5. SAX
DBI and: 9.3.2. Further Ruminations on DBI and SAX
drivers for non-XML sources: 5.4. Drivers for Non-XML Sources
external entity resolution: 5.3. External Entity Resolution
handlers: 5.5. A Handler Base Class
DTD: 5.2. DTD Handlers
event: 5.1. SAX Event Handlers
parser interface: 5.7.3. SAX2 Parser Interface
XML::SAX module and: 5.7. XML::SAX: The Second Generation
schema (XML): 2.11. Schemas
3.7.2. Schemas
Schematron: 3.7.2. Schemas
selector programs for stream processing: 4.4. Stream Applications
semicolons (;): 3.1. XML Parsers
Sergeant, Matt: 4.5. XML::PYX
5.7. XML::SAX: The Second Generation
8.2. XPath
9.3. XML Programming Tools
servers: 1.1. Why Use Perl with XML?
setAttribute method for Element node: 7.2.8.2. Methods
setAttributeNode method for Element node: 7.2.8.2. Methods
setAttributeNodeNS method for Element node: 7.2.8.2. Methods
setAttributeNS method for Element node: 7.2.8.2. Methods
set_document_locator( ) method: 5.7.2.1. Content event handlers
setNamedItem class for Node class (DOM): 7.2.6.2. Methods
setNamedItemNS class for Node class (DOM): 7.2.6.2. Methods
SGML (Standard Generalized Markup Language): 2. An XML Recap
sibling nodes: 6.1. XML Trees
Simple API for XML (see SAX)
Simple Object Access Protocol (SOAP): 9.4. SOAP::Lite
skipped_entity( ) method: 5.7.2.1. Content event handlers
slashes (/): 2.9. Free-Form XML and Well-Formed Documents
well-formed documents and: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
XPath expressions: 8.2. XPath
SOAP (Simple Object Access Protocol): 9.4. SOAP::Lite
SOAP::Lite module: 9.4. SOAP::Lite
Source parameter (SAX parser interface): 5.7.3. SAX2 Parser Interface
spacing in XML: 2.4. Spacing
specified property of Attr class (DOM): 7.2.9.1. Properties
splitText method of Text class (DOM): 7.2.10.1. Methods
square brackets ([ ]): 2.5. Entities
stacks, writing XML document checkers: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
standard-completeness of parsers: 3.2. XML::Parser
Standard Generalized Markup Language (see SGML)
start_cdata( ) event handler: 5.1. SAX Event Handlers
5.7.2.3. Lexical event handlers
start_document( ) method: 5.1. SAX Event Handlers
5.7.2.1. Content event handlers
start_dtd( ) event handler: 5.7.2.3. Lexical event handlers
start_element( ) method: 5.1. SAX Event Handlers
5.7.2.1. Content event handlers
start_entity( ) event handler: 5.7.2.3. Lexical event handlers
start_prefix_mapping( ) method: 5.7.2.1. Content event handlers
startTag( ) Writer method: 3.8. XML::Writer
Sterin, Ilya: 5.4. Drivers for Non-XML Sources
9.3. XML Programming Tools
strategies in XML processing: 3.3. Stream-Based Versus Tree-Based Processing
stream-based processing for parsers: 3.3. Stream-Based Versus Tree-Based Processing
streams: 4.1. Working with Streams
applications: 4.4. Stream Applications
event: 4. Event Streams
parsing style: 3.2.2. Parsing Styles
structure of XML: 2.2. Markup, Elements, and Structure
style attributes: 8.2. XPath
subclassing: 10.2. Subclassing
subs parsing style: 3.2.2. Parsing Styles
subsets (external and internal): 2.5. Entities
substringData method for CharacterData class (DOM): 7.2.7.2. Methods
summarizer program for stream processing: 4.4. Stream Applications
syntax errors in XML documents: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
system identifiers: 2.5. Entities
systemId property
DocumentType class: 7.2.3.1. Properties
Entity node: 7.2.15.1. Properties
Notation class: 7.2.16.1. Properties

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

tagname property for Element node: 7.2.8.1. Properties
tags in markup languages: 2.2. Markup, Elements, and Structure
target parameters: 2.8. Processing Instructions and Other Markup
target property for ProcessingInstruction class (DOM): 7.2.12.1. Properties
template rules in XSLT scripts: 2.12. Transformations
TeX: 2.2. Markup, Elements, and Structure
text( ) method in XPath: 8.2. XPath
Text class (DOM): 7.2.10.1. Methods
Text::Iconv Perl module: 3.9.4.1. iconv and Text::Iconv
TEXT_NODE( ) method: 7.3. XML::DOM
token (character streams): 4.1. Working with Streams
tool for XML programming: 9.3. XML Programming Tools
toString( ) method: 3.8.1. Other Methods of Output
7.3. XML::DOM
transformations in XML: 2.12. Transformations
tree-based processing: 3.2.2. Parsing Styles
6. Tree Processing
8. Beyond Trees: XPath, XSLT, and More
optimized: 8.4. Optimized Tree Processing
vs. stream-based: 3.3. Stream-Based Versus Tree-Based Processing
XML structure and: 2.2. Markup, Elements, and Structure
XML::Grove module: 6.6. XML::Grove
XML::LibXML module and: 3.5. XML::LibXML
XML::Parser: 6.3. XML::Parser's Tree Mode
XML::Simple module: 6.2. XML::Simple
XML::TreeBuilder module and: 6.5. XML::TreeBuilder
XPath and: 8.2. XPath
XSLT: 8.3. XSLT
tree climbers: 8.1. Tree Climbers
tree processing (see tree-based processing)
troff text formatting language: 2.1. A Brief History of XML

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

Unicoce Transformation Format (see UTF)
Unicode character encodings: 1.1. Why Use Perl with XML?
2.6. Unicode, Character Sets, and Encodings
3.9. Character Sets and Encodings
conversion: 3.9.4. Encoding Conversion
Perl support for: 3.9.3.1. Core Perl support
XML: 3.9.1. Unicode, Perl, and XML
vs. ASCII: 1.6. XML Gotchas
Unicode Consortium: 2.6. Unicode, Character Sets, and Encodings
Unicode::String module: 3.9.4.2. Unicode::String
Uniform Resource Identifiers (URIs): 10.1. Perl and XML Namespaces
unparsed_entity_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers
URIs (Uniform Resorce Identifiers): 10.1. Perl and XML Namespaces
UTF-16 character encodings: 3.9. Character Sets and Encodings
7.2. DOM Class Interface Reference
UTF-32 character encodings: 3.9.2.3. UTF-32
UTF (Unicode Transformation Format): 3.9.2. Unicode Encodings

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

validating parsers: 3.1. XML Parsers
3.7. Document Validation
validation (document): 3.7. Document Validation
value property of Attr class (DOM): 7.2.9.1. Properties

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

walker (tree climbers): 8.1. Tree Climbers
Wall, Larry: 3.2. XML::Parser
warning( ) exception handler: 5.7.2.4. Error event handlers and catching exceptions
W3C (World Wide Web Consortium): 2.1. A Brief History of XML
7.1. DOM and Perl
URI identifiers and: 10.1. Perl and XML Namespaces
Web (see World Wide Web)
well-formed documents: 2.9. Free-Form XML and Well-Formed Documents
3.1. XML Parsers
checkers: 2.9. Free-Form XML and Well-Formed Documents
writing: 3.1.1. Example (of What Not to Do): A Well-Formedness Checker
3.2.1. Example: Well-Formedness Checker Revisited
whitespace: 1.6. XML Gotchas
ignorable_whitespace( ) method and: 5.7.2.1. Content event handlers
World Wide Web
HTML and: 2.1. A Brief History of XML
Perl and: 1.1. Why Use Perl with XML?
World Wide Web Consortium (W3C): 2.1. A Brief History of XML
7.1. DOM and Perl
URI identifiers: 10.1. Perl and XML Namespaces
WriteMakeFile( ) function, installing parsers: 5.7.5. Installing Your Own Parser
Writer methods: 3.8. XML::Writer

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

XHTML files, processing with DOM: 7.3. XML::DOM
XML (Extensible Markup Language): 2. An XML Recap
basics: 3. XML Basics: Reading and Writing
declarations: 2.7. The XML Declaration
entities: 2.5. Entities
history of: 2. An XML Recap
namespaces: 2.3. Namespaces
Perl, using with: 1.1. Why Use Perl with XML?
problems with: 1.6. XML Gotchas
programming tools: 9.3. XML Programming Tools
spacing for: 2.4. Spacing
transformations in: 2.12. Transformations
XML Schema: 2.11. Schemas
XML Style Language\: Transformations (see XSLT)
XML Stylesheet Language for Transformations (XSLT): 2.12. Transformations
xmlDecl( ) Writer method: 3.8. XML::Writer
xml_decl method for PerlSAX DTD handlers: 5.2. DTD Handlers
XML::DOM module: 7.1. DOM and Perl
7.3. XML::DOM
XML::Generator::DBI module: 9.3.1. XML::Generator::DBI
XML::Grove module: 6.6. XML::Grove
XML::Handler::Subs module: 5.5. A Handler Base Class
XML::Handler::YAWriter module: 5.6. XML::Handler::YAWriter as a Base Handler Class
9.3.1. XML::Generator::DBI
XMLin( ) method: 1.2. XML Is Simple with XML::Simple
6.2. XML::Simple
XML::LibXML module: 3.5. XML::LibXML
7.1. DOM and Perl
7.4. XML::LibXML
8.2. XPath
XMLout( ) subroutine: 1.2. XML Is Simple with XML::Simple
XML::Parser module: 1.3. XML Processors
3.2. XML::Parser
character encodings and: 3.9.3. Other Encodings
event streams and: 4.6. XML::Parser
styles for: 3.2.2. Parsing Styles
tree mode: 6.3. XML::Parser's Tree Mode
using parsers: 3.4. Putting Parsers to Work
well-formed checkers, writing: 3.2.1. Example: Well-Formedness Checker Revisited
XML::RSS module and: 9.2.2.2. Inheriting from XML::Parser
XML::Parser::Expat module: 3.2. XML::Parser
XML::Parser::PerlSAX module: 5.2. DTD Handlers
XML::PYX module: 4.5. XML::PYX
XML::RSS module: 9.2. XML::RSS
input, user and file: 9.2.4. Input: User or File
object model: 9.2.3. The Object Model
output and: 9.2.5. Off-the-Cuff Output
parsing: 9.2.2.1. Parsing
using: 9.2.2. Using XML::RSS
XML::SAX module: 3.4. Putting Parsers to Work
5.7. XML::SAX: The Second Generation
handlers for: 5.7.2. SAX2 Handler Interface
XML::SAXDriver::Excel module: 5.4. Drivers for Non-XML Sources
XML::SAX::ParserFactory module: 5.7. XML::SAX: The Second Generation
XML::Schematron modules: 3.7.2. Schemas
XML::Simple module: 1.2. XML Is Simple with XML::Simple
6.2. XML::Simple
XML::SimpleObject module: 6.4. XML::SimpleObject
XML::TreeBuilder module: 6.5. XML::TreeBuilder
XML::Writer module: 3.8. XML::Writer
XML::XPath module: 3.6. XML::XPath
8.2. XPath
XPath: 3.6. XML::XPath
8.2. XPath
XS library: 3.2. XML::Parser
XSLT (XML Stylesheet Language for Transformations): 2.12. Transformations
8.3. XSLT
converting XML to HTML: 10.3. Converting XML to HTML with XSLT

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Colophon (Perl and XML)

 Perl & XMLSearch this book
Previous

Colophon

Our look is the result of reader comments, our own experimentation, and feedback from distribution channels. Distinctive covers complement our distinctive approach to technical topics, breathing personality and life into potentially dry subjects.

The animals on the cover of Perl and XML are West African green monkeys. The green monkey, more commonly known as a vervet, is named for its yellow to olive-green fur. Most vervets live in semi-arid regions of sub-Saharan Africa, but some colonies, thought to be descendants of escaped pets, exist in St. Kitts, Nevis, and Barbados. The vervet's diet mainly consists of fruit, seeds, flowers, leaves, and roots, but it sometimes eats small birds and reptiles, eggs, and insects. The largely vegetarian nature of the vervet's diet creates problems for farmers sharing its land, who often complain of missing fruits and vegetables in areas where vervets are common. To control the problem, some farmers resort to shooting the monkeys, who often leave small orphan vervets behind. Some of these orphans are, controversially, sold as pets around the world. Vervets are also bred for use in medical research; some vervet populations are known to carry immunodeficiency viruses that might be linked to similar human viruses.

The green monkey uses a sophisticated set of vocalizations and visual cues to communicate a wide range of emotions, including anger, alarm, pain, excitement, and sadness. The animal is considered highly intelligent and, like other primates, its ability to express intimacy and anxiety is similar to that of humans.

Ann Schirmer was the production editor and copyeditor for Perl and XML. Emily Quill was the proofreader. Claire Cloutier and Leanne Soylemez provided quality control. Phil Dangler, Julie Flanagan, and Sarah Sherman provided production assistance. Joe Wizda wrote the index.

Ellie Volckhausen designed the cover of this book, based on a series design by Edie Freedman. The cover image is a 19th-century engraving from the Royal Natural History. Emma Colby produced the cover layout with QuarkXPress 4.1 using Adobe's ITC Garamond font.

Melanie Wang designed the interior layout, based on a series design by David Futato. Neil Walls converted the files from Microsoft Word to FrameMaker 5.5.6 using tools written in Perl by Erik Ray, Jason McIntosh, and Neil Walls. The text font is Linotype Birka; the heading font is Adobe Myriad Condensed; and the code font is LucasFont's TheSans Mono Condensed. The illustrations that appear in the book were produced by Robert Romano and Jessamyn Read using Macromedia FreeHand 9 and Adobe Photoshop 6. The tip and warning icons were drawn by Christopher Bing. This colophon was written by Ann Schirmer.

Previous | Home
Index | Book Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX2

by David Brownell
ISBN 0-596-00237-8
First Edition, published January 2002.
(See the catalog page for this book.)

Table of Contents

Copyright Page
Preface
Chapter 1: The Simple API for XML
Chapter 2: Introducing SAX2
Chapter 3: Producing SAX2 Events
Chapter 4: Consuming SAX2 Events
Chapter 5: Other SAX Classes
Chapter 6: Putting It All Together
Appendix A: SAX2 API Summary
Appendix B: SAX2 and the XML Infoset
Index
Colophon
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Copyright (SAX2)

 SAX2Search this book

Copyright © 2002 O'Reilly & Associates, Inc. All rights reserved.

Printed in the United States of America.

Published by O'Reilly & Associates, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safari.oreilly.com). For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are registered trademarks of O'Reilly & Associates, Inc. The association between the image of a pampas cat and the topic of SAX2 is a trademark of O'Reilly & Associates, Inc.

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps.

While every precaution has been taken in the preparation of this book, the publisher and the author assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Previous | Home | Next
Table of Contents | Book Index | 0. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Preface (SAX2)

 SAX2Search this book

Preface

Think of this book as if it were really called Everything You Wanted to Know About SAX. It provides a quick tutorial, while also serving as a complete reference that explains how to use this popular XML API effectively and efficiently. You'll find motivations for every programming interface and see how to build components for your application (or specialized environment) on top of SAX.

The information in this book is based on the current version of the Java Figure 0 language support for SAX2. For any further updates to SAX2, see the SAX web site at http://www.saxproject.org.

0.1. Who Should Read This Book?

If you are programming with XML in Java, or starting to do that, and you want to learn how to use SAX2 to its fullest, this book is for you. It assumes that you are familiar with Java programming and have a basic understanding of XML, including DTDs. You may have some exposure to DOM, an alternative parser API, but you need more efficient, or more complete, access to XML than you can get with such a generic tree structure API. Although there's a lot of interest in XML from server-side programmers, and this book includes some examples targeted at servlet-based systems, SAX2 is addressed to Java developers working on all scales, from embedded systems to enterprise applications.

Although versions of the SAX API have been provided for developers who use C/C++, Pascal, Perl, and Python, this book is not addressed to such developers except in the broad sense that good SAX programming idioms transcend the particular language used to express them.

This book is for Java programmers working with XML who need an efficient way of reading or generating XML documents. The simple API for XML (SAX)'s event-based approach provides an extremely streamlined set of tools for Java programmers.

Previous | Home | Next
Copyright Page | Book Index | 0.2. Organization of This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Organization of This Book (SAX2)

 SAX2Search this book

0.2. Organization of This Book

This book is divided into six chapters and two appendixes.

Chapter 1, "The Simple API for XML", The Simple API for XML, orients you in terms of API alternatives, SAX history, software choices, and basic SAX functionality.

Chapter 2, "Introducing SAX2", Introducing SAX2, introduces the core technical details of SAX, showing the basic event producer and consumer APIs needed by almost all code that uses SAX.

Chapter 3, "Producing SAX2 Events", Producing SAX2 Events, focuses on producing SAX events, showing the rest of the parser APIs as well as several nonparser models for using SAX.

Chapter 4, "Consuming SAX2 Events", Consuming SAX2 Events, concentrates on consuming SAX events, showing the other event consumer interfaces (both core and extension), and exploring the SAX pipeline model in a bit more detail.

Chapter 5, "Other SAX Classes", Other SAX Classes, presents SAX helper classes that weren't yet presented, including legacy SAX1 support.

Chapter 6, "Putting It All Together", Putting It All Together, provides more extensive examples than the earlier chapters.

The two appendixes are reference material. Appendix A, "SAX2 API Summary", SAX2 API Summary, summarizes every class or interface in the API and should be a useful quick reference. Appendix B, "SAX2 and the XML Infoset", SAX2 and the XML Infoset, shows how the XML Infoset concepts map to SAX APIs and should be a useful reference when you need to determine which APIs to use to access particular structural data.

Previous | Home | Next
0. Preface | Book Index | 0.3. Conventions Used in This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Conventions Used in This Book (SAX2)

 SAX2Search this book

0.3. Conventions Used in This Book

method()

Method names sometimes include their interfaces or classes.

method parameter or variable

Values bound to parameter or variable names are presented in the same way.

class, interface, or package.name

Names identifying interfaces and classes are identified in the same way as package names.

XML attribute or element

All XML markup in the body of the text is presented consistently.

Previous | Home | Next
0.2. Organization of This Book | Book Index | 0.4. How to Contact Us
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How to Contact Us (SAX2)

 SAX2Search this book

0.4. How to Contact Us

Please address any comments and questions concerning this book to the publisher:

O'Reilly & Associates, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
(800) 998-9938 (in the United States or Canada)
(707) 829-0515 (international/local)
(707) 829-0104 (fax)

We have a web page for this book, where we list errata, examples, or any additional information. You can access this page at:

http://www.oreilly.com/catalog/sax2/

To comment or ask technical questions about this book, send email to:

bookquestions@oreilly.com

For more information about our books, conferences, software, Resource Centers, and the O'Reilly Network, see our web site at:

http://www.oreilly.com

Previous | Home | Next
0.3. Conventions Used in This Book | Book Index | 0.5. Acknowledgments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Acknowledgments (SAX2)

 SAX2Search this book

0.5. Acknowledgments

This book would not have been possible without the contribution and support of many individuals. David Megginson, who led the original SAX and SAX2 development processes, deserves particular thanks from everyone who uses SAX. Numerous other people have contributed to SAX through the xml-dev mailing list, far too many to mention here by name. Simon St.Laurent at O'Reilly provided good feedback and regular encouragement during the creation of this book. Along with review comments from Murray Altheim and David Megginson, he helped identify rough spots and improve the content of this book in many ways.

Previous | Home | Next
0.4. How to Contact Us | Book Index | 1. The Simple API for XML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Simple API for XML (SAX2)

 SAX2Search this book

Chapter 1. The Simple API for XML

Contents:

Types of XML APIs
Why Choose SAX?
Why Not to Choose SAX?
A Short History of SAX
Packages in the SAX2 API
Some Popular SAX2 Parser Distributions
Installing a SAX2 Parser
What XML Are We Talking About?

When XML started, Java was best known as a fun new language that made developing programs for the World Wide Web easy. XML was intended to be the data foundation for the next generation of web infrastructure tools, and it clearly needed the same kind of support that Java offered. The Java programming environment included ways to fetch data over the Web with URLs, which was a novel notion at that. It even had support for Unicode, so working with languages used anywhere on the Web would be easy. Since both those capabilities were important for working with XML, there was already a very active community of XML developers using Java when the XML 1.0 Recommendation was finalized in early 1998. More XML parsers were available at that time for Java than for the more widely adopted C programming language!

Those parsers quickly came to share one feature: applications weren't restricted to some particular product's API. The Simple API for XML, SAX, was well under way; it was the first API usable with all the popular Java parsers. SAX helped make Java a premiere language for developing XML-based applications.

Since then the adoption of XML has exploded, as has the use of Java in web-oriented (and other) applications. Today's Java programmer has an embarrassingly large selection of XML-related APIs to choose from, and SAX has retained its role as a premier XML API. In this chapter we look at why this is true, and learn more about where SAX came from and its current state.

1.1. Types of XML APIs

An Application Programming Interface (API) is a set of interfaces and classes used to expose particular functionality to a variety of applications. Some APIs are specific to particular products. Better Java APIs, like SAX, use the interface facility to work with multiple products: they are defined so that multiple implementations can coexist. Such implementations behave the same except for differences allowed by the API. For example, one might be faster, while another might leverage private interfaces to some subsystem. (In that case an application could use the fast implementation most of the time and the slower one only when those added features are needed.) APIs differ in how they expose functionality, which affects how well applications work.

For the purposes of this book, there are two kinds of APIs to XML. We'll call one a "parser API" and the other a "high-level API." Parser-level APIs model documents in terms of XML notions such as elements, attributes, and character data, and hide all the details of actually turning XML text into information that applications can use. High-level APIs generally focus on non-XML notions, usually to make XML itself seem only an implementation artifact that might be easily replaced by other data interchange technology, or more rarely, by another document technology. This spectrum is not as wide as you might expect. Parser-level APIs are well-suited for working with XML-centric applications, higher-level APIs try to focus on particular visions of the data being encoded with XML, and some APIs "hop" between levels.

SAX is a parser-level API that is rather unique in flavor because it leaves all data structure choices up to higher levels. This feature helps scale XML applications, since it lets SAX be an unobtrusive and extremely effective building block. SAX models documents in terms of a stream of event callbacks; this has been called an "active" API. The events are more structured than the stream of mouse and keyboard events you may know from AWT or Swing. Events are sent to application handlers for basic XML content (such as elements and characters) in exactly the order they're found in the document, as shown in Figure 1-1. That's the same order in which you'd traverse a tree model of that markup: you'd start an element, look at its children, and then end the element.

Figure 1-1. A streaming parser API

Most other parser-level APIs provide a generic, object tree data structure mirroring the parse tree; these have been called "passive" APIs. Figure 1-2 shows some XML text, and its transformation to such a data structure.

Figure 1-2. An object tree parser API

Examples of such generic data structures include W3C's DOM and more Java-friendly variants such as DOM4J and jDOM. Such generic tree structures are usually built with the output of an underlying SAX parser, and so are slightly higher level than SAX. However, these generic structures place significant constraints on application structure and scalability. A popular alternative approach involves building custom data structures from that same SAX output, instead of generic ones, larger than some fraction of available virtual memory. Because the data structures are custom built, they tend to be faster and more task-appropriate than structures built by generic APIs.

Previous | Home | Next
0.5. Acknowledgments | Book Index | 1.2. Why Choose SAX?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Why Choose SAX? (SAX2)

 SAX2Search this book

1.2. Why Choose SAX?

SAX gives you the flexibility to approach application design with your own trade-offs and goals in mind. High-level APIs often make many of those trade-offs for you, but not necessarily in ways that are best for your problems. In particular, SAX lets you design lightweight, task-oriented XML solutions, which can fit into small systems or scale up to large ones. Just having such options can be an important reason to choose SAX over generic APIs that work only at a high level. While initial deployment platforms might be richly featured, this won't necessarily be true for all the systems you need to support, or for the ones your customers want you to support.

Compared to other parser-level APIs, SAX has two unique structural features: its efficient event-stream processing model and its data structure flexibility. These give you more control over the results of your parse.

1.2.1. Stream-Based Processing

SAX is the API to use when you need to stream-process XML to conserve memory and, in most cases, CPU time. In SAX, handler interfaces call application (or library) code for each significant chunk of XML information as it's parsed. These chunks include character data, elements, and attributes. Each event passes information to your code, which can save it or ignore it as appropriate. These handlers see document information as a stream of such event calls, in "document order." Applications can process data incrementally, rather than in one big chunk, and they can discard information as soon as it's not needed.

SAX parsers have several key advantages:

  • SAX parsers can be small and fast because they are minimal. SAX provides the most essential XML data, and no more.

    SAX parsers are well suited for use in resource-constrained environments. This includes not just small systems or classic embedded ones (where cost prevents using of much memory or fast CPUs), but also inside servers (which may have huge amounts of memory and fast CPUs, but need good scaling properties to share them with many clients) such as security gateways. Good security practice avoids large bodies of code, since assurance is so hard to achieve.

  • Because SAX is a streaming API, it promotes pipelined processing, where I/O occurs while you use the CPU to do work. You will naturally structure applications (or at least their SAX components) to use efficient single-pass algorithms and incremental processing.

    As soon as XML data starts to become available (perhaps over a network), SAX parsers start to provide it to applications. While processing element or character data, the network or the filesystem prefetches the next data. Such overlapped processing lowers latencies and makes good use of limited CPU cycles. With most other APIs, your application won't even see data until the whole document has been fetched and parsed; you can't process documents larger than available memory. This causes major trouble when you work with large documents, as discussed in the next section.

  • SAX gives you flexible control over how you handle errors and faults. Fatal errors aren't the only kind of reportable fault, and diagnostic information is readily accessible.

    You easily provide application-specific error reports with the standard mechanism. It's also easy to terminate parsing early: just throw an appropriate exception when you find the <great:widget> element you need, or when some unrecoverable error turns up.

  • It's easy to define custom SAX event producers.

    That is, you can use SAX when your inputs aren't literal XML text. This is a powerful technique that helps you work with data at the level of parsed XML information (the XML "Infoset"), and postprocess SAX events or late-bind data into XML text format. Such early/late-binding flexibility is a powerful architectural tool.

You may be fortunate enough to be able to design the XML representations of your application tasks to facilitate such work-flow streams. When you do this, you may see substantial performance and scalability gains over alternative design approaches. You might even be able to pull the SAX event stream model up into higher-level work flows in your system so that more processing can be stream-based.

For example, you could structure your XML as a sequential list of reasonably sized tasks. Several kinds of data import/export problems are well suited to this approach, although you may find you need to be aware of the I/O costs of random access as you transform data to and from interchange formats.

1.2.2. Data Structure Flexibility

In contrast to higher-level APIs, or most design tools, SAX allows you to populate whatever data structures you choose. It lets you use custom data APIs, optimized for your application, or more general-purpose APIs. This flexibility operates at two broad system levels: architecture and design. Suchs flexibility is required to scale applications up (or down) and to update applications as systems evolve.

Application architecture components affect how systems interact with each other and with external systems. SAX doesn't constrain these components, which include data interchange formats and messaging paradigms, because it lets you use XML in any way you (or your systems partners) need. In contrast, settling early on higher-level XML APIs will constrain application architectures in many ways, often affecting XML structures used for interoperability. For example, many SOAP toolkits expect an RPC paradigm using W3C-style XML schemas, and many data-binding approaches demand a particular schema system and API toolset. The hope is that if you accept those system constraints, you win more than they cost. When that doesn't work, perhaps because the constraints don't suit your application, you'll appreciate the flexibility of SAX.

The design level affects application internals rather than the broader interfaces, which relate to architecture. Design constraints affect runtime and implementation costs. If you're adding XML support to an existing system, design-level concerns may dominate your planning. SAX lets you use your current optimized data structures or define new ones. Since such design issues will often dominate performance measurements (given reasonable architectures), preserving flexibility can be very important.

With SAX, you don't need to use generic (and largely untyped) data structures. You will normally store data directly into specialized data structures as SAX delivers it from its XML representation. This facilitates important architecture-level optimizations. Being able to use custom data structures means you can leverage the strong data-typing facilities in Java and detect many kinds of bugs early, while recovery is possible and cheap. Custom data decisions are the ideal way to work with large documents, for other cases where scale is a major concern, and anywhere that data structure decisions need to be driven by application issues rather than "one size fits all" generic tools.

1.2.3. Memory Consumption with SAX and DOM

To illustrate this design impact, we'll pick on DOM as a representative design choice for an API with a generic XML data structure. You'll often have reasons to use both SAX and DOM, even in the same application, so you'll need to know when to use each API. The strength of DOM is that it's a widely understood and available generic model; it can be good for "proof of concept" solutions. However, it has a high price in terms of flexibility and resource consumption. Later, we look at ways to reduce those DOM costs with help from SAX and ways that DOM and SAX representations of XML data can be interconverted.

For documents with a "typical" markup density, many DOM implementations in Java use about 10 bytes of memory to represent each byte of XML text. (Few take less, some take more.) Yes, that midsize three-megabyte document can easily balloon up to 30 megabytes of memory on your server![1] When using DOM with large documents, memory shortages are common, both for virtual memory and for space in the Java heap. Shortages are made worse if you then need to convert data from a generic DOM representation into custom structures, because you need an extra copy of the data while you build the more appropriate data structure. This clearly limits application scalability.

[1]Some applications certainly revolve around large documents. One translation of the Old Testament is over 3 megabytes in size; one dictionary is over 50 megabytes. Dumps of databases can be gigabytes in size.

On the other hand, with SAX you don't pay for any memory unless you choose to do so. You can ignore most of that three-megabyte document right up front; the API structure makes it natural to capture only significant data (whatever that may be in your application). This reduces memory allocation pressure, as well as overhead from garbage collection. Best, SAX parsers let you use data structures that are appropriate for your application from the very beginning. In fact, they all but require you to do that!

1.2.4. Other Reasons to Prefer SAX

SAX has always defined its concurrency behaviors, making it safe to use SAX in multithreaded applications. Since DOM does not specify those behaviors, multithreaded applications (such as most web services) accept implementation dependencies if they choose to use DOM.

SAX2 provides almost complete support for the XML Infoset, exposing the logical structure of XML data. (See Appendix B, "SAX2 and the XML Infoset".) This means it's substantially more complete than most other XML APIs, and certainly more complete than any other widely available API. You are unlikely to need important information from an XML document that SAX can't provide. This contrasts with DOM, which doesn't have standard APIs to expose much of this information. SAX is great way to turn a stream of such Infoset data into other kinds of data.

At its core, SAX is indeed a very simple API for XML processing; such simplicity is a key virtue. You can write useful XML applications code with only a handful of method calls and still know that the rest of the XML Infoset data is available when you need it. It's not like DOM, in which syntax artifacts that mask the core data model of XML are common. DOM takes a more monolithic approach than SAX. A book that covers DOM as completely as this book covers SAX would need to be several times larger even if it didn't cover the latest version (Level 3).

On top of that, because SAX makes you actually think about the best way to represent your data, it's more fun to work with than tools that claim to solve those issues for you! (They usually can't.) It's also a great way to learn your way around XML and Java.

Previous | Home | Next
1. The Simple API for XML | Book Index | 1.3. Why Not to Choose SAX?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Why Not to Choose SAX? (SAX2)

 SAX2Search this book

1.3. Why Not to Choose SAX?

No API solves problems by itself, and SAX avoided the kitchen sink syndrome better than many others. So there are times it will be clear SAX isn't the whole answer for some particular application-processing stage, even when you have the option to choose it. It will often still be the right way to get data into or out of another processing stage, particularly since many other APIs can interface with SAX. Also, building custom data import/export tools with SAX is fairly easy.

Probably the biggest single issue with SAX is that by itself it doesn't provide random access to XML data. Its event stream is "forward-only": you can't go backwards or reorder it without your own record of the events. Such data structure policy would be handled by application layers on top of SAX, and you'll need such layers if you use random access models such as XPath. Typically, applications use SAX to construct data structures that are either customized for their particular random access requirements or generic (typically DOM-like). You might create Person objects and index them by name, perhaps in some sort of hash table or using some kind of database as a backing store. In some applications it's acceptable to just re-scan small to midsize XML documents on demand; it can be inexpensive when modern operating systems have already cached the data.

If you're looking for an API that helps you write a low-level XML text editor and lets you work with malformed XML while it preserves semantically meaningless information,[2] then SAX isn't what you want. Similarly, parsing less than an entire XML document isn't standardized by SAX (or by the XML specification). Such processing requires an API that works at the level of potentially malformed tokens. SAX (and any other application programming interface not targeted at text editors) makes hiding such details a primary goal. SAX works well for "structural" editors, which prevent creation of malformed XML and hide semantically meaningless information.

[2]For example, whitespace outside element content, attribute order, or singly versus doubly quoted strings.

It's important to note that SAX is intentionally limited. It's the core of a library of XML support, and that "S" in its name really does mean "simple"; complex functionality is for layers on top of SAX and is not part of SAX itself. Even basic facilities like XML text output (printing) are layered over SAX. While open source code to handle such functions is often available on the Internet, you may still need to find and choose between such libraries. SAX is somewhat of a "close to the metal" low-level API, though it's more flexible than most such APIs.

Previous | Home | Next
1.2. Why Choose SAX? | Book Index | 1.4. A Short History of SAX
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Short History of SAX (SAX2)

 SAX2Search this book

1.4. A Short History of SAX

The official SAX web site is at http://www.saxproject.org. You will find a more complete history there, with updates for anything that happened after this book went to print, as well as the current software release and its documentation.

1.4.1. SAX1

SAX 1.0 development started in late December 1997, shortly after publication of the last review draft of the XML 1.0 specification. The initial impetus was to permit Java applications to be independent of which parser they used, and to promote uniformity in the data models available to applications without imposing some particular data representation. At that time, several such Java parsers existed (notably Ælfred, Lark, MSXML, and XP), each with their own APIs and feature sets. That approach would clearly be counterproductive and had already caused complications for one early XML browser, Jumbo, used with a Chemical Markup Language built with XML. (See http://www.xml-cml.org for more information about CML and Jumbo.)

Discussion proceeded quickly. The development primarily took place on the open Internet xml-dev mailing list. There was no bureaucracy since it was organized and run by one person, David Megginson, the original author of Ælfred. Essential contributions were made by developers of other Java XML parsers, including Tim Bray, editor of the XML 1.0 Recommendation and author of Lark, and James Clark, Technical Lead of the XML 1.0 Recommendation and author of XP. In terms of openness, the process was similar to those used historically by the Internet Engineering Task Force (IETF) and by many current open source development projects. Unlike the process for most recent Java/XML API standards, helping to define SAX required no nondisclosure agreements, or reassignment of intellectual property rights, and had a transparent process. Public list archives are available, if you want to see how (or why!) some things turned out the way that they did.

The initial draft API was published in January 1998, less than a month after initial discussions started. It featured key characteristics still seen today: it was event based, and distinguished interface and implementation without insisting that implementations commit to the overhead of a "provider" glue layer. To improve its coolness factor, it used the org.xml.sax package name, since Jon Bosak owned the "xml.org" DNS domain name and gave approval for that use.[3] Best, it was indeed a Simple API for XML. Discussions continued fast and furious. More developers helped improve these early proposals, including the author of this book.

[3] This domain was subsequently transferred to the OASIS group, which later took over operations for the xml-dev mailing list. However, SAX still remains independent of OASIS. SAX is currently maintained using SourceForge.net project resources.

The SAX1 API was finalized in May 1998, just three months after XML itself was finalized, and was generally well received. Most Java XML parsers quickly adapted to it, and new ones quickly adopted it. At one point, it was possible to find no less than a dozen open source SAX1 parsers. Today, new XML projects tend to build on top of the standard APIs such as SAX, rather than underneath them, since most widely used parsers do support SAX.

1.4.2. SAX2

When SAX1 was finished, there were features it did not address. That was to be expected because of the 80/20 rule. Satisfying the 80% of application requirements that involved only simple functionality meant that only a small handful of applications needed more complex functionality; that handful was much less than 20% of the application space. Notably, anyone who tried to use SAX to round-trip XML data found that important parts were omitted. (Round-tripping a SAX event stream means turning it back into XML text and parsing the result, without losing any data.) Similarly, anyone using SAX to construct a DOM tree found that there was a mismatch: DOM also expected more information to be provided. Although many applications were happy not to see that additional data, it was still a conformance issue. Moreover, since DTD declarations were not available, it wasn't practical to maintain arbitrary valid documents with a SAX1-only parser. On top of all that, it wasn't possible to tell if a parser could validate, nor could you change whether or not it was validating; this all but prevented parser-neutral application configuration and setup. As developers learned their way around XML, the 80/20 line shifted so more functionality was needed.

So discussions continued, but at a much slower pace. In late 1998 some draft interfaces were posted, which later became the basis of the two standard SAX2 extensions. (Not many parsers worked with those interfaces until they were fleshed out later in the SAX2 process.) Discussions later in the next year focused on ways to let such additional extension handlers and other new features be added without changing "core" APIs, by supporting parser configurability.

The final catalyst for SAX2 was probably the realization that without parser-level API support, the XML Namespaces specification would probably not be adopted soon with any really standard semantics. Application-specific implementations tended to have bugs in their interpretation of the namespaces specification. (That specification has turned out to cause a surprising amount of confusion.) To make a long story short, further discussions happened, and SAX2 was finalized in May 2000. SAX1 parsers were initially wrapped in adapters that layered the namespace processing, making it easy to convert to use the core SAX2 APIs. The first parser to natively support the full set of SAX2 APIs, including the extension interfaces, was Ælfred2, in the second half of 1999. By the second half of 2000, such support was available in the current releases of most other widely used parsers.

This book focuses on the current SAX2 release, which includes minor bug fixes as well as more robust bootstrapping and clarifications, and explanations for the API documentation.

1.4.3. SAX2 Extensions

As mentioned earlier, one of the original reasons to extend SAX1 was that the SAX core didn't expose information needed by various applications and, of course, DOM. Not everyone needs or wants that information. A cautionary example is exposing comments, which were never intended to be used (or seen) by applications; they were grandfathered into XML APIs through horrible accidents involving old HTML browsers and DOM. However, lack of such such data was a problem for some applications. That 80/20 rule kept such features at a relatively low priority. The fact that exposing this information called for changes to parser internals ensured that it couldn't be part of the SAX2 core. (Because information such as a comment was discarded in SAX1, this information couldn't be layered, in the same way that org.xml.sax.helpers.ParserAdapter does for namespace support.)

The resolution was to decouple development of the SAX2 declaration and lexical handlers from the SAX core and to make them optional. The "SAX-extension" interfaces were not finalized until December 2000, well after the SAX2 core was finalized; at this writing, many of the deployed SAX2 parsers still only support the beta test versions of those interfaces. In practice, most SAX2 parsers do support these two handlers, which are mostly used to develop infrastructure tools. Applications value the "simple" nature of SAX, which lets them focus primarily on a single event handler interface included in the core of SAX.

In the future, most SAX2 extensions will be able to be layered independently of SAX2 parsers. Only very few additional kinds of information appear to need standardized support from inside such parsers.[4] Today, most new XML technologies are defined as layers above the XML Infoset, so they can (and should!) be implemented as layers above SAX2-based parsers rather than within them.

[4] See the SAX we site for more information about these. Also, some probable new extensions are noted in Appendix B, "SAX2 and the XML Infoset".

1.4.4. Is SAX2 a "Standard"?

In a word, yes: SAX is a shining example of a de facto standard API. You will have a hard time finding an XML parser written in Java that doesn't support SAX. In contrast to the recent spate of standards originated by a formal de jure standards body (notably the International Standards Organization, or the ISO), or to specifications pushed by vendors or a vendor-dominated consortium, SAX2 is a standard in the more classic sense. It was hammered into shape by users, quenched in the fire of real-world use, and adopted as a tool after it proved its worth. This partially motivates its small size and clear focus: it had a clear mission, and little of the "mission creep" pressure often caused by standards organization politics. This also explains why its legal status may seem to be unique; SAX is in the public domain, not copyrighted or controlled by any corporation or consortium.

You may be familiar with other examples of technology standards that were developed similarly. For example, the sockets network API widely used for TCP was popularized at the University of California at Berkeley; from there it migrated into other Unix systems and then into Microsoft Windows. Similar processes occured for other core Unix APIs and the standard C Library functions. (Some have entered de jure standardization processes through the ANSI or IEEE POSIX processes, or have been adopted by vendor consortiums like the one that produced the UNIX98 API set.)

The same sort of process has been happening with SAX. From its initial base in Java, it's been imported into many XML-programming tool sets in Python, Perl, Pascal, and JavaScript. There are several different C/C++ versions, and Microsoft has even provided SAX-like COM interfaces. Each new environment has made changes and adaptations. Some have remained truer to the original API (in Java) than others, but it looks as if this growth will only continue. In the best and most classic sense, SAX is a standard.

1.4.5. Sun's Java API for XML Processing (JAXP)

As of JDK 1.4, SAX2 has been incorporated into the Java2 Standard Edition (J2SE) through Sun's Java Community Process. It's part of Version 1.1 of Sun's Java API for XML Processing (JAXP). A JAXP implementation is bundled with JDK 1.4 releases, and is available separately for use with other Java-compliant platforms (JDK 1.1 and later). The Java2 Enterprise Edition (J2EE) has recognized JAXP for some time, and web applications that use servlets have long been using XML and SAX.

From the perspective of this book, JAXP is just a vehicle to get SAX2 interfaces (and the Crimson parser) into the hands of more Java developers. Sun incorporated these standard APIs directly into their API set, exactly as one would desire. In this case, the real community process had completed before Sun's own process started. The stamp of recognition provided by Sun facilitated further adoption; some organizations are uncomfortable with software that has no such recognition.

JAXP 1.1 also incorporates DOM Level 2 and some other APIs, including TRAX, a wrapper for XSLT-based transformations. TRAX offers limited SAX support; it supports producing partial SAX event streams as output or sometimes inputs. It's worth noting that if you use DOM, JAXP solves a critical portability problem for you. JAXP has the first "standard" Java solution for vendor-independent bootstrapping with DOM. DOM Level 3 plans to address that problem, but JAXP will have solved it years before Level 3 becomes widely available. If you don't use JAXP's DOM bootstrap APIs, you must use vendor-specific APIs to get a document object that's populated with the content of any XML text. Starting down the path of such vendor-specific APIs quickly leads to nonportable code. SAX has never had that problem, because it has always included vendor-neutral bootstrapping APIs. (Although JAXP defines additional SAX bootstrapping APIs, this book discourages their use.)

Previous | Home | Next
1.3. Why Not to Choose SAX? | Book Index | 1.5. Packages in the SAX2 API
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Packages in the SAX2 API (SAX2)

 SAX2Search this book

1.5. Packages in the SAX2 API

SAX2 consists of three packages:

org.xml.sax

This package contains the core SAX interfaces and exceptions, plus two concrete classes. The package has SAX1 as well as SAX2 support.

org.xml.sax.helpers

As suggested by its name, this package exists to help applications use the SAX core. Most of its classes are utility implementations of core interfaces (except the parser interfaces), but the portable bootstrap APIs are also part of this package. SAX2 added to what SAX1 started.

org.xml.sax.ext

This package holds the SAX2 extension handlers, described earlier, and will probably grow to hold other SAX2 extensions in the future.

Only the org.xml.sax package is guaranteed to be part of every SAX distribution. The helpers don't depend on the extensions, to preserve this notion of "core plus options." In practice, it's a rare distribution that doesn't include all of these packages, and SAX2 parsers without support for the extension handlers are likely to support only XML subsets. JAXP 1.1 (and hence JDK 1.4) includes all three packages.

Previous | Home | Next
1.4. A Short History of SAX | Book Index | 1.6. Some Popular SAX2 Parser Distributions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Some Popular SAX2 Parser Distributions (SAX2)

 SAX2Search this book

1.6. Some Popular SAX2 Parser Distributions

Today a variety of high-quality SAX2 parsers are available. Increasingly, they are packaged with Java programming environments, so you may not need to fetch one yourself unless you need upgrades (or bug fixes), or are constructing such a programming environment yourself (perhaps packaging an embedded system or a standalone application). You should be able to bootstrap any SAX parser. As a rule, if an XML parser is part of your Java programming environment, it already supports SAX and probably SAX2. The documentation should say whether SAX2 is supported. If it only mentions SAX1, you can upgrade to get most of the core SAX2 features; see Section 5.2, "SAX1 Support ", in Chapter 5, "Other SAX Classes", for more information.

If your programming environment doesn't include a SAX parser, you'll need to get and install one. This section provides a brief summary of some of the most widely available open source SAX2 parsers.[5] These packages all include SAX2, DOM Level 2, and JAXP 1.1 support, and can validate XML for you. They also have full support for the standard SAX2 extensions. If you don't happen to download documentation that includes the SAX2 documentation, it'll be available from the same site as the parser. All of these perform well in most applications, as long as you avoid the memory penalties of DOM.

[5]Proprietary SAX2 parsers exist, such as one from Oracle that is commonly used in Oracle-hosted server-side applications. More information is available on the Oracle web site, http://www.oracle.com/xml/.

Current versions of all these parsers do quite well on the open source SAX/XML conformance tests, available at http://xmlconf.sourceforge.net/java/. Those tests verify that these processors report essential information required of a SAX1 processor, and evaluate how well they support the XML 1.0 specification. SAX2 conformance testing isn't yet as well advanced, though some tests are now available.

In addition to a SAX2 parser, you will likely want to have some SAX2/XML utilities that are layered on top of that parser. The packages described here include a DOM implementation, which is normally provided as a clean layer over SAX2. You might also consider other more Java-friendly packages such as DOM4J (http://www.dom4j.org) or JDOM (http://www.jdom.org), both of which are layered over SAX2, as well as other APIs that provide more data-structure options. When you're learning SAX, having access to the source code of tools and applications built with SAX can help you learn the API, at least if it's high-quality source that uses the SAX APIs correctly.

1.6.1. Ælfred2

One of the original XML parsers mentioned earlier, Ælfred, has long been recognized for its simplicity, small size, and good performance. As XML parsers go, it is easy to read and understand. With a different maintainer (your humble author), this parser was updated to be the first with full native SAX2 support, and to substantially improve its conformance to the XML specification. This updated version is called Ælfred2, and versions have been incorporated in a variety of applications where its simplicity, size, and conformance are compelling features. It is now part of the GNU Classpath Extensions project and forms the core of the GNU JAXP library.

The updated version has taken SAX2 further than most other parsers. It has a highly modular structure; the reference distribution is able to use an optional "stream validator" that uses the SAX2 events. The model of an XML pipeline of such events is a natural and powerful way to think about SAX; the SAX2 pipeline package in this distribution lets applications compose arbitrary processing modules in series or parallel. This style of SAX2 processing is emphasized in this book, and some of the examples show how to use these advanced components. Validation and DOM support remain completely modular, and use SAX event pipelines, so Ælfred can still be distributed as a lightweight nonvalidating parser without those components. Likewise, the validation and DOM support don't need Ælfred to work.

The current version of Ælfred is licensed under the GNU General Public License (GPL), with the "library exception" clause to ensure that it can be used in proprietary applications (notably, embedded systems) that aren't themselves licensed under the GPL. That license is used with many GNU libraries, such as the GCC Java (GCJ) runtime libraries. Ælfred includes a gnujaxp.jar file that needs installation.

See http://www.gnu.org/software/classpathx/jaxp/ for information about the current distribution of Ælfred.

1.6.2. Crimson

Sun, through Java Project X in its Java division, was one of the earliest major Java vendors to support SAX and XML namespaces. This parser was the first to demonstrate that XML could be validated without a significant penalty. It was dozens of times faster than its competitors and offered more XML conformance. History buffs may like to know that its validation was based on some of the SGML/HTML validation code from the HotJava web browser, the original Java-and-the-Web showpiece software package. This XML code ties directly to some of the earliest Java software seen outside of JavaSoft.

Crimson is a version of the Java Project X software, updated to support SAX2, DOM Level 2, and JAXP 1.1 (for which it is the reference implementation). It was submitted to the Apache XML project to help trigger a "best of breed" XML parser.

Crimson is licensed under the Apache Software License. The Crimson parser has been incorporated into Sun's JDK 1.4 release as its standard XML parser. It is separately distributed as the reference parser for JAXP, so most JAXP distributions include it. This book describes Crimson Version 1.1.3 (matching JDK 1.4), dated October 2001, which includes jaxp.jar and crimson.jar files that need installation.

See http://java.sun.com/xml/ for information about this distribution.

1.6.3. Xerces

Xerces is a family of XML parsers in the Apache XML project; in this book, we refer only to the Java version, not the C/C++ version. It has evolved from the second generation of IBM's XML for Java (XML4J) parser, and much of its development and maintenance is still handled by IBM. It is relatively large, and is monolithic rather than modular. It also supports many nonstandard extensions. For example, validation against W3C's XML schemas is part of the parser, rather than a layered feature.

Xerces v2 is a third-generation project. Goals of that project include a more maintainable and modular design. It includes an internal XML event pipeline model, which is strikingly similar to that used in Ælfred to layer validation and DOM support, except that it doesn't use SAX2 to represent the XML Infoset data.

Xerces is licensed under the Apache Software License. This book describes Xerces Version 1.4.3, dated August 2001, which includes a xerces.jar file that needs installation.

See http://xml.apache.org/ for information about this distribution.

Previous | Home | Next
1.5. Packages in the SAX2 API | Book Index | 1.7. Installing a SAX2 Parser
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Installing a SAX2 Parser (SAX2)

 SAX2Search this book

1.7. Installing a SAX2 Parser

Unless you use JDK 1.4 (which bundles SAX2 and the Crimson parser) or some other environment that's already set up with SAX2 support (such as any up-to-date web application server), you will need to update your Java programming environment so that you can use SAX. Consult the documentation that comes with your parser and Java Virtual Machine for specific details. Assuming the SAX interfaces and your SAX parser are distributed in a single JAR file called xml.jar (you'll need to know and use the correct full pathname, including the directory), you'll probably use one of these approaches shown in the following list.

Add to extensions directory

If you use JDK 1.2 or later for your runtime environment,[6] you can install the JAR file into the jre/lib/ext subdirectory of your Java distribution. This is the preferred solution during development, since it's the simplest and least error-prone.

[6]Most current graphical development tools, called IDEs, bundle this software for Java.

On Windows, you may need to add this to two different locations: one for the development environment as well as one for the runtime environment.

Update class path on command line

This solution works with JDK 1.2 and later. Whenever you invoke a program that needs the SAX support (such as java, javac, or javadoc) pass the -cp xml.jar parameter to add SAX to the class path.

Add to CLASSPATH in environment

This is the original way to add software to your Java environment, and it works on a JDK 1.1-based system and on many Java implementations that aren't derived from Sun's JDK. You may prefer this technique if you have to make several different Java execution environments cooperate -- perhaps one for each IDE and test environment used for application development. You could also have your application use its bundled JVM when it's deployed, rather than whatever the end user happened to have around.

The details vary from operating system to operating system, and from installation to installation, because you may need to ensure that your CLASSPATH includes libraries internal to the JVM. Put the CLASSPATH assignment into your login script (autoexec.bat or your environment variables, .profile, .login, or other file). On Windows, you'll likely need to reboot after you modify autoexec.bat, to ensure that all new JVM instances see the new configuration.

You may end up with a variety of SAX2 parsers in your environment. Sometimes which parser you use will be important, but you should avoid creating such problems. See Section 3.2, "Bootstrapping an XMLReader" in Chapter 3, "Producing SAX2 Events" for information about making sure you're using a particular parser; there are several mechanisms, including setting system properties and adding META-INF/services/ resources to your class path. If you work within some application environment (perhaps a web server), you may want to look for specialized configuration mechanisms. Also, if you have SAX1 support in your environment, you can easily upgrade it; see Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

Note that because SAX lets applications hand character streams to parsers with java.io.Reader, you can't use JDK 1.0 with SAX. You need JDK 1.1, which is a more complete and stable release in any case. Since the Java environments that aren't based on Sun's code generally treat JDK 1.1 as the conformance target,[7] that should cause no real trouble. SAX itself doesn't require more recent APIs, but some of the tools you use with SAX might have such requirements. For portability, the example code in this book avoids use of APIs added in JDK 1.2 and later. The main impact of this restriction is that in a few cases you'll be able to get minor performance improvements by using the collections APIs.

[7]Many Macintosh developers can't use JDK 1.2 yet. The Microsoft JVM also does not support JDK 1.2 APIs.

Previous | Home | Next
1.6. Some Popular SAX2 Parser Distributions | Book Index | 1.8. What XML Are We Talking About?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What XML Are We Talking About? (SAX2)

 SAX2Search this book

1.8. What XML Are We Talking About?

Over the past years, there has been an explosive growth in the number of XML-related standards. Talking about XML has become confusing, because those three letters can mean so many different things. Some people actually mean what I've called "Greater XML." Think of it this way: Boston is significant city, but people who don't live there may often name Boston to refer to other nearby towns (Arlington, Cambridge, and so on). What they're really talking about is the "Greater Boston Metropolitan Area," or sometimes even just "Eastern Massachusetts."

In much the same way, many people now talk about "XML" when they really mean one of dozens of related technologies built around the nucleus of XML. Some of these may even be part of the original XML vision as "SGML for the Web." Using XML to develop documents using a DTD like DocBook (http://www.docbook.org) is clearly part of that original open systems vision. However, it's also been trendy to market "new and improved!" software as based on XML. Such ambiguities can be confusing and can even implicitly promote vendor lock-in, rather than liberate customer data from vendor control. The simplicity at the core of XML isn't friendly to lock-in strategies, but complex application layers on top of XML can certainly cause closed systems.

So when someone says that SAX is a great API for XML processing, exactly what part of Greater XML does that mean? Briefly, parts built with the "core" XML specifications. The following lists shows the parts that this book uses in most of its examples.

XML 1.0 (Second Edition)
http://www.w3.org/TR/REC-xml

This text document format is the core of XML. SAX2 parsers work with this format and turn it into a stream of events that present the XML Infoset. However, as we'll see, SAX can be quite useful without even parsing XML text. (The second edition incorporates a variety of bug fixes and a few functional changes, which were previously published as a separate list of errata.)

XML includes Document Type Declarations, or DTDs. These provide several processing facilities, most of which you can rely on even when you don't use a validating parser. All XML parsers must support DTDs; they're what "schema" technologies attempt to improve on.

Unicode support has been part of XML from the earliest days. Java programmers may tend to overlook the significance of that fact, since it's always been part of Java too. But it's actually a big deal that XML moves web technologies firmly away from ASCII toward Unicode, in all programming environments (not just Java) -- not everyone needs to be a native English speaker to make best use of Internet technologies. XML has even been called a "virus for Unicode."

XML Infoset
http://www.w3.org/TR/xml-infoset/

The Infoset is best explained as an abstract model for what XML represents: information like elements, attributes, and character data. The Infoset exposes XML structure, not meaningful data. Applications transform Infoset data into forms that are suited to their particular tasks, normally behind a veil of application objects, unless they manipulate the text like a text editor.

The SAX2 event APIs present Infoset-level data; the lower-level alternative is to work directly with text. (See Appendix B, "SAX2 and the XML Infoset" for details about Infoset support in SAX2.) Other XML infrastructure, such as XInclude, generally transforms or augments Infoset data. Higher-level APIs generally hide such XML structures.

XML Namespaces
http://www.w3.org/TR/REC-xml-names/

Namespaces are an optional convention for XML 1.0 documents. Namespaces distinguish elements and attributes so that names can be reused when necessary. For example, in document markup a <table> probably refers to a tabular presentation of data, but in a furniture catalog it might also refer to something rather different. XML namespaces distinguish those cases with name prefixes; unlike "straight XML" with DTDs, those prefixes are expected to change in different contexts (such as different parts of that furniture catalog). This makes combining namespaces and DTDs complicated.

One of the most visible differences between SAX1 and SAX2 is that SAX2 has integrated support for XML namespaces to promote their widespread adoption.

Over time, some other simple layers (and conventions) may become appropriate to view as part of the core of XML. The XML Base specification (http://www.w3.org/TR/xml-base/) might be an example of such a facility; it explains how to use an xml:base attribute to augment normal processing of relative URIs found in text.[8] Various internationalization rules and policies are also likely to fit into that core. One example is W3C work on the Character Model for the World Wide Web (http://www.w3.org/TR/charmod/), which promotes uniform handling of sequences used to represent some non-ASCII characters. Another is currently called XML Blueberry, which will modify XML 1.0 to allow use of new Unicode characters in element and attribute names. Those characters support languages not previously supported (before Unicode 3.1) and also improve support for languages such as Japanese.

[8]In fact, since this list includes the XML Infoset in the core, documents with the xml:base attribute implicitly need XML base in their core view of XML to augment normal interpretation of URIs in document content. Example 5-1 shows one way to implement such processing in SAX.

Many of the increasingly substantial layers over XML, such as schemas (there are many schema approaches, with one from W3C), schema APIs and tools (which may focus on non-XML data models, distant from "downtown XML"), Remote Procedure Calls ("RPCs"; again, many approaches including one from W3C), XPath (and its outgrowths), and XSLT are prime examples of technologies that deserve to be viewed as technology choices in their own right. They are other cities in the metropolis of Greater XML, satellites of the original village that leverage the original civic infrastructure. Some of those layers may even reflect different fundamental goals and requirements from those that originally drove the creation and adoption of XML. That doesn't mean that you won't put SAX interfaces on them (or at least SAX-friendly ones), but because they are data layers over the core of XML, they may involve API layers too.

If you look at Java implementations of other technologies in Greater XML, you'll probably find SAX not far from the surface. This book identifies a number of such SAX-based tools and shows SAX events used as a framework to efficiently integrate these different technologies.

Previous | Home | Next
1.7. Installing a SAX2 Parser | Book Index | 2. Introducing SAX2
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Introducing SAX2 (SAX2)

 SAX2Search this book

Chapter 2. Introducing SAX2

Contents:

Producers and Consumers
Beginning SAX
Basic ContentHandler Events
Producer-Side Validation
Exception Handling
Namespaces and SAX2

SAX gets its power from the unifying notion that sequences of event callbacks are powerful and lightweight ways to represent the information in XML documents. Building on that notion, you can create many powerful tools. Most of the essential SAX calls use interfaces, so the interesting behavior comes from how you combine implementations of those interfaces to assemble tools and what those implementations do.

This chapter shows the basic structure of SAX and of several classic SAX applications using an XML parser, the "simple" core of SAX. It starts by showing the essential components and the framework through which they relate. Then it shows how to customize the most important features and concepts in that framework and how to work with the core XML data model of elements, attributes, and text. You'll also see how to handle errors and learn how SAX exposes XML namespaces.

This chapter focuses on the parts of SAX that essentially every application needs to know. It doesn't provide full information about every interface. Later chapters elaborate on these structures and concepts, showing additional parts of these APIs, ways to combine SAX components, and how to work with additional parts of the XML data model. Depending on what your application needs to do, you may not need to know much more of SAX than is explained in this chapter.

2.1. Producers and Consumers

The first thing to learn is that there are really two kinds of roles in this API -- or three, if you include your role as director, configuring sets of components to serve those roles and provide your application's functionality. Complete SAX applications integrate all these roles.

The first role is an event producer, which is typically an XML parser packaged as an instance of some library class. The producer is in charge of pushing parsing events to objects that serve the second role: an event consumer. Most SAX applications will only have one event producer, though we'll look at some cases where you need more than one. This chapter touches on several of the ways to configure (or customize) event producers.

Consumers normally do most of the "real" work for any given SAX-based application: they make sense of the parsing events and often create some specialized data structures. Without a consumer to handle events, nothing happens! SAX2 defines several kinds of handlers to consume different parts of the XML content. Later chapters look at each kind of handler in detail, but in this chapter we look only at the most important methods and handlers.

When we show SAX components connecting, we'll use diagrams like Figure 2-1, with the dashed lines indicating individual event handlers. There are four of them because there are four handlers used to deliver content to consumers. The producer uses a big arrow, which should remind you in which direction it pushes events.

Figure 2-1. Producer and consumer

When you're using SAX, any or all of these components can be provided by your application or can be library components. Often you'll use an XML parser from a library to produce events, but in other cases applications produce such events directly.

Previous | Home | Next
1.8. What XML Are We Talking About? | Book Index | 2.2. Beginning SAX
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Beginning SAX (SAX2)

 SAX2Search this book

2.2. Beginning SAX

This chapter explores SAX through some progressively more functional examples, which build on each other to present the key concepts that are discussed later in more detail. Essential producer and consumer interfaces are presented together to show how they interact, and you'll see how to customize classic SAX configurations. We'll focus first on the producer side, saving most details about consumer-side APIs for a bit later.

2.2.1. How Do the Parts Fit Together?

In the simplest possible example, you (in your role as director) will get an XML parser, which will later produce parsing events. Then you will get a consumer and connect it to the producer for processing the most important events. Finally, you'll ask that parser to produce events, pushing them through to the consumer.

To start, focus on what the different parts are, and how they relate to each other. Example 2-1 is a simple SAX program, which you can compile and run if you like.

Example 2-1. SAX2 application skeleton

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class Skeleton {

    // argv[0] must be the absolute URL of an XML document
    public static void main (String argv [])
    {
        XMLReader       producer;
        DefaultHandler  consumer;

        // Get an instance of the default XML parser class
        try {
            producer = XMLReaderFactory.createXMLReader ();
        } catch (SAXException e) {
            System.err.println (
                  "Can't get parser, check configuration: "
                + e.getMessage ());
            return;
        }

    // Set up the consumer

        try {

        // Get a consumer for all the parser events
        consumer = new DefaultHandler ();

        // Connect the most important standard handler
        producer.setContentHandler (consumer);

        // Arrange error handling
        producer.setErrorHandler (consumer);
    } catch (Exception e) {
        // Consumer setup can uncover errors,
        // though this simple one shouldn't
        System.err.println (
              "Can't set up consumers:"
                + e.getMessage ());
            return;
    }

        // Do the parse!
        try {
            producer.parse (argv [0]);
        } catch (IOException e) {
            System.err.println ("I/O error: ");
        e.printStackTrace ();
        } catch (SAXException e) {
            System.err.println ("Parsing error: ");
        e.printStackTrace ();
        }
    }
}

This is a complete SAX application, though it's sort of boring since it throws away all the data the parser delivers. The only reason this program would print anything at all is if you didn't pass it an argument that was the URL for a well-formed XML file. Other than that, it's fairly typical of how you'll be using SAX2, at least in terms of the basic structure. You can make real programs from this skeleton if you substitute smarter components for the simple ones shown here.

We introduced a few SAX classes and interfaces, so we can add some details to our earlier producer/consumer picture to get Figure 2-2. This producer is an XMLReader, and we're listening to one consumer interface and the ErrorHandler. The whole thing is driven by an application which is pulling the whole document through the reader.

Figure 2-2. Basic SAX roles and components

XMLReader producer;

The most common type of SAX2 event producer is an XML parser. Like most parsers, XML parsers implement the XMLReader interface. Whether or not they parse actual XML (instead of HTML or something else), they are required to produce events as if they did.

Don't confuse this class with the java.io.Reader from which you can pull a stream of character data. SAX parsers produce streams of SAX events, which they push to event consumers. Those are rather different models for how to deliver data.

producer = XMLReaderFactory.createXMLReader ();

This is the best all-around SAX2 bootstrap API when you need an XML parser. The only time it should produce any kind of exception is when your environment is misconfigured. For example, you might need to set the org.xml.sax.driver system property to the class name for your parser (see Section 3.2.1, "The XMLReaderFactory Class" in Chapter 3, "Producing SAX2 Events").

You can (and should!) keep reusing this XMLReader, but you should only have one thread touch a parser at a time. That is, parsing is not re-entrant. Parsers are perfectly safe to use with multiple threads, except that two threads can't use the same parser at the same time. (That's a good rule of thumb for most objects in multithreaded code, in all programming languages; it should feel natural to apply that rule to SAX parsers.)

consumer = new DefaultHandler ();

The DefaultHandler class is particularly handy when you're just starting to use SAX. It implements most of the event consumer interfaces, providing stubbed out (no-op) implementations for each method that's not part of an extension handler. That means it's easy to subclass this method if you need a place to start: just override each stub method to provide real code when you need it. We'll use DefaultHandler to avoid presenting extra callback methods.

producer.setContentHandler (consumer);

In this chapter, we're only showing the most commonly used consumer interfaces. ContentHandler is used to report elements, attributes, and characters; that's enough to get almost all serious XML work done.

producer.setErrorHandler (consumer);

ErrorHandler lets applications control handling of various kinds of errors, and we'll need it in later examples. We'll usually look at error handling as a specialized kind of task, different from other consumer roles. Even though "handler" is part of its name, it's a different kind of object.

producer.parse (argv [0]);

This call tells a parser to read the XML text found at a particular fully qualified URL. There's another call you'll use when you don't have a URL for that text, but most of the time this is the call you ought to use. If you're tempted to pass filenames or relative URIs, just say no! Filenames need to be converted to URLs first (see Section 3.1.3, "Filenames Versus URIs" in Chapter 3, "Producing SAX2 Events"), and relative URIs must be converted to absolute ones.

Parsing can report exceptions. This is important, and not just because it's the only way that a chunk of code like this (using just an XMLReader) could seem to "do" anything. Normally, those exceptions will be thrown only for fatal errors, such as well-formedness errors in an XML document, or for document I/O problems.

The application thread is "pulling" the XML text through the XMLReader-style producer: the parse() call won't return until the whole document is parsed, or until parsing is aborted by throwing an exception. Until it returns, the thread that called the XMLReader is either blocking on I/O, parsing data that it just read, or "pushing" data into one of the consumer interfaces. That is, from the perspective of event consumers SAX2 is a "push" API: handlers do nothing until they're asked.

2.2.2. What Are the SAX2 Event Handlers?

SAX2 events are grouped into several interfaces, which we explore later in more detail. All except two are implemented by DefaultHandler. Each interface encapsulates a set of events; to see those events, applications give parsers objects that implement the handler interfaces they're interested in.

org.xml.sax.ContentHandler

Essentially every significant use of SAX2 involves this handler. The element and character data callbacks (discussed later in this chapter) are defined in this interface, as are callbacks for most other SAX2 events for general-purpose data. Many SAX2 applications will focus primarily on this interface. If you only need the core XML data model (elements, attributes, and text), this could be the only handler you use.

org.xml.sax.ext.DeclHandler

This handler reports DTD declarations that aren't exposed through DTDHandler (or in one case LexicalHandler) callbacks: declarations for elements, attributes, and parsed entities.

Because it is an extension handler, it won't necessarily be recognized by all SAX2 parsers, and DefaultHandler doesn't provide no-op implementations for its callbacks.

org.xml.sax.DTDHandler

This handler reports DTD declarations that the XML 1.0 specification requires all processors to expose: declarations for notations and for unparsed entities. Most applications won't use this interface unless they're connected to SGML-based infrastructure that depends on such tools. This is probably the most exotic SAX handler interface; web-oriented XML applications will use MIME types instead of notations and URIs instead of unparsed entities.

org.xml.sax.ErrorHandler

The events reported by this class are errors and warnings. These behaviors are part of XML, but not part of the data model so they don't show up in the Infoset. Grouping these events in one interface lets application code centralize treatment of XML or application data errors. After ContentHandler, it's probably the most important SAX2 handler. It's also usefully managed apart from other handlers, so in this book it's usually not lumped with "real" handlers. (This interface is discussed later in this chapter.)

org.xml.sax.ext.LexicalHandler

This interface mostly exposes information that is intended to be semantically meaningless, such as comments and CDATA section boundaries, as well as entity and DTD boundaries.

Because it is an extension handler, it won't necessarily be recognized by all SAX2 parsers, and DefaultHandler doesn't provide no-op implementations for its callbacks.

With the exception of ErrorHandler, you'll normally want to work with all of these interfaces as a single group: four interfaces, two for content in the document body and two for DTD content. That way, you will work with all the XML data from a document (its Infoset) as part of a cohesive whole. There are SAX2 helper classes (like DefaultHandler and XMLFilterImpl) that group most of these interfaces into classes, but they ignore the two extension handlers (Decl and Lexical handlers in the org.xml.sax.ext package). SAX2 application layers often handle such grouping; for example, you can subclass those helper classes in a different package, adding extension interface support.

The logic behind keeping these interfaces separate, rather than merging all of their methods into one huge interface, is that it's more appropriate for simple applications. You must explicitly ask for bells and whistles; they aren't thrust upon you by default. You can easily prune out certain data by ignoring the interfaces that report it. Most code only uses ContentHandler and ErrorHandler implementations, so the methods in other interfaces are easy to ignore. Plus, from the application perspective, parser recognition of the extension handlers isn't guaranteed. There's a slight awkwardness associated with needing to bind each type of handler separately, but that's a small trade-off for the benefit of having a modular API extension model already in place.

SAX2 defines another important interface beyond these handlers and the XMLReader: parsers use EntityResolver to retrieve external entity text they must parse. That interface is also stubbed out by DefaultHandler. If you want the parser to use local copies of DTDs rather than DTDs accessed from a server that might not be available, you'll want to become familiar with EntityResolver. However, it isn't really a consumer API since it doesn't deal directly with parsed XML data (the Infoset); it deals with accessing raw unparsed text, the same stuff that's given to XMLReader.parse() methods. This book presents it as a producer-side helper for parsers, in Section 3.4, "The EntityResolver Interface" in Chapter 3, "Producing SAX2 Events".

2.2.3. XMLWriter: an Event Consumer

The next part of SAX we show in this overview is really not a part of SAX, except that it uses SAX to do something you'll likely need to do fairly often. (Pretty much everyone does!) As you've seen, SAX2 includes an XMLReader interface, used to turn XML text into a stream of SAX events. But it does not include the corresponding XMLWriter to reverse the process: turning such events back into text and supporting XML for program outputs as well as inputs. SAX isn't only for reading XML. The same APIs are used to write XML too.

It's almost a tradition to show how to write most of such a class as an example when explaining SAX. We avoid that in this book because getting all the XML details right is tricky, and because this class is a clear example of something that should be treated as a reusable SAX library component. There are lots of ways the data needs to be escaped, and sometimes you need to use output encodings (like ASCII) that have problems representing some XML characters.

There's a better solution: use one of several such classes, which are widely available. This book uses the gnu.xml.util.XMLWriter class (bundled with gnujaxp.jar andÆlfred) when it needs XML generation functionality, because it doesn't force applications to discard as much of the XML data. It supports all of the SAX2 handlers, including the extension handlers LexicalHandler and DeclHandler, so it can round-trip almost all XML data. To use such classes, at least in their simple low-fidelity modes, you can modify the skeleton program shown earlier to something like this:

import java.io.FileOutputStream;
import gnu.xml.util.XMLWriter;

public class ... {

    ...
        setContentHandler (
        new XMLWriter (new FileOutputStream ("out.xml"))
        );
    ...
}

In addition to the GNU class used in this book, other versions are available. One is provided with DOM4J org.dom4j.io.XMLWriter, which supports Content and Lexical handlers and evolved from the com.megginson.sax.XMLWriter class, which supports only ContentHandler. Curiously, neither Crimson nor Xerces include such SAX-to-text functionality at this time.

2.2.3.1. Event pipelines

Of course, just parsing and echoing data is not very useful. Such classes are best used to output XML data that you've massaged a bit. We'll look at two ways to do this later. One way is to use an XML pipeline, where consumers produce data for other consumers, as illustrated in Figure 2-3. For example, one stage could filter the event stream from a parser to remove various uninteresting elements, or otherwise transform the data, and then feed the result to an XMLWriter. You can combine several such stages into a "pipeline" and debug them using an XMLWriter to watch data as it flows through particular stages. Remember that XMLReader isn't the only kind of SAX event producer: programs can write events and feed the result to an XMLWriter. Also, the consumer doesn't need to be an XMLWriter; it could construct any kind of useful data structure. In fact we'll look later at doing this with DOM.

Figure 2-3. Simple SAX2 event pipeline

This kind of processing pipeline is a fundamental model for more advanced uses of SAX and for structuring components that are SAX-aware. We look at pipelines again in Section 4.5, "XML Pipelines " in Chapter 4, "Consuming SAX2 Events". For now, keep in mind that sometimes event consumers will be producing events for later processing components.

2.2.3.2. Concerns when writing XML text

There are several important issues to consider when writing XML output, which should be mentioned in the documentation for the XMLWriter you use. You may even be able to use your XMLWriter to canonicalize output, so you can safely compare processor output or create digital signatures. The GNU class shown earlier handles most of these directly, but that's not true for all such classes.

  • You need the flexibility to choose different line endings, such as Macintosh style (CR only), DOS style (CRLF), and Unix style (LF only). The default should be right for the host Operating System, but sometimes that's not right for the destination.

  • The SAX2 event stream might discard essential namespace prefix information. If you're using documents with namespaces, you need to provide a sanitized event stream, making sure either that such data is not discarded (using the "mixed mode" namespace handling discussed later in this chapter) or that corresponding data gets synthesized (maybe in some pipeline stage).

  • You might be sending XML to applications that don't handle DTDs or external entities very well. For example, many web browsers won't read DTDs. To talk robustly to such applications, you might need to send standalone documents.

  • If your application just uses ContentHandler events, you'll have discarded information needed to re-create "high-fidelity" output reflecting DTD content, comments, entity references, and CDATA section boundaries. More handlers are detailed in Chapter 4, "Consuming SAX2 Events" as well as and briefly summarized later in this section; most of the writers implement many such interfaces.

  • If you don't want to use UTF-8 as your character encoding (or UTF-16), you'll have to be sure the names used by your markup can be expressed using that character encoding. That's because while numeric character references can be used inside text, they can't be used inside markup components like element and attribute names. ASCII, for example, is hopeless at handling element names that use Japanese ideographic characters, but it can handle Japanese text if you don't mind that every character in the document text is cryptically expressed as a numeric character reference.

  • The first time you try to debug XML output where a single line is even just a few kilobytes in length, you'll want your XMLWriter to be "pretty printing." Minimally it should add line breaks; ideally it should be able to indent to show document structure.

Such an XMLWriter is part of almost every developer's SAX toolkit, even though it isn't part of SAX itself. As you work with SAX, you'll probably start to collect and develop your own library of such reusable event consumer code.

Previous | Home | Next
2. Introducing SAX2 | Book Index | 2.3. Basic ContentHandler Events
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Basic ContentHandler Events (SAX2)

 SAX2Search this book

2.3. Basic ContentHandler Events

You've just seen how the parts of a SAX2 application fit together, so now you're ready to see how the data is actually handled as it arrives. Here we focus on the events that deal with the core XML data model of elements, attributes, and text. To work with that model, you need to use only a handful of methods from the ContentHandler interface.

2.3.1. The DefaultHandler Class

As mentioned earlier, this class is a convenient way to start using SAX2 because it provides stubs for many of the handler methods. You can just override those stubs with methods to do real work. Using DefaultHandler as a base class is just an implementation option. It's often just as convenient not to use such a base class. The class is used in this chapter to avoid explaining handler methods that you don't really need.

In some scenarios, Sun's JAXP requires you to use DefaultHandler as a base class. That's much more of a restriction than SAX itself makes. If you stick to using the SAX XMLReader API, as recommended in this book, you'll still have the option of using DefaultHandler as a base class, but this policy won't be imposed on your application code. For example, you can have separate objects to encapsulate policies such as error handling, so you won't need to hardwire all such policies into a single class.

2.3.2. Example: Elements and Text

Let's use this simple XML document to learn the most essential SAX callbacks:

<stanza>
    <line>In a cavern, in a canyon,</line>
    <line>Excavating for a mine,</line>
    <line>Dwelt a miner, forty-niner,</line>
    <line>And his daughter Clementine.</line>
</stanza>

This is a simple document, only elements and text, with no attributes, DTD, or namespaces to complicate the code we're going to write. When SAX2 parses the document, our ContentHandler implementation will see events reported for those elements and for the text. The calls will be more or less as follows; they're indented here to correspond to the XML text, and the characters() calls show strings since slices of character arrays are awkward:

startElement ("", "", "stanza", empty)
    characters ("\n    ")
    startElement ("", "", "line", empty)
    characters ("In a cavern, i");
    characters ("n a canyon,");
    endElement ("", "", "line")
    characters ("\n    ")
    startElement ("", "", "line", empty)
    characters ("Excavating for a mine,");
    endElement ("", "", "line")
    characters ("\n    ")
    startElement ("", "", "line", empty)
    characters ("Dwelt a miner, forty-niner,");
    endElement ("", "", "line")
    characters ("\n    ")
    startElement ("", "", "line", empty)
    characters ("And his daughter");
    characters (" Clementine.");
    endElement ("", "", "line")
    characters ("\n")
endElement ("", "", "stanza")

Notice that SAX does not guarantee that all logically consecutive characters will appear in a single characters() event callback. With this simple text, most parsers would deliver it in one chunk, but your application code can't rely on that always being done. Also, notice that the first two parameters of startElement() are empty strings; they hold namespace information, which we explain toward the end of this chapter. For now, ignore them and the last parameter, which is for the element's attributes.

For our first real work with XML, let's write code that prints only the lyrics of that song, stripping out the element markup. We'll start with the characters() method, which delivers characters in part of a character buffer with a method signature like the analogous java.io.Reader.read() method. This looks like Example 2-2.

Example 2-2. Printing only character content (a simple example)

public class Example extends DefaultHandler {
    public void characters (char buf [], int offset, int length)
    throws SAXException
    {
    System.out.write (new String (buf, offset, length));
    }
}

If you create an instance of this Example class instead of DefaultHandler in Example 2-1 and then run the resulting program[9] with a URL for the XML text shown earlier, you'll see the output.

[9]On some systems, the user will need to provide system property on the command line, passing -Dorg.xml.sax.driver=..., as shown in Section 3.2, "Bootstrapping an XMLReader" in Chapter 3, "Producing SAX2 Events".

<userinput>$ java Skeleton file:///db/sax2/verse.xml</userinput>

    In a cavern, in a canyon,
    Excavating for a mine,
    Dwelt a miner, forty-niner,
    And his daughter Clementine.
$

You'll notice some extra space. It came from the whitespace used to indent the markup! If we had a DTD, the SAX parser might well report this as "ignorable whitespace." (See Section 4.1.1, "Other ContentHandler Methods " in Chapter 4, "Consuming SAX2 Events" for information about this callback.) But we don't have one, so to get rid of that markup we should really print only text that's found inside of <line> elements. In this case, we can use code like Example 2-3 to avoid printing that extra whitespace; however, we'll have to add our own line ends since the input lines won't have any.

Example 2-3. Printing only character content (a better example)

public class Example extends DefaultHandler {
    private boolean    ignore = true;

    public void startElement (String uri, String local, String qName,
    Attributes atts)
    throws SAXException
    {
    if ("line".equals (qName))
        ignore = false;
    }

    public void endElement (String uri, String local, String qName)
    throws SAXException
    {
    if ("line".equals (qName)) {
        System.out.println ();
        ignore = true;
    }
    }

    public void characters (char buf [], int offset, int length)
    throws SAXException
    {
    if (ignore)
        return;
    System.out.write (new String (buf, offset, length));
    }
}

With a more complicated content model, this particular algorithm probably wouldn't work. SAX content handlers are often written to understand particular content models and to carefully track application state within parses. They often keep a stack of open element names and attributes, along with other state that's specific to the particular task the content handler performs (such as the "ignored" flag in this example). A full example of an element/attribute stack is shown later, in Example 5-1.[10]

[10]Whitespace handling in text can get quite messy. XML defines an xml:space attribute that may have either of two values in a document: default, signifying that whatever your application wants to do with whitespace is fine, and preserve, which suggests that whitespace such as line breaks and indentation should be preserved. W3C XML Schemas replace default with two other options to provide a partial match for the whitespace normalization rules that apply to attribute values.

In simple cases like this, where namespaces aren't involved, you could use a particularly simple stack, as shown in Example 2-4. You can use such an element stack for many purposes. The depth of the stack corresponds to the depth of element nesting. This feature can help you debug by allowing you to structurally indent diagnostics. You can also use the stack contents to make decisions: maybe you want to print line elements that are from some stanza of a song, but not lines spoken by a character in a play. To do that, you might verify that the parent element of the line was a stanza. Make sure you understand how this example works; once you understand how startElement() and endElement() always match, as well as how they represent the document structure, you'll understand an essential part of how SAX works.

Example 2-4. Printing only character content (element stack)

public class Example extends DefaultHandler {
    private Stack    stack = new Stack ();

    public void startElement (String uri, String local, String qName,
    Attributes atts)
    throws SAXException
    {
    stack.push (qName);
    }

    public void endElement (String uri, String local, String qName)
    throws SAXException
    {
    if ("line".equals (qName))
        System.out.println ();
    stack.pop ();
    }

    public void characters (char buf [], int offset, int length)
    throws SAXException
    {
    if (!"line".equals (stack.peek ()))
        return;
    System.out.write (new String (buf, offset, length));
    }
}

Although they didn't appear in this simple scenario, most startElement() callbacks will have if/then/else decision trees that compare element names. Or if you're the kind of developer who likes to generalize such techniques, you can store per-element handlers in some sort of table and look them up by name. In both cases, you need to have some way to handle unexpected elements, and because of XML namespaces, the qName parameter isn't always what you should check first. One policy is just to ignore unexpected elements, which is what most HTML browsers do with unexpected tags. Another policy is to treat them as some kind of document validity error.

2.3.3. The Attributes Interface

In the previous section, we skipped over the attributes provided with each element. Let's look at them in a bit more detail.

SAX2 wraps the attributes of an element into a single Attributes object. For any attribute, there are three things to know: its name, its value, and its type. There are two basic ways to get at the attributes: by an integer index (think "array") or by names. The only real complication is there are two kinds of attribute name, courtesy of the XML Namespaces specification.

2.3.3.1. Attribute lookup by name

You often need to write handler code that uses the value of a specific attribute. To do this, use code that accesses attribute values directly, using the appropriate type of name as arguments to a getValue() call. If the attribute name has a namespace URI, you'll pass the URI and the local name (as discussed later in this chapter). Otherwise you'll just pass a single argument. A value that is an empty string would be a real attribute value, but if a null value is returned, no value was known. In such a case, your application might need to infer some nonempty attribute value. (This is common for #IMPLIED attributes.)

Consider this XML element:

<billable label='finance'
    xmlns:units="http://www.example.com/ns/units"

        units:currency="NLG"
    >
    25000
</billable>

Application code might need to enforce a policy that it won't present documents with such data to users that aren't permitted to see "finance" labeled data. That might be a meaningful policy for code running in application servers where users could only access data through the server. Code to enforce that policy might look like this:

public void
startElement (String uri, String local, String qName, Attributes atts)
throws SAXException
{
    String    value;

    value = atts.getValue ("label");

    if ("finance".equals (value) && !userClearedForFinanceData
    getUser ()))
    throw new SAXException ("you can't see this data");

    ... process the element
}

Other application code might need to know the currency in which the billable amount was expressed. In this example, this information is provided using namespace-style naming, so you would use the other kind of accessor to ensure that you see the data no matter what prefix is used to identify that namespace:

    String	currency;
    currency = atts.getValue ("http://www.example.com/ns/units",
    "currency");

    // what's the best exchange rate today?

There are corresponding getType() accessors, which accept both types of attribute names, but you shouldn't want to use those. After all, if you know enough about the attribute to access it by name and to process it, you should certainly know its type already!

Accessing attribute values or types using an index is faster than looking up their names. If you need to access attribute values or types more than once, consider using the appropriate one of the two getIndex() calls to get and save the index, as well as using the third syntax of the getValue() or getType() calls (shown in the next section).

2.3.3.2. Attribute lookup by index

You might need to look at all the attributes provided with an element, particularly when you're building infrastructure components. Here's how you might use an index to iterate over all the attributes you were given in a startElement() callback and print all the important information. This code uses a few methods that we'll explain later when we discuss namespace support. getLength() works like the "length" attribute on an array.

Attribute       atts = ...;
int             length = atts.getLength ();

for (int i = 0; i < length; i++) {
    String uri = atts.getURI (i);

    // Does this have a namespace-style name?
    if (uri.length () > 0) {
    System.out.print ("{ " + uri);
    System.out.print (" " + atts.getLocalName (i) + " }");

    // no namespace
    } else
    System.out.println (atts.getQName (i));

    // value comes from document, or is defaulted from DTD
    System.out.print (", value = " + atts.getValue (i))

    // type is CDATA unless it comes from <!ATTLIST ...> in DTD
    System.out.print (", type = " + atts.getType (i))
}

You'll notice that accomodating input documents that use XML namespaces has complicated this code. It's important to remember that from the SAX perspective, attributes can have either of two kinds of names, and you must not use the wrong kind of name. (The same is true for elements.) Application code that handles arbitrary input documents will usually needs to handle both types of names, using the logic shown earlier. It's rarely safe to assume your input documents will only use one kind of name.

It's often good practice to scan through all the attributes for an element and report some kind of validity error if a document has unexpected attributes. (These might include xmlns or xmlns:* attributes, but often it's best to just ignore those.) This can serve as a sanity check or a kind of procedural validation. For example, if you validated the input against its own DTD, that DTD might have been modified (using the internal subset or some other mechanism) so that it no longer meets your program's expectations. Such a scan over attribute values can be a good time to make sure your application does the right thing with any attributes that need to be #IMPLIED, or have type ID.

2.3.3.3. Other attributes issues

Attribute values will always be whitespace-normalized as required by the XML specification. This means that the only whitespace in an attribute will be space characters or whitespace provided by character references to a tab, newline, or carriage return. If the type isn't reported as CDATA, additional normalization is done: leading and trailing spaces are stripped, and consecutive space characters are replaced by a single space.

If the parser read the DTD, you are able to see the XML attribute type it declared. The best way to see this type is to use the DeclHandler.attributeDecl() event, which needs a bit of advance planning. (This callback is discussed later in Section 4.3.1, "The DeclHandler Interface " in Chapter 4, "Consuming SAX2 Events".) Or you can use the Attributes.getType() methods if you can deal with incomplete reporting for enumerated types. (You won't see the possible values, and the type will either be NOTATION or NMTOKEN.)

The Attributes object passed to startElement() is only usable during that callback. If you need access to information found there, you must copy it. A utility AttributesImpl class is available, with a copy constructor, and is discussed in Chapter 5, "Other SAX Classes" in Section 5.1.1, "The AttributesImpl Class "..

The methods in the Attributes interface are summarized in Appendix A, "SAX2 API Summary". For more information, consult the SAX javadoc.

2.3.4. Essential ContentHandler Callbacks

In the earlier code example, we used some callbacks without really explaining what they did and what their parameters were. This section provides more details.

In the summaries of handler callbacks presented in this book, the event signatures are omitted. This is just for simplicity: with a single exception (ContentHandler.setDocumentLocator()), the event signature is always the same. Every handler can throw a SAXException to terminate parsing, as well as java.lang.RuntimeExceptions and java.lang.Error, which any Java method can throw. Handlers can throw such exceptions directly, or as a slightly more advanced technique, they can delegate the error-handling policies to an ErrorHandler and recover cleanly if those calls return instead of throwing exceptions. (ErrorHandler is discussed later in this chapter.)

The ContentHandler callbacks include:

void startElement(uri,local,qName,Attributes atts)
void endElement(uri,local,qName)

These two callbacks bracket element content, starting with startElement() to identify the element and provide its attributes. Typically, startElement() will be followed by a series of other event callbacks to report child content, such as character data and other elements. After all children of the element have been reported, endElement() reports the end of the element.

String uri

For elements associated with a namespace URI, this is the URI. For other kinds of elements, this is the empty string.

String local

For elements associated with a namespace URI, this is the element name with any prefix removed. For other kinds of elements, this is the empty string.

String qName

This is the element name as found in the XML text, but for elements associated with a namespace URI, this might be the empty string. (Don't rely on it being nonempty unless the URI is empty, or you've configured the parser in "mixed" namespace reporting mode as described later in this chapter, in Section 2.6.3, "Namespace Feature Flags".)

Attributes atts

An element's attributes are only provided in the startElement() call. The atts object is owned by the parser and is only on short-term loan to the event callback. If your application code needs to save attribute data, it must make a copy. (The AttributesImpl helper class may help.)

These callbacks appear in pairs unless an exception is thrown to abort parsing. Even empty elements (like <this/>) cause two calls.

Most applications do a lot of work in startElement() callbacks to set up further processing, but endElement() work varies. Sometimes endElement() does nothing, sometimes it's just a quick state cleanup (popping stacks), and sometimes it's where all the work queued during an element's processing is finally performed.

void characters (buf, offset, length)

Text content is provided as a range from a character array. Applications will often need to make a copy of this data, appending it either to another character array or to a StringBuffer. (Use strings if their extra cost is not a problem.) Then the "real action" to process character data would be taken when this callback learns that all the relevant characters have been provided, often because of a startElement() or endElement() call.

char buf[]

A character array holding the text being provided. You must ignore characters in this buffer that are outside of the specified range.

int offset

The index of the first character from the buffer that is in range.

int len

The number of text characters that are in the range's buffer, beginning at the specified offset.

Application code must expect multiple sequential calls to this method. For example, it would be legal (but slow) for a parser to issue one callback per character. Content found in different external entities will be reported in different characters() invocations so location information is reported correctly. (This is described in Section 4.1.2, "The Locator Interface " in Chapter 4, "Consuming SAX2 Events".) Most parsers have only a limited amount of buffer space and will flush characters whenever the buffer fills; flushing can improve performance because it eliminates a need for extra buffer copies. Excess buffer copying is a classic performance killer in all I/O-intensive software.

The XML specification guarantees that you won't see CRLF- or CR-style line ends here. All the line ends from the document will use single newline characters ("\n"). However, some perverse documents might have placed character references to carriage returns into their text; if you see them, be aware that they're not real line ends!

There are many other methods in the ContentHandler interface, discussed later in Section 4.1.1, "Other ContentHandler Methods " in Chapter 4, "Consuming SAX2 Events".

Previous | Home | Next
2.2. Beginning SAX | Book Index | 2.4. Producer-Side Validation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Producer-Side Validation (SAX2)

 SAX2Search this book

2.4. Producer-Side Validation

All uses of SAX2 parsers will involve extending and customizing the basic scenario we saw earlier. Our next example illustrates two basic configuration mechanisms: error handling options, which lets you use the appropriate policy when you see errors, and parser configuration through feature flags, which let you control some details of how the parser works. (Some event handlers are managed with a configuration mechanism that is quite similar to the feature flag mechanism.) The example also shows how SAX2 parsers expose the core XML notion of DTD-based validation.

Validity and XML

Validation is particularly important when you are interchanging documents that have been wholly or partially authored by hand, but it can also be helpful when working with XML that's generated by custom code. When you validate an XML document, you ensure that it meets certain rules needed to process them -- such as requiring a <title> element as the first child of every <chapter> element or prohibiting dangling internal cross-references.

Validation is done at several levels in most applications. Lower levels tend to use rule-based logic, such as the DTD validation that's defined by XML 1.0. The various types of XML schema provide different kinds of rule-based sanity checks, which are usually done before applications see the data. (W3C's schemas also extract additional information items, beyond the XML data model of elements, attributes, and text. This information is called the "Post-Schema-Validation Infoset" or PSVI.) Higher-level validation processes tend to involve richer notions of data validity and tend to be expressed as procedural logic. For example, "business logic" often involves ad hoc relationships, policies, and heuristics; it relies on information not normally expressible by DTD or schema-style rules. Such logic is often captured in application-level methods. As a rule, no single data validation technology is sufficient for all purposes.

Your development process should try to ensure that you create only valid documents; you will likely send XML to applications that don't handle invalid data very well. Safe operational practice involves validating all documents received from other parties and accepting the small costs involved. (Use local copies of DTDs, or schemas, to avoid depending on remote files that might disappear. Techniques to achieve this are discussed in Section 3.4, "The EntityResolver Interface" in Chapter 3, "Producing SAX2 Events".) The cost of rule-based validation is usually smaller than routine system load variations for real applications; even in parsing speed benchmarks it's rarely high. It's usually worth the cost since it can prevent someone else's data from accidentally breaking your software. Validation against a good DTD (or schema) provides a useful base level of input data checking, but it will rarely be sufficient.

You will often tell XML parsers to validate XML as they produce events. Because SAX2 provides access to most of the data in XML documents, including declarations from DTDs, it also supports performing such validation on the event consumer side, possibly with a cached DTD or schema. (The consumer side is the only place to perform procedural validation.) Such consumer-side validation can be important when you're trying to make your program output meet the constraints of a particular information interchange agreement; just add a streaming validation stage to your output processing. This approach can also be used for DOM revalidation and similar purposes. Here, we look at how to validate data that is already in the form of XML text.

Keep in mind that some important DTD-related processing does not involve validation. Documents with DTDs can use entity substitution for document modularity and text portability, and can have attributes defaulted and normalized. Validation with DTDs only involves checking a set of rules. Disabling DTD validation turns off only the rule checks, not the processing for entities and attributes.

2.4.1. SAX2 Feature Flags

SAX2 exposes many parser behaviors, including DTD validation, using a "feature flag" mechanism. These flags are Boolean settings, which may have values or be unspecified. Parsers can have up to four different modes for any feature flag. For example, with the validation flag SAX2 implies four kinds of XML parsers:

Optionally validating parsers

The feature flag is read/write and can be either true or false. If it's set to false, few nonfatal errors will be reported and parsing will be a bit faster (maybe 5 or 10 percent of the cost of parsing XML, which is usually negligible to start with).

Nonvalidating parsers

The feature flag is read-only and always false. Some nonfatal errors might be reported (the XML specification demands them in some cases).

Always validating parsers

The feature flag is read-only and always true. Validity errors are always reported as nonfatal. (By default, such errors are ignored; see Section 2.4.2, "Handling Validity Errors" later in this chapter.)

Unknown validation behavior

The feature flag is not recognized, so its value can't be determined. (This mode is uncommon for the SAX2 validation flag, but you'll see it with other feature flags.)

Later in this chapter, look at the feature flags used to characterize namespace processing. Those flags are not optional, so fewer potential parser modes are possible. All the standardized feature flags are detailed in Section 3.3.2, "XMLReader Feature Flags" in Chapter 3, "Producing SAX2 Events".

In SAX, URIs identify feature flags. These are used purely as unique identifiers. This is the same approach used in XML namespaces: don't use these URIs to retrieve data, even if they do look like URLs you could type into a browser. The URI http://xml.org/sax/features/validation identifies the flag-controlling validation.

URIs = URLs + URNs

The use of URIs in XML namespaces has been confusing, and since SAX2 also uses URIs to identify parser feature flags and properties, the same sort of confusion can show up. Think of URIs as names: you can talk about "Fred" even if he's not there, or about "Godot" even if he may not exist, and "the third house on the left" probably makes sense to someone standing at your side.

Classically, a Universal Resource Identifier (URI), is either a Universal Resource Locator (URL) or a Universal Resource Name (URN). Both types of URIs are represented as strings. You're used to seeing URLs in web browsers; they serve as detailed addresses. They often look like http://www.example.com/ but they may use other URI schemes -- for example, they may use https:, ftp: and file:. The scheme indicates the way to access the resource. URNs use URI schemes that start with urn:. You probably have not seen many URNs; one example is urn:uuid:221ffe10-ae3c-11d1-b66c-00805f8a2676. URN schemes (like uuid in this example) describe what the resource is, more than how to access it.

Filenames are never URIs, but you can convert a filename into a URL (hence URI) that works on systems where the original filename was legal. Just to be confusing, there are also "relative URIs," which often look like POSIX-style filenames. Like filenames, relative URIs should never be handed directly to a SAX parser or be used as namespace identifiers.

With XML namespaces and SAX2, the term URI is used to emphasize that the string is being used as a pure identifier: it's more like a URN than a URL, even when the URI is syntactically a URL. It's explicitly irrelevant whether any resource is actually associated with the URI. Don't assume you can fetch resources using those URIs.

To check how a given XML parser handles validation, use code similar to Example 2-5. Code for any other kind of parser feature will look much the same, as long as you use the correct ID for the feature flag; you'll see the same exception types working in the same way. (The same is true for parser "properties," which you'll see in Section 3.3.1, "XMLReader Properties" in Chapter 3, "Producing SAX2 Events".)

Example 2-5. Checking for validation support

XMLReader       producer;
String          uri = "http://xml.org/sax/features/validation";

// ... get the parser

// Try getting and setting the flag
try {
    System.out.println ("Initial validation setting: "
    + producer.getFeature (uri));
    // if we get here, validation behavior is known

    producer.setFeature (uri, true);
    // if we get here, the parser either validates by
    // default or is optionally validating

} catch (SAXNotSupportedException e) {
    // value not supported; parser is nonvalidating
    System.out.println ("Can't enable validation: "
    + e.getMessage ());
    System.exit (1);

} catch (SAXNotRecognizedException e) {
    // feature not understood; parser has weak SAX2 support.
    // maybe it's a SAX1 parser inside a ParserAdapter
    System.out.println ("Doesn't understand validation: "
    + e.getMessage ());
    System.exit (1);
}

As a rule, programs will probably set the validation flag to true only when they really need reports of validity errors. (Why? As we'll see in a moment, it's natural to ignore reports of validity errors when they're not important, so it doesn't much matter if you validate when you don't need to.) The skeleton program in Example 2-1 really just needs a setFeature() call and a small update to the diagnostic message, to be sure it's always validating. (The diagnostics could be more precise using some more-specialized exceptions that we haven't discussed yet.)

// Get an instance of the default XML parser class
try {
    producer = XMLReaderFactory.createXMLReader ();
    producer.setFeature (
    "http://xml.org/sax/features/validation",
    true);
} catch (SAXException e) {
    System.err.println (
      "Can't get validating parser, check configuration: "
    + e.getMessage ());
    return;
}

The validation feature flag is probably the most widely used, with the possible exception of the flags controlling namespace handling. Most parsers leave validation off by default to save some minor parsing overhead.

2.4.2. Handling Validity Errors

If you modify the skeleton program to set the parser's validation flag and then run it on a well-formed but invalid document (perhaps one without a DTD), you will probably be surprised to discover that it doesn't seem to report any errors. That's exactly what should happen since it's the default behavior specified by SAX. To make validity errors cause anything interesting to happen, you have to change how they're handled. If you don't change this handling, you won't be able tell a validating parser apart from a nonvalidating one!

The simplest way to change the handling of validity errors is to make them work just like well-formedness errors: by aborting the parse. This uses the ErrorHandler interface that we look at later in this chapter, in Section 2.5.2, "ErrorHandler Interface", but for now it's simpler to focus on one method. In terms of the skeleton program shown earlier, such a change can be an update to just one line, using an anonymous inner class to make the code look simple. (Of course, avoid using anonymous classes for anything complex; they can make code hard to maintain.)

// Get a consumer for all the parser events
consumer = new DefaultHandler () {
    public void error (SAXParseException e)
    throws SAXException
    { throw e; }
    };

XML parsers call ErrorHandler.error() whenever they find a validity error, or when they see certain other nonfatal errors. In this case, our custom handler adopts a policy that whenever it sees such an error, it will abort the parse by throwing the exception reported to it. Later in this chapter we look at some alternative policies.

When your callback detects serious application-level errors, you can throw a SAXException from any SAX event handler callback to abort parsing. That doesn't have be done only from an ErrorHandler. For example, when input data is valid XML but doesn't meet essential semantic requirements of the application, report it using some kind of SAXException. If your code only knows how to process shipping invoices, then greeting cards should be rejected immediately.

Previous | Home | Next
2.3. Basic ContentHandler Events | Book Index | 2.5. Exception Handling
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Exception Handling (SAX2)

 SAX2Search this book

2.5. Exception Handling

Exceptions are the primary way that SAX event consumers communicate to event producers; this is the reverse of the typical communication pattern (from producer to consumer). We'll look at SAX exceptions before we delve more deeply into either producers or consumers. We'll look at the several types of exceptions that might be thrown, the error handler interface that lets your code decide how to handle errors, and then how these normally fit together.

Keep this rule of thumb in mind: when a SAX handler throws any exception -- including a java.lang.RuntimeException or a java.lang.Error -- parsing stops immediately. The exception passes through the parser and is thrown by XMLReader.parse(). Beyond some possible additional error reports, the only additional event callback should be ContentHandler.endDocument(). This method is always called before parsing finishes, even after errors, to ensure it can be used for cleaning up. (That callback is presented in Chapter 4, "Consuming SAX2 Events", in Section 4.1.1, "Other ContentHandler Methods ".)

2.5.1. SAX2 Exception Classes

There are four standard exception classes, with a common base class used in the signature for all handler methods. The parse() methods, as well as the EntityResolver class presented in Section 3.4, "The EntityResolver Interface" in Chapter 3, "Producing SAX2 Events", can also throw java.io.IOException to indicate problems unrelated to XML text content. You will find that many XML APIs are declared the same way; for example, JAXP parser methods may throw such exceptions even if they don't expose SAX events directly. See Appendix A, "SAX2 API Summary" for method summaries for these exception classes.

org.xml.sax.SAXException

This is the base exception class. Typically you will see its subclasses. These exceptions have messages and may wrap other exceptions for diagnostic purposes. When an application's event callback catches an exception it's not permitted to throw, it can wrap it in one of these exceptions and then throw that exception. Every SAX2 event callback can throw a SAXException, although most callback examples in this book won't demonstrate this.

org.xml.sax.SAXNotRecognizedException

This exception is thrown when the parser does not understand the URI identifying a feature or property you tried to access. Most processors recognize the standard IDs, so if you're trying to use those and you get this exception, make sure you're using the correct URI.

org.xml.sax.SAXNotSupportedException

These exceptions are typically used to indicate that an XMLReader property or feature value you tried to change was recognized, but the value you requested isn't supported. Reasons this might be reported include setting a property to an illegal value (such as the wrong type of handler) and trying to set a feature or property that is read-only in a given implementation (or when the request is made). For instance, it's not possible to ask a parser to stop validating in mid-parse, but for some parsers it's reasonable to do so before starting to parse a document.

org.xml.sax.SAXParseException

This is the most commonly seen exception class; instances provide detailed diagnostic information, such as the base URI of a file with bad XML content, and the line and column number of such content. XML parsers provide such exceptions when the report sends errors to ErrorHandler implementations.

Applications can also construct this information when reporting application-level errors through SAX callbacks. In fact, they probably should do so, providing a Locator object to the constructor (and perhaps wrapping an exception to identify a root cause) in order to provide good diagnostics. (See Section 4.1.2, "The Locator Interface " in Chapter 4, "Consuming SAX2 Events" for information about Locator objects.)

The "wrapped" exception is a powerful tool. You might be familiar with this mechanism from the new JDK 1.4 "Chained Exception" facility or the older java.lang.reflect.InvocationTargetException exception mechanism. (The JDK 1.4 getCause() method exposes essentially the same functionality as the SAX getException(), though it builds on new JVM features to add intelligence to exception printing.) While parsers may use it internally, you'll likely want to use it to ensure higher-level software will see the root cause of some SAXException your handler reported:

// in some SAX event handler:
try {
    ... application specific stuff ...
} catch (MyApplicationException cause) {
    throw new SAXException ("it broke!", cause);
    // or better yet: throw new SAXParseException
    //	 ("broke", locator, cause)
}

If you print the stack backtrace of such a SAXException, you'll see two stacks, starting with the root cause. Being able to see that root cause information can be a real lifesaver when debugging. And some application error recovery strategies will use the SAXException.getException() method to find the root cause and then determine how to recover from it. For example, if the application exception identified some resource that was unavailable, higher levels in the application might be able to use that information to choose an alternative resource and restart processing.

2.5.2. ErrorHandler Interface

Normally, you will configure SAX event-processing code to use a specialized implementation of ErrorHandler to process faults that are uncovered during parsing. This is done with the XMLReader.setErrorHandler() call. This interface has three methods; you saw one of them in an earlier example. The interface is used to encapsulate an error-handling strategy. The primary choices you have to make are whether to ignore an error or to abort parsing, and whether to emit diagnostics. Those strategies are driven by the severity of the problem, as exposed by which method is used to report it, though sometimes exception-typing may give programs information about exactly what error was detected.

void error (SAXParseException e)

This method is used to report errors that aren't expected to be fatal. The best-known example is violation of XML validity constraints, but some other XML errors are nonfatal too. Many kinds of application-level errors (as reported by event-consumer logic, not XML parsers) will fall into this category, and most parsers use this callback to report violations of namespace constraints (such as referring to an undeclared namespace prefix).

When validating, applications often adopt a policy of treating these errors as if they were fatal, or generating a diagnostic for every such error. By default, all nonfatal errors are ignored. That default will be a big surprise, if you expect a validating parser to stop parsing when it sees validation errors. You have to override the default error-handling policy if you want such behavior.

void fatalError (SAXParseException e)

This method is used to report errors, typically violations of well formedness, that are fatal. Some XML parsers may be able to continue processing after reporting such errors, but only to report additional errors. The XML specification itself requires that no more data will be reported after a fatal error.

By default, fatal errors cause parsing to stop; the parse() method will return. This method is often used to provide a diagnostic or to log the exception. After it does that, it has two main choices: throw the parameter to terminate processing or return. Most parsers will treat a return as equivalent to throwing the parameter to terminate parsing. Some XML parsers continue checking for errors; in such cases, they aren't allowed to call any handlers other than the ErrorHandler.

void warning (SAXParseException e)

This method is used to report problems that aren't errors. Such situations are specific to the software that reports the warning; unlike fatal and nonfatal errors, the XML specification doesn't place requirements on reporting such situations. XML infrastructure software may generate warnings for any reason at all (much like many pet dogs I have known) and yet be fully compliant with the XML specification.

By default, warnings are ignored. Applications typically ignore them, or print low-priority diagnostics. Because there is such variability in what generates a warning, it is probably not useful to put a "no warnings allowed" policy into software (by treating this like a fatal error); users have to decide on a warning-by-warning basis whether to ignore it or treat it as significant.

Event consumers can also use this API to provide a standard way to report faults uncovered in layers above pure XML, for instance, when data in element content or an attribute value is invalid or corrupt. When both the application and the SAX-related components use the same ErrorHandler instance to handle error-reporting policy issues, maintaining that policy is easier. For example, developers like being able to collect lots of error reports with one test run rather than getting only one error per run; it can be more effective to resolve problems in groups, with shorter test cycles. You can do that with SAX by saving the exceptions (or their associated diagnostics) as they're reported. The same flexibility can be important in production systems.

An ErrorHandler can throw any SAXException it wants; it doesn't have to be the SAXParseException passed as its argument. Don't throw a different exception unless you find a certifiably excellent reason to do so; to discard that original exception just makes problems become harder to troubleshoot. One such reason might be to report a "double fault," in which you triggered another exception while handling the first one. (Operating systems sometimes panic in such cases, so there's no reason applications shouldn't do so too!)

JAXP also uses this handler to report errors when building DOM documents; SAXException objects may be thrown to terminate parsing after a DOM parser finds a problem, if the application chooses to handle those errors. Most DOM implementations in Java use SAX parsers to populate their DOM tree, so this is natural behavior. (JAXP only specifies a SAX-compatible way to present and report such errors. They might be reported from a non-SAX parser.)

2.5.3. Errors and Diagnostics

When you see a SAXException, it'll normally have a message you'll use for diagnostics, like any exception. It'll also have stack backtrace, which will help when you're debugging, like any exception; in some cases you might even see a nested "root cause" exception. At this time, standard methods only tell an error's severity; there's no way to distinguish different validity errors from each other, for example.

You can get better diagnostics when the exception is really a SAXParseException, and give accurate information about exactly where the error appeared. SAX parsers normally provide such data when reporting parsing errors, and applications can do the same thing by avoiding the more generic SAXException. With non-GUI applications, I often use code like that shown in Example 2-6 to present the most important diagnostic data.

Example 2-6. Getting diagnostics from a SAXParseException

static private String printParseException (
    String              label,
    SAXParseException   e
) {
    StringBuffer        buf = new StringBuffer ();
    int                 temp;

    buf.append ("** ");
    buf.append (label);
    buf.append (": ");
    buf.append (e.getMessage ());
    buf.append ('\n');
    // most such exceptions include the (absolute) URI for the text
    if (e.getSystemId () != null) {
        buf.append ("   URI:  ");
        buf.append (e.getSystemId ());
        buf.append ('\n');
    }
    // many include approximate line and column numbers
    if ((temp = e.getLineNumber ()) != -1) {
        buf.append ("   line: ");
        buf.append (temp);
        buf.append ('\n');
    }
    if ((temp = e.getColumnNumber ()) != -1) {
        buf.append ("   char: ");
        buf.append (temp);
        buf.append ('\n');
    }
    // public ID might be available, but is seldom useful

    return buf.toString ();
}

It's natural to call such code in two places. One place is after you've caught an exception of this type, in a "try" block. That's a bit awkward and error prone; you'll need to have two different "catch" clauses, first for SAXParseException and then for SAXException, or else use a cast. The more natural place is centralized in an ErrorHandler that can treat generating diagnostics as one of several options for processing errors, as shown in Example 2-7. In fact, it's the only way to generate diagnostics for nonfatal errors, or for warnings, without treating them as fatal errors; or to centralize your error-handling policy to make it easily configurable.

Example 2-7. Customizable diagnostic error handler

public class MyErrorHandler implements ErrorHandler
{
    int         flags;

    // bit mask values for flags
    public static final int ERR_PRINT = 1;
    public static final int ERR_IGNORE = 2;
    public static final int WARN_PRINT = 4;
    public static final int FATAL_PRINT = 8;
    public static final int FATAL_IGNORE = 16;

    MyErrorHandler () { flags = ~0; }
    MyErrorHandler (int flags) { this.flags = flags; }

    public void error (SAXParseException e)
    throws SAXParseException
    {
    if ((flags & ERR_PRINT) != 0)
        System.err.print (printParseException ("Error", e));
    if ((flags & ERR_IGNORE) == 0)
        throw e;
    }

    public void fatalError (SAXParseException e)
    throws SAXParseException
    {
    if ((flags & FATAL_PRINT) != 0)
        System.err.print (printParseException ("FATAL", e));
    if ((flags & FATAL_IGNORE) == 0)
        throw e;
    }

    public void warning (SAXParseException e)
    throws SAXParseException
    {
    if ((flags & WARN_PRINT) != 0)
        System.err.print (printParseException ("Warning", e));
    // always ignored
    }

    // printParseException() method (above) is part of this class
}

Such an error handler gives you flexibility about which errors to report and how to handle the various types that show up. A silent mode of operation might never print diagnostics, a verbose one might print all of them, and a different default could be somewhere in between. A defensive operational mode might terminate XML processing when it sees any error; a permissive one might try to continue after every error. The default shown is verbose and permissive.

To use such an error handler for handling application-specific SAXExceptions, you'll need to adopt the same classifications that SAX derives from XML: fatal errors, nonfatal errors, and warnings. That's usually pretty natural, particularly if application configuration flags control which potential error cases are tested.

Previous | Home | Next
2.4. Producer-Side Validation | Book Index | 2.6. Namespaces and SAX2
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespaces and SAX2 (SAX2)

 SAX2Search this book

2.6. Namespaces and SAX2

However you use XML namespaces with SAX, you need to understand the core concepts discussed in this section. Namespaces can be confusing; they're more complex than perhaps they ought to be. In part this is because of how they interact (or don't interact) with other parts of Greater XML; in part it's because everyone has different ways to a determine what words mean, and XML names are kinds of words. We'll look at some of those complexities first, and then at the mechanisms SAX2 has to help you deal with them.

But first, just what are namespaces supposed to do? Usually, they identify some particular technical vocabulary. People often reuse words rather than create new ones, and they acquire context-specific meanings and nuances that can be extremely important. A namespace can distinguish whether a word like "bill" refers to part of a bird, a now-archaic weapon, part of a hat, legislative acts, or a number of other things. So a <bill length='45cm'/> element might be associated with a namespace, which provides context that should help applications interpret the element. A processor for "Birder's Markup Language" could know to reject (or ignore) markup intended for legislative or financial uses, even if they all use "bill" elements.

XML defines a way to declare namespaces as needed, using attributes. Namespaces are usually indicated by a prefix, which can serve as a qualifying adjective: "the bird's bill" might be bird:bill while "the consultant's bill" might be consultant:bill. You can also set up a default element namespace so that an unadorned bill element might indicate, for example, a weapon.

2.6.1. What Namespaces Do to XML

XML namespaces are a convention for using attributes to associate URIs with some element and attribute names. Since not all legal XML documents follow this convention, the XML Namespaces specification effectively specifies a dialect of XML. SAX2 supports both dialects: strict XML and XML plus namespaces. By default, SAX2 parsers expect the namespaces dialect. In most cases you'll be able to ignore the difference between those two XML dialects, since documents that use XML in namespace-incompatible ways aren't common.

Even apart from the two-dialects issue, the use of namespaces with XML complicates XML programming. There are two models for using element and attribute names in XML:

  • In one model, names have a single role (or "type," or "meaning") throughout the document. This is the model most XML DTDs use, except they allow different attributes to use the same name with different meanings if the attributes are attached to different element types. (But even for attributes, well-designed DTDs share attribute definitions between elements.) The startElement() callback parameters give you all the information you need, even when those names are "globalized" using namespace URIs.

  • In the other model, a name's role is dependent on context. For example, the same name used in two different enclosing elements might mean two different things. It gets confusing, just like names in the real world. This model is used with elements in some schema systems, such as local elements in W3C-style XML schemas.

If you're working with or designing XML structures with context-dependent names, then namespaces add new kinds of context and hence new ways to cause confusion. SAX2 gives you the tools to track all the context, but you'll have to record it yourself (probably with some kind of stack) since startElement() parameters will no longer give all the context you need.

There are also some conflicts between the element-naming approach of the XML Namespaces specifications and DTD validity as defined in the XML specification. They may not affect your SAX2 programs, but can affect the systems you're implementing with XML and SAX2. The issue is basically that DTDs expect everything to be declared once up front (like import statements in Java), while the namespace mechanism provides a lexical scoping mechanism (like declaring variables that live on the execution stack) that's flexible about what a given prefix indicates. You can make namespace-correct documents that are DTD-valid, but then you can't change the prefixes bound to namespaces.[11] Namespace-aware DTDs will often define default element namespaces for element names.

[11]If you want any flexibility in those prefixes, and have a deep understanding of how to use parameter entities, look at the approach to DTD modularization found in the XHTML 1.1 specification.

If you are designing a namespace and want to use the URI to publish information describing the namespace, rather than just use it as a unique identifier, then RDDL (http://www.rddl.org) is probably a good resource. RDDL defines an XHTML-based document syntax that can be viewed or mechanically processed. It lets you find some of the resources that might be important when working with the namespaces -- for example, different stylesheets and schemas and documentation in various languages. The RDDL web site includes SAX support for accessing this data.

2.6.2. Element and Attribute Naming with Namespaces

The direct impact of XML namespaces on your SAX2 application code is to give you a second way to identify elements and attributes. Documents will normally use only one identification style for a given element or attribute. These identification styles are distinct from the two models for using such names, described earlier:

Qualified names

These are exactly as found in the XML text. Examples include para and, with a prefix, xhtml:p. (XML documents that don't use namespaces, and some namespace-style documents won't use colons.)

Universal names

These consist of two separate strings: a "local name" from the XML text (removing any namespace prefix) and a "namespace name" (always a URI) from namespace declarations. For the qualified name xhtml:p, the local name is p, and the namespace name is the URI associated with the prefix xhtml, which is a function in the namespace declaration. Such names are in a sense "universalized" by addition of a suitable URI.

Note that the XML Namespaces specification only standardizes the "qualified name" (qName) terminology; it doesn't standardize terminology for universal names. Because of this, you will also see other terms, such as "expanded names" (the term used by XPath) or "namespace-style names" (used to talk about that style of naming).

Since ContentHandler.startElement() callbacks now have to deal with three different kinds of name strings, the code can get rather complicated. Plus, even if you're expecting only universal names, you'll need to notice when elements or attributes don't have universal names and use qualified names to work with them. Element names are identified in method parameters (the same as in ContentHandler.endElement()), while attribute names show up in accessor methods for Attributes objects. We'll use the following XML text to illustrate these different types of names:

<big:animals  xmlns="http://www.example.com/dog">
          xmlns:big="http://www.example.com/big">
    <wolfhound cat='no' big:dog='yes' />
    <greyhound big:dog='yes' xmlns=""/>
</big:animals>

SAX2 calls names in XML text "Qualified Names." These are the same thing as "XML 1.0 names" except that XML 1.0 names have no restrictions on the use of colons. When you disable namespace processing in a SAX2 parser, it will deliver "qualified names" that are really XML 1.0 names, without those restrictions. With namespace processing enabled, many qualified names (including every name with a prefix) will correspond to a namespace-style name.

Element names without a prefix might not have a corresponding universal name. Unprefixed attribute names will never have a universal name. In those cases, applications must use the qualified name along with non-namespace context, such as the enclosing element, to figure out what the name is supposed to mean. There are no universally accepted policies for such cases. Yes, all that confuses other people as well.

2.6.2.1. Element naming

The identifiers for the element names are the first three parameters of void startElement(String namespaceURI, String localName, String qName, Attributes atts). Table 2-1 shows the values of the element names for the previous example, as reported by a SAX2 parser in its default mode. Notice particularly that the namespace URI is empty except when a namespace declaration applies to that element name, and that if there's a nonempty namespace URI, there might not be a value for qName. That's not just for element names using namespace prefixes; for element names, a default element namespace declaration will apply if it's within scope. (Remember that empty strings aren't the same as nulls.)

Table 2-1. ContentHandler.startElement() parameters for element names

namespaceURI | localName | qName
http://www.example.com/big | animals | empty or big:animals
http://www.example.com/dog | wolfhound | empty or wolfhound
empty | empty | greyhound

You could end up with lots of code like this in your SAX event handlers. Or, you may prefer to factor it as a table lookup (maybe using application-specific types of handler objects) rather than as a tree of comparisons. Notice that for elements without a namespace URI, the qName is checked, but if there's a namespace URI, then localName is used. Also all unrecognized elements are reported as a kind of validity error. You may well need to have more context-dependent logic too, if elements may only show up in appropriate contexts. Such contexts often need different decision trees. See Example 2-8 for a decision tree for startElement().

Example 2-8. Decision tree in startElement( )

public void
startElement (String uri, String localName, String qName, Attributes atts)
throws SAXException
{
    // elements outside of any namespace?
    if ("".equals (uri)) {
    if ("greyhound".equals (qName)) {
        ... handle
        return;
    }
    ... else handle N other elements; return on success

    // no recognized element: a validity error
    errorHandler.error (new SAXParseException (
        "Unrecognized element: " + qName,
        locator
        ));
    // if that doesn't abort the parse:
    return;

    // in the "big" namespace?
    } else if ("http://www.example.com/big".equals (uri)) {
    if ("animals".equals (localName)) {
        ... handle
        return;
    }
    ... handle "islands" and N other big things; return on success
    // FALLTHROUGH for unrecognized elements

    // in the "dog" namespace?
    } else if ("http://www.example.com/dog".equals (uri)) {
    if ("wolfhound".equals (localName)) {
        ... handle
        return;
    }
    ... handle "terrier", "collie" and so on; return on success
    // FALLTHROUGH for unrecognized elements
    }

    ... and so on for other namespaces

    // element not in a namespace we recognize: a validity error
    errorHandler.error (new SAXParseException (
        "Unrecognized element: " + uri + " (" + localName + ")",
        locator
        ));
    // returns if that doesn't abort the parse
}

Most SAX2 parsers provide qualified names in all cases, but you shouldn't rely on their availablity unless the parser is configured to provide namespace prefix information (which also causes namespace-declaration attributes to be "un-hidden"). You should probably avoid using the qName, even for diagnostics, when there's a nonempty namespaceURI.

2.6.2.2. Attribute naming

The identifiers for the attribute names are accessed using Attributes methods such as getQName(), getLocalName(), and getURI() when you iterate over an element's attributes with a "for" loop. You can access attribute values directly if you use either XML 1.0-style names (qName) or XML Namespace-style names (namespaceURI and localName).

SAX2 parsers handle attribute names from the example text as shown in Table 2-2. This table shows the "mixed mode" behavior, described later; in the default SAX2 parser mode, the xmlns and xmlns:big attributes won't appear. You'd have to set the namespace-prefixes feature flag (as described later in this chapter, in Section 2.6.3, "Namespace Feature Flags") to see these attributes. Note that according to the namespaces specification there is no such thing as a default namespace for attribute names, so that namespace declaration attributes don't go into any namespace.

Table 2-2. Attributes methods to access attribute names

getURI() | getLocalName() | getQName()
empty | empty | xmlns
empty | empty | xmlns:big
empty | empty | cat
http://www.example.com/big | dog | empty or big:dog

So if you wanted to write some code that ignored elements without a big:dog attribute (that is, the URI is http://www.example.com/big/ and the local name is dog) with value "yes", it might look like this:

public void startElement (String uri, String local, String qName,
    Attributes atts)
throws SAXException
{
    String    value;

    value = atts.getValue ("http://www.example.com/big", "dog");
    if (!"yes".equals (value)) {
    // arrange to ignore text and elements until this finishes
    return;
    }

    ... process the element
}

2.6.2.3. Things to keep in mind

To avoid confusing things, the previous code didn't illustrate two somewhat perverse cases. First, if the big prefix were redefined for some element, the same qualified name could correspond to a different universal name, with the same local name but different namespace URIs. That's one reason the previous code doesn't check for a qName of big:dog. Using a qName of big:dog might make sense if you were working with XML 1.0 without using XML namespaces. Second, if the URI used with the big prefix were associated with a second prefix, different qualified names could correspond to the same universal names. That's another reason the previous code doesn't check for a qName of big:dog. If you are writing namespace-aware code, use only namespace-style name testing in your code to avoid such problems. That makes your code work correctly even when it deals with documents that use namespace declarations in ways you didn't expect.

By default, SAX2 XML parsers provide universal names for elements and attributes that have namespaces (they'll have nonempty localName and namespaceURI strings) or qualified names for elements and attributes that don't, and will remove the namespace declaration attributes from the Attributes object provided in the ContentHandler.startElement() event. Unless a default element namespace declaration is in scope, an element whose XML 1.0-style name has no prefix won't have a namespace-style identifier. Attributes with unprefixed names work differently, since default element namespace declarations never apply to attribute names.

If you work with both SAX2 and DOM Level 2, you need to be aware of the differences in how these APIs expose namespaces. The terminology is similar but not identical; SAX2 talks about "URI" while DOM Level 2 talks about "NamespaceURI," and SAX2 uses "QName" not "Name"; but both APIs talk about the "LocalName." When using element or attribute construction methods in the org.w3c.dom.Document class, you will notice that DOM uses two different APIs in places in which SAX2 provides just one callback (in three different modes, as discussed in the next section). You are most likely to trip over different ways to tell whether an element or attribute has no namespace URI: SAX2 uses an empty string (length zero), while DOM Level 2 uses a null string. You may also notice that while SAX2 follows the XML Namespaces specification with regards to the attributes that define namespaces, DOM does not. In SAX2, those attributes have no URIs, but DOM assigns http://www.w3.org/2000/xmlns/ as their namespace URI.

2.6.3. Namespace Feature Flags

SAX2 controls its namespace-processing support through two feature flags, which can be tested and changed using the setFeature() and getFeature() methods described earlier in this chapter in Section 2.4.1, "SAX2 Feature Flags". The two flags are http://xml.org/sax/features/namespaces (namespaces), which controls whether parsers handle namespace declarations, and http://xml.org/sax/features/namespace-prefixes (namespace-prefixes), which controls whether applications can see the underlying XML syntax. All SAX2 parsers support both flags, although their values might be read-only.

Given two flags, there are four possible combinations. Only three are legal. It's easiest to understand what the flags do by considering them as each controlling a small processing task layered over a core that just parses XML text. The SAX2 defaults are set so both tasks are performed.

XML 1.0 mode

Only XML 1.0-style names are reported for elements and attributes, using the qName. The namespaces flag is false, and the namespace-prefixes flag is true; those values are exactly the opposite of the SAX2 defaults.

This mode passes xmlns and xmlns:* attributes without looking at them. Namespace-style names (with URIs) might be provided with element or attribute names, but you must not rely on this; few parsers will do the extra work of processing the namespace declarations. If you enable this mode, your SAX2 parser will be doing what a SAX1 parser did, but the information will flow through APIs with slots for holding namespace-style names.

Mixed mode

Both XML 1.0- and XML plus Namespaces-style names are reported for elements and attributes. The namespaces flag is true (like the default SAX2 mode), and the namespace-prefixes flag is true (like XML 1.0 mode).

This mode is much like XML 1.0 mode, but setting the namespaces flag causes startPrefixMapping() and endPrefixMapping() events (discussed in the next section) to match xmlns and xmlns:* attributes, and processes those declarations so the parser always provides namespace URIs for element and attribute names when they're defined. The qName is always provided, even when a namespace URI is defined.

Parsers running in this mode should generate some kind of error report for legal XML 1.0 documents that don't meet all the rules of the "XML plus namespaces" dialect. (Most parsers use ErrorHandler.error() although the namespace specification doesn't say what class of error to report.) One example is to use colons in names for things that aren't elements or attributes, and not declare namespace prefixes. Similarly, you might get warnings about using relative URIs in namespace declarations. There is a performance impact to this additional processing, often five percent of the usually negligible overhead for XML parsing.

XML plus namespaces mode (SAX2 default)

The difference between this and mixed mode is that some information is discarded. The namespaces flag is true, and the namespace-prefixes flag is false.

Clearing the namespace-prefixes flag tells parsers they must filter out xmlns and xmlns:* attributes, and they may report empty strings instead of providing the qName (as found in the document) whenever a namespace URI is reported. In practice, most current SAX2 parsers always report qualified names, since there's little benefit to filtering them out.

The fourth combination of flags, disabling both namespace support and namespace prefix reporting, would be meaningless, and so it is an illegal parser state. Don't set this mode; parsers might not detect that you've put them into an illegal mode and may react unintelligently (such as by entering "XML 1.0 mode"). Unfortunately it's easy to set this mode if you just set the namespaces flag to false without first setting the namespaces-prefix flag to true (entering mixed mode).

I tend to prefer the mixed mode over the SAX2 default mode. Enabling it is simple: just set the namespaces-prefix flag to true, after setting up a parser for the SAX2 defaults. This mode provides better support for the XML Infoset, since it doesn't discard information about the prefixes. You won't see implementation-dependent behaviors in exposing either type of name. Certain kinds of XML processing will work better. In particular, algorithms working near the XML syntax level -- such as writing out XML text or performing consumer-side DTD validation -- will then work without needing to guard against discarded prefixes and without re-creating namespace declaration attributes. Discarding or changing prefixes, in particular, can cause confusion when people need to look at the XML output. The only real impact on applications is having to ignore xmlns and xmlns:* attributes, which isn't hard.

Few, if any, applications really need to work with documents that use colons in ways other than the XML namespaces specification, leaving a small performance impact as the primary reason to care about the pure XML 1.0 mode. Even applications that don't use namespaces usually won't see colons used in interesting ways (like nested:contexts:for:names). While most SAX2 XML parsers support all three of these modes, they are only required to support the SAX2 default mode.

2.6.4. ContentHandler and Prefix Mappings

Sometimes XML needs to handle "meta-level" processing, in which XML talks about XML. In such processing, namespace URIs are sometimes implicitly called by prefixes found in places no XML parser will look: CDATA attributes (which can contain anything) and character content found within elements. For example, XPath expressions include prefixes, and they are found in XSLT template attributes. The W3C XML Schema Datatypes (XSD) defines a QName datatype that formalizes such usage.

When you need to work with those types of XML text, you'll find two particular ContentHandler event callbacks helpful. They provide the same information found in xmlns and xmlns:* attributes, relieving your application code of the responsibility of correctly applying the XML Namespaces specification. For example, your code won't need to know how a default element namespace declaration can be explicitly undone by xmlns="" attributes or by ending the lexical scope of that attribute.

void startPrefixMapping(String prefix, String uri)

Each namespace declaration causes one of these calls. Each call corresponds to an attribute in the next startElement() callback to be made; you probably won't see other callbacks intervening. (This method has to appear before the element; the mapping will be used to interpret names of the element or its attributes.) If the prefix is the empty string, then the declaration is for the default element namespace. This is the only time the URI may be specified as the empty string (indicating that there is no longer a default element namespace in effect).

void endPrefixMapping(String prefix)

Each call to startPrefixMapping() is paired with a matching event to declare that the mapping has gone out of scope. These calls correspond to the most recent endElement() callback. However, the mapping "start" calls and the mapping "end" calls won't necessarily be perfectly nested. For example, two prefix mappings found in one element might be started in the order xlink then MyApp, but either mapping could end first.

You'd normally ignore these two calls, unless you use them to maintain some data structure that tracks active namespace prefixes. It would have to be a stacklike data structure, since one mapping for a prefix only temporarily hides a previous mapping for the same prefix. This is the notion of lexical scope, which you are familiar with from most programming languages. SAX2 includes a helper class to handle this for you: NamespaceSupport, discussed in Section 5.1.3, "The NamespaceSupport Class " in Chapter 5, "Other SAX Classes". Then when you parse the meta-level content, you can use those data structures to interpret prefix references and handle other namespace-related work.

Previous | Home | Next
2.5. Exception Handling | Book Index | 3. Producing SAX2 Events
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Producing SAX2 Events (SAX2)

 SAX2Search this book

Chapter 3. Producing SAX2 Events

Contents:

Pull Mode Event Production with XMLReader
Bootstrapping an XMLReader
Configuring XMLReader Behavior
The EntityResolver Interface
Other Kinds of SAX2 Event Producers

The preceding chapter provided an overview of the most widely used SAX classes and showed how they relate to each other. This chapter drills more deeply into how to produce XML events with SAX, including further customization of SAX parsers.

3.1. Pull Mode Event Production with XMLReader

Most of the time you work with SAX2, you'll be using some kind of org.xml.sax.XMLReader implementation that turns XML text into a SAX event stream. Such a class is loosely called a "SAX parser." Don't confuse this with the older SAX1 org.xml.sax.Parser class. New code should not be using that class!

This interface works in a kind of "pull" mode: when a thread makes an XMLReader.parse() request, it blocks until the XML document has been fully read and processed. Inside the parser there's a lot of work going on, including a "pull-to-push" adapter: the parser pulls data out of the input source provided to parse() and converts it to events that it pushes to event consumers. This model is different from the model of a java.io.Reader, from which applications can only get individual buffers of character data, but it's also similar because in both cases the calling thread is pulling data from a stream.

You can also have pure "push" mode event producers. The most common kind writes events directly to event handlers and doesn't use any kind of input abstraction to indicate the data's source; it's not parsing XML text. We discuss several types of such producers later in this chapter. Using threads, you could also create a producer that lets you write raw XML text, a buffer at a time, to an XMLReader that parses the text; that's another kind of "push" mode producer.

3.1.1. The XMLReader Interface

The SAX overview presented the most important parts of the XMLReader interface. Here we discuss the whole thing, in functional groups. Most of the handlers are presented in more detail in the next chapter, which focuses on the consumption side of the SAX event streaming process. Each handler has get and set accessor methods, and has a default value of null.

XMLReader has the following functional groups:

void parse(String uri)
void parse(InputSource in)

There are two methods to parse documents. In most cases, the Java environment is able to resolve the document's URI; the form with the absolute URI should be used when possible. (You may need to convert filenames to URIs before passing them to SAX. SAX specifically disallows passing relative URIs.) The second form is discussed in more detail along with the InputSource class. Both of these methods can throw a SAXException or java.io.IOException, as presented earlier. A SAXException is normally thrown only when an event handler throws it to terminate parsing. That policy is best encapsulated in an ErrorHandler, but handler methods can make such decisions themselves.

Only one thread may call a given parser's parse() method at a time; applications are responsible for ensuring that threads don't share parsers that are in active use. (SAX parsers aren't necessarily going to report applications that break that rule, though!) The thread doing the parsing will normally block only while it's waiting for data to be delivered to it, or if a handler's processing causes it to block.

void setContentHandler(ContentHandler handler)
ContentHandler getContentHandler()

Key parts of the ContentHandler interface were presented as part of the SAX overview; ContentHandler packages the fundamental parsing callbacks used by SAX event consumers. This interface is presented in more detail in Chapter 4, "Consuming SAX2 Events", in Section 4.1.1, "Other ContentHandler Methods ".

void setDTDHandler(DTDHandler handler)
DTDHandler getDTDHandler()

The DTDHandler is presented in detail later, in Chapter 4, "Consuming SAX2 Events" in Section 4.3.2, "The DTDHandler Interface ".

void setEntityResolver(EntityResolver handler)
EntityResolver getEntityResolver()

The EntityResolver is presented later in this chapter, in Section 3.4, "The EntityResolver Interface". It is used by the parser to help locate the content for external entities (general or parameter) to be parsed.

void setErrorHandler(ErrorHandler handler)
ErrorHandler getErrorHandler()

The ErrorHandler was presented in Section 2.5.2, "ErrorHandler Interface" in Chapter 2, "Introducing SAX2". It is often used by consumer code that interprets events reported through other handlers, since they may need to report errors detected at higher levels than XML syntax.

void setFeature(String uri, boolean value)
boolean getFeature(String uri)

Parser feature flags were discussed in Chapter 2, "Introducing SAX2", and are presented in more detail later in this chapter in Section 3.3.2, "XMLReader Feature Flags".

void setProperty(String uri, Object value)
Object getProperty(String uri)

Parser properties are used for data such as additional event handlers, and are presented in more detail later in this chapter in Section 3.3.1, "XMLReader Properties".

All the event handlers and the entity resolver may be reassigned inside event callbacks. At this level, SAX guarantees "late binding" of handlers. Layers built on top of SAX might use earlier binding, which can optimize event processing.

Many SAX parsers let you set handlers to null as a way to ignore the events reported by that type of handler. Strictly speaking, they don't need to do that; they're allowed to throw a NullPointerException when you use null. So if you need to restore the default behavior of a parser, you should use a DefaultHandler (or something implementing the appropriate extension interface) just in case, rather than use the more natural idiom of setting the handler to its default value, null.

If for any reason you need a push mode XML parser, which takes blocks of character or byte data (encapsulating XML text) that you write to a parser, you can easily create one from a standard pull mode parser. The cost is one helper thread and some API glue. The helper thread will call parse() on an InputSource that uses a java.io.PipedInputStream to read text. The push thread will write such data blocks to an associated java.io.PipedOutputStream when it becomes available. Most SAX parsers will in turn push the event data out incrementally, but there's no guarantee (at least from SAX) that they won't buffer megabytes of data before they start to parse.

3.1.2. The InputSource Class

The InputSource class shows up in both places where SAX needs to parse data: for the document itself, through parse(), and for the external parsed entities it might reference through the EntityResolver interface.

In almost all cases you should simply pass an absolute URI to the XMLReader.parse() method. (If you have a relative URI or a filename, turn it into an absolute URI first.) However, there are cases when you may need to parse data that has no URI. It might be in unnamed storage like a String; or it might need to be read using a specialized access scheme (maybe a java.io.PipedInputStream, or POST input to a servlet, or something named by a URN). The web server for the URI might misidentify the document's character encoding, so you'd need to work around that server bug. In such cases, you must use the alternative XMLReader.parse() method and pass an InputSource object to the parser.

InputSource objects are fundamentally holders for one or two things: an entity's URI and the entity text. (There can be a "public ID" too, but it's rarely useful.) When only one of those is needed, an application's work for setting up the InputSource might end with choosing the right constructor. Whenever you provide the entity text, you need to pay attention to some character encoding issues. Because character encoding is easy to get wrong, avoid directly providing entity text when you can.

3.1.2.1. Always provide absolute URIs

You should try to always provide the fully qualified (absolute) URI of the entity as its systemId, even if you also provide the entity text. That URI will often be the only data you need to provide. You must convert filenames to URIs (as described later in this chapter in Section 3.1.3, "Filenames Versus URIs"), and turn relative URIs into absolute ones. Some parsers have bugs and will attempt to turn relative URIs into absolute ones, guessing at an appropriate base URI. Do not rely on such behavior.

If you don't provide that absolute URI, then diagnostics may be useless. More significantly, relative URIs within the document can't be correctly resolved by the parser if the base URI is forgotten. XML parsers need to handle relative URIs within DTDs. To do that they need the absolute document (or entity) base URIs to be provided in InputSource (or parse() methods) by the application. Parsers use those base URIs to absolutize relative URIs, and then use EntityResolver to map the URIs (or their public identifiers) to entity text. Applications sometimes need to do similar things to relative URIs in document content. The xml:base attribute may provide an alternative solution for applications to determine the base URI, but it is normally needed only when relative URIs are broken. This can happen when someone moves the base document without moving its associated resources, or when you send the document through DOM (which doesn't record base URIs). Moreover, relative URIs in an xml:base attribute still need to be resolved with respect to the real base URI of the document.

The following methods are used to provide absolute URIs:

InputSource(String uri)

Use this constructor when you are creating an InputSource consisting only of a fully qualified URI in a scheme understood by the JVM you are using. Such schemes commonly include http://, file://, ftp://, and increasingly https://.

InputSource.setSystemId(String uri)

Use this method to record the URI associated with text you are providing directly.

For example, these three ways to parse a document are precisely equivalent:

String    uri = ...;
XMLReader parser = ...;

parser.parse (uri);
// or
parser.parse (new InputSource (uri);

3.1.2.2. Providing entity text

For data without a URI, or that uses a URI scheme not supported by your JVM, applications must provide entity text themselves. There are two ways to provide the text through an InputSource: as character data or as binary data, which needs to be decoded into character data before it can be parsed. In both cases your application will create an open data stream and give it to the parser. It will no longer be owned by your application; the parser should later close it as part of its end-of-input processing. If you provide binary data, you might know the character encoding used with it and can give that information to the parser rather than turning it to character data yourself using something like an InputStreamReader.

InputSource(java.io.Reader in)

Use this constructor when you are providing predecoded data to the parser, which will then ignore what any XML or text declaration says about the character encoding. (Also, call setSystemId(uri) when possible.) This constructor is useful for parsing data from a java.io.Reader such as java.io.CharArrayReader and for working around configuration bugs in HTTP servers.

Some HTTP servers will misidentify the text encoding used for XML documents, using the content type text/xml for non-ASCII data, instead of text/xml;charset=... or application/xml.[12] If you know a particular server does this, and that the encoding won't be autodetected, create an InputSource by using an InputStreamReader that uses the correct encoding. If the correct encoding will be autodetectable, you can use the InputStream constructor.

[12]application/xml is the safest MIME type to use for *.xml, *.dtd, and other XML files. See RFC 3023 for information about XML MIME types and character encodings.

InputSource(java.io.InputStream in)

Use this constructor when you are providing binary data to a parser and expect the parser to be able to detect the encoding from the binary data. (Also, call setSystemId(uri) when possible.)

For example, UTF-16 text always includes a Byte Order Mark, a document beginning <?xml ... encoding="Big5"?> is understood by most parsers as a Big5 (traditional Chinese) document, and UTF-8 is the default for XML documents without a declaration identifying the actual encoding in use.

InputSource.setEncoding(String id)

Use this method if you know the character encoding used with data you are providing as a java.io.InputStream. (Or provide a java.io.Reader if you can, though some parsers know more about encodings than the underlying JVM does.)[13] If you don't know the encoding, don't guess. XML parsers know how to use XML and text declarations to correctly determine the encoding in use. However, some parsers don't autodetect EBCDIC encodings, which are mostly used with IBM mainframes. You can use this method to help parsers handle documents using such encodings, if you can't provide the document in a fully interoperable encoding such as UTF-8.

[13]JDK 1.4 includes public APIs through which applications can support new character encodings. Some applications may need to use those APIs to support encodings beyond those the JVM handles natively.

All XML parsers support "UTF-8" and "UTF-16" values here, and most support other values, such as US-ASCII and ISO-8859-1. Consult your parser documentation for information about other encodings it supports. Typically, all encodings supported by the underlying JVM will be available, but they might be inconsistently named. (As one example, Sun's JDK supports many EBCDIC encodings, but gives them unusual names that don't suggest they're actually EBCDIC.) You should use standard Internet (IANA) encoding names, rather than Java names, where possible. In particular, don't use the name "UTF8"; use "UTF-8".

So if you want to parse some XML text you have lying around in a character array or String, the natural thing to do is package it as a java.io.Reader and wrap it up in something like this:

String    text = "<lichen color='red'/>";
Reader    reader = new StringReader (text);
XMLReader parser = ... ;

parser.setContentHandler (...);
parser.parse (new InputSource (reader));

In the same way, if you're implementing a servlet's POST handler and the servlet accepts XML text as its input, you'll create an InputSource. The InputSource will never have a URI, though you could support URIs for multipart/related content (sending a bundle of related components, such as external entities). Example 3-1 handles the MIME content type correctly, though it does so by waving a magic wand: it calls a routine that implements the rules in RFC 3023. That is, text/* content is US-ASCII (seven-bit code) by default, and any charset=... attribute is authoritative. When parsing XML requests inside a servlet, you'd typically apply a number of configuration techniques to speed up per-request processing and maintain security.[14]

[14]You might have a pool of parsers, to reduce bootstrap costs. You'd use an entity resolver to turn most entity accesses from remote ones into local ones. Depending on your application, you might even prevent all access to nonlocal entities so the servlet won't hang when remote network accesses get delayed.

Some security policies would also involve the entity resolver. Basically, every entity access "requested" by the client (through a reference in the document) is a potential attack. If it's not known to be safe (for example, access to standard DTD components), it may be important to prevent or nullify the access. (This does not always happen in the entity resolver; sometimes system security policies will be more centralized.) In a small trade-off against performance, security might require that the request data always be validated, and that validity errors be treated as fatal, because malformed input data is likely to affect system integrity.

Example 3-1. Parsing POST input to an HTTP Servlet

import gnu.xml.util.Resolver;

public void doPost (HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
    String       type = req.getContentType ();
    InputSource  in;
    XMLReader    parser;

    if (!(type.startsWith ("text/xml")
            || type.startsWith ("application/xml")) {
        response.sendError (response.SC_UNSUPPORTED_MEDIA_TYPE,
            "non-XML content type: " + type);
        return;
    }

    // there's no URI for this input data!
    in = new InputSource (req.getInputStream ());

    // use any encoding associated with the MIME type
    in.setEncoding (Resolver.getEncoding (req.getContentType ()));

    try {
        parser = XMLReaderFactory.createXMLReader();
        ...
        parser.setContentHandler (...);
        parser.parse (in);
        // content handler expected to handle response generation

    } catch (SAXException e) {
        response.sendError (response.SC_BAD_REQUEST,
            "bad input: " + e.getMessage ());
        return;

    } catch (IOException e) {
    // maybe a relative URI in the input couldn't be resolved
        response.sendError (response.SC_INTERNAL_SERVER_ERROR
            "i/o problem: " + e.getMessage ());
        return;
    }
}

You might have some XML text in a database, stored as a binary large object (BLOB, accessed using java.sql.Blob) and potentially referring to other BLOBs in the database. Constructing input sources for such data should be slightly different because of those references. You'd want to be sure to provide a URI, so the references can be resolved:

String        key = "42";
byte          data [] = Storage.keyToBlob (key);
InputStream   stream = new ByteArrayInputStream (data);
InputSource   source = new InputSource (stream);
XMLReader     parser = ... ;

source.setSystemId ("blob:" + key);
parser.parse (source);

In such cases, where you are using a URI scheme that your JVM doesn't support directly, consider using an EntityResolver to create the InputSource objects you hand to parse(). Such schemes might be standard (such as members of a MIME multipart/related bundle), or they might be private to your application (like this blob: scheme). (Example 3-3 shows how to package handling for such nonstandard URI schemes so that you can use them in your application, even when your JVM does not understand them. You may need to pass such URIs using public IDs rather than system IDs, so that parsers won't report errors when they try to resolve them.)

3.1.3. Filenames Versus URIs

Filenames are not URIs, so you may not provide them as system identifiers where SAX expects a system identifier: in parse() or in an InputSource object. If you are depending on JDK 1.2 or later, you can rely on new File(name).toURL().toString() to turn a filename into a URI. To be most portable, you may prefer to use a routine as shown in Example 3-2, which handles key issues like mapping DOS or Mac OS filenames into legal URIs.

Example 3-2. File.toURL() analogue for JDK 1.1

public static String fileToURL (File f)
throws IOException
{
    String      temp;

    if (!f.exists ())
        throw new IOException ("no such file: " + f.getName ());

    temp = f.getAbsolutePath ();

    if (File.separatorChar != '/')
        temp = temp.replace (File.separatorChar, '/');
    if (!temp.startsWith ("/"))
        temp = "/" + temp;
    if (!temp.endsWith ("/") && f.isDirectory ())
        temp = temp + "/";
    return "file:" + temp;
}

If you're using the GNU software distribution that is described earlier, gnu.xml.util.Resolver.fileToURL() is available so you won't need to enter that code yourself.

Previous | Home | Next
2.6. Namespaces and SAX2 | Book Index | 3.2. Bootstrapping an XMLReader
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Bootstrapping an XMLReader (SAX2)

 SAX2Search this book

3.2. Bootstrapping an XMLReader

There are several ways to obtain an XMLReader. Here we'll look at a few of them, focusing first on the most commonly available ones. These are the "pure SAX" solutions.

It's good policy to reuse parsers, rather than constantly discard and recreate them. Some parsers are more expensive to create than others, so such reuse can improve performance if you parse many documents. Similarly, factory approaches add some fixed costs to achieve vendor neutrality, and those costs can add up. In contexts like servlets, where any number of threads may need to parse XML concurrently, parsers are often pooled so those bootstrapping costs won't increase per-request service times.

3.2.1. The XMLReaderFactory Class

The simplest way to get a parser is to use the default parser for your environment, as we saw earlier:

import org.xml.sax.helpers.XMLReaderFactory;

...

XMLReader       parser = null;

try {
    parser = XMLReaderFactory.createXMLReader ();
    // success!

} catch (SAXException e) {
    System.err.println ("Can't get default parser: " + e.getMessage ());
}

Normally, the default parser is defined by setting the org.xml.sax.driver system property. Application startup should set that property, normally using JVM invocation flags. (In a very few cases System.setProperty() may be appropriate.)

$ java -Dorg.xml.sax.driver=gnu.xml.aelfred2.XMLReader

Unfortunately, in many cases the original reference implementation of that method is used. This is problematic in two situations: when the system property isn't set and when security permissions are set to prevent access to that system property; this is common for many applets. Good SAX2 distributions will ensure that this factory method succeeds in the face of such errors. The current release of the SAX2 helper classes makes this easy to do.[15]

[15]The current version of XMLReaderFactory has more intelligence and supports additional configuration mechanisms. For example, your application or parser distribution can configure a META-INF/services/org.xml.sax.driver resource into its class path, holding a single string to be used if the system property hasn't been set. SAX2 parser distributions are expected to work even if the system property or class path resource hasn't been set.

Because of that problem, you may choose to code your application so parser choice is a configuration option encoded through some other mechanism than system properties. You can't keep it in your application's XML-format configuration file. Once you get that configuration data you'll probably use a different XMLReaderFactory call:

import org.xml.sax.helpers.XMLReaderFactory;

...

XMLReader       parser = null;
String          className = ...;

try {
    parser = XMLReaderFactory.createXMLReader (className);
    // success!

    } catch (SAXException e) {
    System.err.println ("Can't get default parser: " + e.getMessage ());
}

Using this factory call, the class name identifies the SAX parser you want to use. It may well be one of the entries in Table 3-1, though some frameworks bundle other parsers.

Table 3-1. SAX2 XMLReader implementation classes

Parser (and type) | Class name
Ælfred (nonvalidating) | gnu.xml.aelfred2.SAXDriver
Ælfred (optionally validating) | gnu.xml.aelfred2.XmlReader
Crimson (optionally validating) | org.apache.crimson.XmlReaderImpl
Xerces (optionally validating) | org.apache.xerces.parsers.SAXParser

If you're using a parser without a settable option for validation, you may want to let distinct parsers be configured for validating and nonvalidating usage, assuming that your application needs both. Parsers with validation support are significantly larger than ones without it, which is partly whyÆlfred still has a nonvalidating class.

3.2.2. Calling Parser Constructors

If you need to force the use of some particular parser, you can invoke its constructor directly. Every SAX2 XMLReader must have a default constructor in order to work with the XMLReaderFactory class. Since it exists, you can invoke it directly using the same class names you may have passed to the XMLReaderFactory, if you used application-level configuration:

import org.xml.sax.XMLReader;
import gnu.xml.aelfred2.XmlReader;

...

XMLReader       parser = new XmlReader ();

In some cases you may actually prefer to force use of some particular parser. In other cases, you may have no option, maybe because of class loader or security configuration. If you run into trouble with those mechanisms, you may not be able to use factory APIs to access parsers unless they are visible through the system class loader.

In general, avoid such nonportable coding decisions; use a factory API wherever you can.

3.2.3. Using JAXP

Sun's JAXP 1.1 supports yet another way to bootstrap SAX parsers. It's a more complex process, taking several steps instead of just one:

  1. First, get a javax.xml.parsers.SAXParserFactory.

  2. Tell it to return parsers that will do the kind of processing needed by your application.

  3. Ask it to give you a JAXP parser of type javax.xml.parsers.SAXParser.

  4. Finally, ask the JAXP parser to give you the XMLReader that is normally lurking inside of it.

Conceptually this is like the no-parameters XMLReaderFactory.createXMLReader() method, except it's complicated by expecting the factory to return preconfigured parsers.[16] Configuring the parser using the SAX2 flags and properties directly is preferable; the API "surface area" is smaller. Other than having different default namespace-processing modes, the practical difference is primarily availability: many implementations ensure that a JAXP system default is always accessible, but they haven't paid the same attention to providing the default SAX2 parser. (Current versions of the SAX2 classes make that easier, but you might not be using such versions.)

[16]You can also look at this as choosing between parsers. For example, JAXP 1.2 will probably say how to request that schema validation be done. That's most naturally done as a layer on top of SAX, with a parser filter postprocessing the output of some other SAX parser.

The code to use the JAXP bootstrap API to get a SAX2 parser looks like this:

import org.xml.sax.*;
import javax.xml.parsers.*;

XMLReader        parser;

try {
    SAXParserFactory factory;

    factory = SAXParserFactory.newInstance ();
    factory.setNamespaceAware (true);
    parser = factory.newSAXParser ().getXMLReader ();
    // success!

} catch (FactoryConfigurationError err) {
    System.err.println ("can't create JAXP SAXParserFactory, "
    + err.getMessage ());
} catch (ParserConfigurationException err) {
    System.err.println ("can't create XMLReader with namespaces, "
    + err.getMessage ());
} catch (SAXException err) {
    System.err.println ("Hmm, SAXException, " + err.getMessage ());
}

Rather than calling newInstance(), you can hardcode the constructor for a particular factory, probably using one of the classes listed in Table 3-2. It's better to keep implementation preferences as configuration issues though, and not hardwire them into source code. For situations where you may have several parsers in your class path (or a tree of class loaders, as found in many recent servlet engines), JAXP offers several methods to configure such preferences. You can associate the factory class name value with the key javax.xml.parsers.SAXParserFactory by using the key to name a system property (which sets the default parser for your JVM instance) or by putting it in the $JAVA_HOME/jre/lib/jaxp.properties property file (which sets the default policy for that JVM implementation). I prefer the jaxp.properties solution; with the other method the default parser is a function of your class path settings and even the names assigned to various JAR files. You can also embed this preference in your application's JAR files as a META-INF/services/... file, but that solution is similarly sensitive to class loader configuration issues.

Table 3-2. JAXP SAXParserFactory implementation classes

JAXP factory | Class name
Ælfred | gnu.xml.aelfred2.JAXPFactory
Crimson | org.apache.crimson.jaxp.SAXParserFactoryImpl
Xerces | org.apache.xerces.jaxp.SAXParserFactoryImpl

If you're using JAXP to bootstrap a SAX2 parser, rather than the SAX2 APIs, the default setting for namespace processing is different: JAXP parsers don't process namespaces by default, while SAX2 parsers do. SAX2 normally removes all xmlns* attributes, reports namespace scope events, and may hide the namespace prefixes actually used by element and attribute names. JAXP does none of that unless you make it; in fact, the default parser mode for some current implementations is the illegal SAX2 mode described in the previous chapter. The example code in this section made the JAXP factory follow SAX2 defaults.

This book encourages you to use SAX2 directly, rather than through the JAXP factory mechanism. Even if JAXP is available, it's more complex to use. Also, the resulting parser is configured differently, so many of the examples in this book would break.

Previous | Home | Next
3. Producing SAX2 Events | Book Index | 3.3. Configuring XMLReader Behavior
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Configuring XMLReader Behavior (SAX2)

 SAX2Search this book

3.3. Configuring XMLReader Behavior

A configuration mechanism was one of the key features added in the SAX2 release. Parsers can support extensible sets of named Boolean feature flags and property objects. These function in similar ways, including using URIs to identify any number of features and properties. The exception model, presented in Chapter 2, "Introducing SAX2" in Section 2.4.1, "SAX2 Feature Flags" is used to distinguish the three basic types of feature or property: the current value may be read-only, read/write, or undefined. Some flags and properties may have rules about when they can be changed (typically not while parsing) or read.

Applications access property objects and feature flags through get*() and set*() methods and use URIs to identify the characteristic of interest. Since SAX does not provide a way to enumerate such URIs as supported by a parser, you will need to rely on parser documentation, or the tables in this section, to identify the legal identifiers. (Or consult the source code, if you have access to it.)

If you happen to be defining new handlers or features using the SAX2 framework, you don't have to ask for permission to define new property or feature flag IDs. Since they are identified using URIs, just start your ID with a base URI that you control. (Only the SAX maintainers would start with the http://xml.org/sax/ URI, for example.) Typically, it will be easiest to make up some HTTP URL based on a fully qualified domain name that you control. As with namespace URIs, these are used purely as identifiers rather than as locations from which data would be retrieved. (The "I" in URI stands for "identifier.")

3.3.1. XMLReader Properties

SAX2 defines two XMLReader calls for accessing named property objects. One of the most common uses for such objects is to install non-core event handlers. Accessing properties is like accessing feature flags, except that the values associated with these names are objects rather than Booleans:

XMLReader       producer ...;
String          uri = ...;
Object          value = ...;

// Try getting and setting the property
try {
    System.out.println ("Initial property setting: "
    + producer.getProperty (uri);
    // if we get here, the property is supported

    producer.setProperty (uri, value);
    // if we get here, the parser set the property

} catch (SAXNotSupportedException e) {
    // bad value for property ... maybe wrong type, or parser state
    System.out.println ("Can't set property: "
    + e.getMessage ());
    System.exit (1);

} catch (SAXNotRecognizedException e) {
    // property not supported by this parser
    System.out.println ("Doesn't understand property: "
    + e.getMessage ());
    System.exit (1);
}

You'll notice the URIs for these standard properties happen to have a common prefix. This means that you can declare the prefix (http://xml.org/sax/properties/) as a constant string and construct the identifiers by string catenation.

Here are the standard properties:

http://xml.org/sax/properties/declaration-handler

This property holds an implementation of org.xml.sax.ext.DeclHandler, used for reporting the DTD declarations that aren't reported through org.xml.sax.DTDHandler callbacks or for the root element name declaration, org.xml.sax.ext.LexicalHandler callbacks. This handler is presented in Section 4.3.1, "The DeclHandler Interface ".

Ælfred, Crimson, and Xerces support this property. In fact, all JAXP-compliant processors must do so.

http://xml.org/sax/properties/dom-node

Only specialized parsers will support this property: parsers that traverse DOM document nodes to produce streams of corresponding SAX events. (Typical SAX2 parsers parse XML text instead of DOM content.) When read, this property returns the DOM node corresponding to the current SAX2 callback. The property can only be written before a parse, to specify that the DOM node beginning and ending the SAX event stream need not be a org.w3c.dom.Document. This type of parser is presented later in this chapter, in Section 3.5.1, "DOM-to-SAX Event Production (and DOM4J, JDOM)".

One example of such a parser is gnu.xml.util.DomParser, which is currently packaged along with the Ælfred parser. At this time, neither Crimson nor Xerces include such functionality.

http://xml.org/sax/properties/lexical-handler

This property holds an implementation of org.xml.sax.ext.LexicalHandler, used for reporting various events mostly (but not exclusively) relating to details of XML text that have no semantic or structural meaning, such as comments. This handler is presented in Chapter 4, "Consuming SAX2 Events" in Section 4.2, "The LexicalHandler Interface ".

Ælfred, Crimson, and Xerces support this property. In fact, all JAXP-compliant processors must do so.

http://xml.org/sax/properties/xml-string

This property returns a literal string of characters associated with the current parser callback event. Exactly which characters are returned isn't specified by SAX2. An example would be returning all the characters in the start tag of an element, including unexpanded entity and character references as well as excess whitespace and the exact type of quote characters (single, double) used to delimit attribute values. (This feature is intended to be of use when constructing certain kinds of XML editors, or DTD analyzers, that are willing to re-parse this data.)

No widely available open source SAX2 parser currently supports this property.

Applications may find it useful to define their own types of handler interfaces, assembling sequences of SAX event "atoms" into higher-level event "molecules" that incorporate essential application-level semantics (and probably some procedural validation). This is the same kind of process model used by W3C's XML schema processing model: the Post-Schema-Validation Infoset (PSVI) additions incorporate semantics suited to processing with that kind of schema. Most applications need to associate even more semantics with data than are easily captured by such simple rules (including DTDs and all types of schema). Those semantics would likely not be understood by any common XMLReader, but other kinds of SAX processing components can help manage such application-level handlers. You can see an example of this technique in Example 6-3.

3.3.2. XMLReader Feature Flags

The previous chapter showed how to access feature flags from SAX parsers and used the standard validation flag as the primary example. Accessing feature flags follows the same model as accessing properties, except the values are boolean not Object. There are a handful of standard SAX2 feature flags, which are all you normally need. The namespace for features is different from the namespace for properties. You can't set a property to a java.lang.Boolean value and expect to have the same effect as setting the feature flag that happens to use the same identifier.

As with properties, the URIs for these standard feature flags happen to have a common prefix: http://xml.org/sax/features/. It's good programming practice to declare the prefix as a constant and construct these feature identifiers by string catenation, helping reduce errors. Also, remember that flags aren't necessarily either settable (read/write)[17] or readable (supported); some parsers won't recognize all these flags, and in some cases these flags expose parser behaviors that don't change.

[17]SAX could support write-only flags too, but these are rarely a good idea.

The standard flags are as follows:

http://xml.org/sax/features/external-general-entities

The default value for this flag is parser-specific. When the parser is validating, and in most other cases, the flag is true, indicating that the parser reads all external entities used outside the DTD. When the flag is false, the XML parser won't expand references to external general entities, so applications won't see the entire body of documents using such entities. This value can't be changed during parsing.

Crimson and Xerces only support true for this property. (For such parsers, you can get most of the effect of setting this flag to false by using an EntityResolver that returns zero-length entities after the first startElement() event.)Ælfred supports changing the value of this property.

http://xml.org/sax/features/external-parameter-entities

The default value for this flag is parser-specific. When the parser is validating, and in most other cases, the flag is true, indicating the DTD will be completely processed. When the flag is false, the XML parser will skip any external DTD subset, as well as named external parameter entities, so it won't necessarily read the entire DTD for a document. This value can't be changed during parsing.

Skipping these entities means attributes declared in them will not be defaulted or normalized as expected, and their types won't be known. As a result, default namespace declarations may get dropped. Parts of the internal subset after a reference to a skipped external parameter entity will be ignored. It also means some general entities might not be declared, making it impossible to correctly distinguish whether references to undefined entities are well-formedness errors.

Normally, you are better off providing an entity resolver that accesses locally cached copies of your DTD components, or not using DTDs, rather than disabling processing of external parameter entities. But don't assume all the XML you work with will have these DTD entities processed; the XML processors in some web browsers will not read these entities by default.

Xerces and Crimson only support true for this property. (For such parsers, you can get an effect similar to setting this to false by using an EntityResolver that returns zero-length entities before the first startElement() event. The parser won't correctly ignore declarations found later in the DTD.) Ælfred supports changing the value of this property.

http://xml.org/sax/features/is-standalone/

This feature flag derives its value from the document being parsed, so it is read-only and only available after the first part of the document has been parsed. When the flag is true, the document has been declared to be standalone. If that declaration is correct, then all external entities may be safely ignored. This feature is part of XML 1.0 and is intended to reduce the cost of parsing some documents.

This flag should be part of an upcoming SAX extensions release.

http://xml.org/sax/features/lexical-handler/parameter-entities

The default value for this flag is parser-specific and is implicitly false if the parser doesn't support the LexicalHandler through a parser property. When the flag is true, the parser will report the beginning and end of parameter entities through LexicalHandler calls. (Skipped parameter entities are always reported, through the appropriate ContentHandler call.) Parameter entities are distinguished from general entities because the first character of their entity name will be a percent sign (%). The value can't be changed during parsing.

Currently, only theÆlfred parser reports parameter entities.

http://xml.org/sax/features/namespaces

This flag defaults to true in XML parsers, which indicates the parser performs namespace processing, reporting xmlns attributes by startPrefixMapping() and endPrefixMapping() calls and providing namespace URIs for each element or attribute. Otherwise no such processing is done at the parser level. This can't be changed during parsing.

You will leave flag this at its default setting unless your XML documents aren't guaranteed to conform to the XML Namespaces specification. Setting this to false usually gives some degree of parsing speed improvement, although it will likely not provide a significant impact on overall application performance. If you disable namespaces, make sure you first enable the namespace-prefixes feature.

This is supported by all SAX2 XML parsers.Ælfred, Crimson, and Xerces support changing the value of this property.

http://xml.org/sax/features/namespace-prefixes

This flag defaults to false in XML parsers, indicating the parser will not present xmlns* attributes in its startElement() callbacks. Unless the flag is true, parsers won't portably present the qualified names (which include the prefix) used in an XML document for elements or attributes. The value can't be changed during parsing.

If you want to see the namespace prefixes for any reason, including for generating output without further postprocessing or for performing layered DTD validation, make sure this flag is set. Also make sure this flag is set if you completely disable namespace processing (with the namespaces feature flag), because otherwise the behavior of a SAX2 parser is undefined.

This is supported by all SAX2 parsers.Ælfred, Crimson, and Xerces support changing the value of this property.

http://xml.org/sax/features/string-interning

The default value for this flag is parser-specific. When true, this indicates that all XML name strings (except those inside attribute values) and namespace URIs returned by this parser will have been interned using String.intern(). Some kind of interning is almost always done to improve the performance of parsers, and this flag exposes this work for the benefit of applications. This value can't be changed during parsing.

When applications know interning has been done, they know they can rely on fast, identity-based tests for string equality (== or !=) rather than the more expensive String.equals() method. Using equality testing for strings will always work, but it can be much slower than identity testing. Java automatically interns all string constants. Lots of startElement() processing needs to match element and attribute name strings (as sketched in Example 2-8), so this kind of optimization can often be a win.

Ælfred interns all strings. Some older versions of Crimson don't recognize this flag, but all versions should correctly intern those strings. Xerces reports that it does not intern these strings.

http://xml.org/sax/features/validation

The default value for this flag is parser-specific; in most cases it is false. When the flag is true, the parser is performing XML validation (with a DTD, unless you've requested otherwise). When the flag is false, the parser isn't validating. The value can't be changed while parsing.

Ælfred, when packaged with its optional validator, Crimson, and Xerces support both settings.

A few additional standard extension features will likely be defined, providing even more complete Infoset support from SAX2 XML parsers.Ælfred also includes a nonvalidating parser, which supports only false for this flag.

Of the widely available parsers, only Xerces has nonstandard feature flags. (The Xerces distribution includes full documentation for those flags.) As a rule, avoid most of these, because they are parser-specific and even version-specific. Some are used to disable warnings about extra definitions that aren't errors. (Most parsers don't bother reporting such nonerrors; Xerces reports them by default.) Others promote noncompliant XML validation semantics. Here are a few flags that you may want to use.

http://apache.org/xml/features/validation/schema

This tells the parser to validate with W3C-style schemas. The document needs to identify a schema, and the parser must have namespaces and validation enabled. (Defaults to false.)

W3C XML schema validation does not need to be built into XML parsers. In fact, most currently available schema validators are layered.

http://apache.org/xml/features/validation/schema-full-checking

This flag controls whether W3C schema validation involves all the specified tests. By default, some of the more expensive checks are not performed; Xerces is not "fully conforming" by default.

http://apache.org/xml/features/allow-java-encodings

This flag defaults to false, limiting the encodings that the parser accepts to a handful. When the flag is set to true, more encoding names are supported. Most other SAX2 parsers effectively have true as their default. A few of those additional encoding names are Java-specific (such as "UTF8"); most of them are standard encoding names, either the preferred version or recognized alternatives.

http://apache.org/xml/features/continue-after-fatal-error

When set, this flag permits Xerces to continue parsing after it invokes ErrorHandler.fatalError() to report a nonrecoverable error. If the error handler doesn't abort parsing by throwing an exception, Xerces will continue. The XML specification requires that no more event data be reported after fatal errors, but it allows additional errors to be reported. (Of course, depending on the initial error, many of the subsequent reports might be nonsense.)

Previous | Home | Next
3.2. Bootstrapping an XMLReader | Book Index | 3.4. The EntityResolver Interface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The EntityResolver Interface (SAX2)

 SAX2Search this book

3.4. The EntityResolver Interface

As mentioned earlier, this interface is used when a parser needs to access and parse external entities in the DTD or document content. It is not used to access the document entity itself. Cases where an EntityResolver should be used include:

  • When "more local" copies of entity data should be used. Such copies might be from a local filesystem or from a smart caching proxy. A normal web server may be unavailable or may only be accessible through a slow or congested network link; such remote access can cause application slowdowns and failures. This is generically called catalog or cache processing.

  • When the entity's systemId uses a URI scheme that is not understood by the underlying JVM. Built-in schemes usually include http://, file://, ftp://, and increasingly https://. Schemes not supported by the JVM include urn: and application-specific schemes. (You may need to put such URI schemes into publicID values, in order to prevent problems resolving relative URIs.)

  • When entities need to be constructed dynamically, or not through the standard URI resolution scheme. For example, entity text might be the result of a query through some user interface or another computation.

  • When the XML source text doesn't provide usable URIs. SGML-style systems sometimes use system identifiers that aren't really URIs; they might be relative to some base URI other than the base URI of the appropriate entity (document or DTD). Avoid this practice for XML-based systems; it's not very interoperable because most XML processors strongly expect system IDs in XML documents to be valid URIs, relative to the actual base URI of their declaration.

Applications that handle documents with DTDs should plan to use an EntityResolver so they work robustly in the face of partial network failures, and so they avoid placing excessive loads on remote servers. That is, they should try to access local copies of DTD data even when the document specifies a remote one. There are many examples of sloppily written applications that broke when a remote system administrator moved a DTD file. Examples range from purely informative services like most RSS feeds to fee-based services like some news syndication protocols.

You can implement a useful resolver with a data structure as simple as a hash table that maps identifiers to URIs. There is normally no reason to have different parsers use different entity resolvers; documents shouldn't use the same public or (absolute) system identifiers to denote different entities. You'll normally just have one resolver, and it could adaptively cache entities if you like.

More complex catalog facilities may be used by applications that follow the SGML convention that public identifiers are Formal Public Identifiers (FPIs). FPIs serve the role that Universal Resource Names (URNs) serve for Internet-oriented systems. Such mappings can also be used with URIs, if the entity text associated with URIs is as stable as an FPI. (Such stability is one of the goals of URNs.)

Applications pass objects that implement the EntityResolver interface to the XMLReader.setEntityResolver() method. The parser will then use the resolver with all external parsed entities. The EntityResolver interface has only one method, which can throw a java.io.IOException as well as the org.xml.sax.SAXException most other callbacks throw.

InputSource resolveEntity(String publicId, String systemId)

Parsers invoke this method to map entity identifiers either to other identifiers or to data that they will parse. See the discussion in Section 3.1.2, "The InputSource Class", earlier in this chapter, for information about how the InputSource interface is used. If null is returned, then the parser will resolve the systemId without additional assistance. To avoid parsing an entity, return a value that encapsulates a zero-length text entity.

The systemId will always be present and will be a fully resolved URI. The publicId may be null. If it's not null, it will have been normalized by mapping sequences of consecutive whitespace characters to a single space character.

Example 3-3 is an example of a simple resolver that substitutes for a web-based time service running on the local machine by interpreting a private URI scheme and mapping public identifiers to alternative URIs using a dictionary that's externally maintained somehow. (For example, you might prime a hashtable with the public IDs for the XHTML 1.0, XHMTL 1.1, and DocBook 4.0 XML DTDs to point to local files.) It delegates to another resolver for other cases.

Example 3-3. Entity resolver, with chaining

public class MyResolver implements EntityResolver
{
    private EntityResolver next;
    private Dictionary     map;

    // n -- optional resolver to consult on failure
    // m -- mapping public ids to preferred URLs
    public MyResolver (EntityResolver n, Dictionary m)
    { next = n; map = m; }

    InputSource resolveEntity (String publicId, String systemId)
    throws SAXException, IOException
    {
    // magic URL?
    if ("http://localhost/xml/date".equals (systemId)) {
        InputSource	  retval = new InputSource (systemId);
        Reader 	  date;

        date = new InputStringReader (new Date().toString ());
        retval.setCharacterStream (date);
        return retval;
    }

    // nonstandard URI scheme?
    if (systemId.startsWith ("blob:") {
        InputSource   retval = new InputSource (systemId);
        String        key = systemId.substring (5);
        byte          data [] = Storage.keyToBlob (key);

        retval.setInputSource (new ByteArrayInputStream (data));
        return retval;
    }

    // use table to map public id to local URL?
    if (map != null && publicId != null) {
        String url = (String) map.get (publicId);
        if (url != null)
        return new InputSource (url);
    }

    // chain to next resolver?
    if (next != null)
        return next.resolveEntity (publicId, systemId);
    return null;
    }
}

Traditionally, public identifiers are mainly used as keys to find local copies of entities. In SGML, system identifiers were optional and system-specific, so public identifiers were sometimes the only ones available. (XML changed this: system identifiers are mandatory and are URIs.) In essence, public identifiers were used in SGML to serve the role that URNs serve in web-oriented architectures. An ISO standard for FPIs exists, and now RFC 3151 (available at http://www.ietf.org/rfc/rfc3151.txt) defines a mapping from FPIs to URNs. (The FPI is normalized and transformed, then gets a urn:publicid: prefix.) When public identifiers are used with XML systems, it's largely by adopting FPI policies to interoperate with such SGML systems; however, XML public identifiers don't need to be FPIs. You may prefer to use URN schemes in newer systems. If so, be aware that some XML processing engines support only URLs as system identifiers. By letting applications interpret public IDs as URNs, SAX offers more power than some other XML APIs do.

If you want richer catalog-style functionality than the table mapping shown earlier, look for open source implementations of the XML version of the OASIS SGML/Open Catalog (SOCAT). At this time, a specification for such a catalog is a stable draft, still in development; see http://www.oasis.org/committees/entity/ for more information. This specification defines an XML text representation of mappings; the mappings can be significantly more complex than the tabular one shown earlier.

Previous | Home | Next
3.3. Configuring XMLReader Behavior | Book Index | 3.5. Other Kinds of SAX2 Event Producers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Other Kinds of SAX2 Event Producers (SAX2)

 SAX2Search this book

3.5. Other Kinds of SAX2 Event Producers

Normally, an XMLReader turns XML text into SAX event callbacks. This book encourages you to think of those event consumer callbacks as the most important part of the process, so using XML text as input is just one option for feeding those consumers.

For example, some SAX parsers have turned HTML text into SAX callbacks; there have even been SAX wrappers around the limited javax.swing.text.html parser. These wrappers can help migrate to XHTML, first by making sure tags are properly formed, paired, and nested, then by helping make the XHTML be valid so more tools can work with it. Malformed HTML is a huge problem; there's lots of brain-dead HTML text on the Web.[18] In practice, no generally available SAX HTML parser is quite good enough to substitute for tools like HTML Tidy (see http://tidy.sourceforge.net) combined with manual fixup for problem cases, but that could change.

[18]One early browser development policy was that there's no such thing as broken HTML, so parsers needed to accept pretty much everything. The policy helped simplify content creation when there were few tools beyond text editors, but it also led to serious problems with browser incompatibility which are only now beginning to go away. It's also helped spread tools fostering malformed HTML (including flakey CGI scripts) and made it harder to present HTML on low-cost systems (it takes a fat parser to handle even a fraction of the different kinds of broken HTML).

The draconian error-handling policy of the XML specification (if it's not well formed, it must be rejected) was a reaction to those problems: XML parsers don't need to compete on how well they can make sense of garbage input. It was added at the request of the main browser vendors, which were then Netscape and Microsoft. This policy makes it a lot easier to create tools to process XML text, including presentation tools (XHTML browsing) that can even work on limited resource systems (such as PDAs or cell phones), content management tools, and "screen scrapers" for mining XHTML presentation text (to repurpose the data shown there).

3.5.1. DOM-to-SAX Event Production (and DOM4J, JDOM)

It's so typical to want to turn a DOM node into a series of SAX events that SAX2 defined a standard way to do this. Several of the projects that claim to improve on DOM by being more Java-friendly, such as DOM4J and JDOM, have similar functionality.

In conjunction with any sort of SAX text output API (such as an XMLWriter), this technique is an easy way to turn a DOM tree into text. Utilities to turn a DOM node into text all need to do more or less the same thing: traverse the tree and emit the right sort of text. Using SAX (and SAX utilities) you can do this without needing support for any optional DOM Level 3 modules and without relying on any vendor-specific DOM extensions. (It's also a fine technique to use when you need a debugging snapshot and can't afford the memory needed to deep-clone a DOM document.)

Of course, any other processing can be done too, such as validating the output. After initializing and connecting an appropriate event producer, consumer-side validator, and ErrorHandler, just produce the events and watch for reports of validity errors. In some cases (as with DOM-to-SAX converters), you can look at individual element subtrees; in other cases, you'll need to examine entire documents.

3.5.1.1. Turning DOM trees into SAX events

To turn a DOM node into SAX events, you'll need to use a special parser class; normal SAX parsers require text as input and won't know the first thing about DOM. If it's a Level 2 DOM and is using namespace support, you'll probably need to manually patch up the namespace data, since DOM isn't guaranteed to maintain it. Patching can be done before or after you generate SAX events; I prefer to use a single, generic SAX2 processing component to handle namespace fixups no matter where the problem arose, since DOM isn't the only culprit. Given such a parser class (the GNU version is used here), your code will look like this:

import gnu.xml.util.XMLWriter;
import org.w3c.dom.Node;
import gnu.xml.util.DomParser;

XMLReader       parser;
Node            node = ...;
ContentHandler  contentHandler = new XMLWriter (system.out);

parser = new DomParser ();
parser.setContentHandler (contentHandler);
// you may also set DTDHandler, LexicalHandler, and DeclHandler

parser.setProperty ("http://xml.org/sax/properties/dom-node", node);
parser.parse ("dom-node value gets parsed");

Neither Crimson nor Xerces currently include support for such DOM-to-SAX transformations.

3.5.1.2. Turning DOM4J trees into SAX events

In DOM4J (http://www.dom4j.org), it works like this. The current version of DOM4J isn't as flexible or complete as a DOM-to-SAX converter, though it has a few more options than JDOM. See the current release for more information.

import gnu.xml.util.XMLWriter;
import org.dom4j.io.SAXWriter;
import org.dom4j.Document;

SAXWriter       parser;
ContentHandler  contentHandler = new XMLWriter (system.out);
Document        doc = ...;

parser = new SAXWriter ();
parser.setContentHandler (contentHandler);
// you may also set DTDHandler and LexicalHandler

parser.write (doc);

3.5.1.3. Turning JDOM trees into SAX events

Here's how to do this conversion in JDOM (http://www.jdom.org). As this is being written, the current version of JDOM doesn't support the level of flexibility of a DOM-to-SAX parser; it only handles JDOM document nodes. It also doesn't support LexicalHandler or DeclHandler events. JDOM could support some of the LexicalHandler events, such as those for comments and CDATA section boundaries. See the current release for more information.

import gnu.xml.util.XMLWriter;
import org.jdom.Document;
import org.jdom.output.SAXOutputter;

SAXOutputter    parser;
ContentHandler  contentHandler = new XMLWriter (system.out);
Document        doc;

parser = new SAXOutputter (contentHandler);
// you may also set DTDHandler

parser.output (doc);

3.5.2. Push Mode Event Production

Since SAX event handlers are just objects, your application software can call their methods directly. This is a common technique for application code that needs to convert data structures to XML: turn them into SAX event streams for processing by other components. That component could be an XMLWriter sending data across the web to a partner, but you can do other kinds of processing too. Such application code normally has no reason to be wrapped as an implementation of XMLReader.

When used with in-memory data structures, this is part of what's sometimes called serialization. Be careful not to confuse this with the more specialized meaning in Java RMI, where serialization is a binary data format tied to individual Java classes. Other words used to describe this kind of process include "marshaling," "encoding," and "pickling." Reversing the process is an important parallel problem, since most of the time applications must both produce and consume XML data. That is, most applications round-trip data, rather than just consuming it or producing it.

This event generation technique is not restricted to data structures that were originally stored in memory. You can use it with data from databases, stored on filesystems, and entered through user interfaces. The same general technique is used in all these cases.

3.5.2.1. Turning CSV files into SAX events

Comma Separated Values, or CSV, is a data format that is widely used for some data interchange problems. Many spreadsheets and databases can read and write it, and it can be used to publish fairly large databases. It's one of the more widely understood "flat file" text formats, and it's not uncommon to need to translate data CSV formats into XML. With luck, the meaning of each field will be documented or maybe obvious from context. A simple CSV list of some yoga classes might have five fields per record and look like this:

daniela,4:30-5:45pm,ashtanga,sun,mixed
(staff),10:30am-12:00m,sivanenda,daily,open
philippe,7-9:00pm,ashtanga,mon,mixed
larry,4:30-5:45pm,ashtanga,wed,rocket
mahadevi,6-8:00pm,sivanenda,wed,advanced
savonn,7-8:30pm,vinyasa,wed,2-3
kei,9:30-11am,vinyasa,thu,intermediate
patti,7:30-9pm,iyenegar,thur,1-2
regan,9:30-11am,bikram,fri,open
mark,12m-2pm,ashtanga,sat,mysore

The translation is easier than the parsing of CSV itself. Details like handling of empty or missing fields, quoted values, and inconsistent value syntax are messy, and critical when importing lots of data. In fact, it's so messy that Example 3-4 completely avoids such lexical issues for CSV input data. (Nonlexical issues should be delegated to XML processing layers.) The example shows one way to translate; it's packaged more simply than a real-world application would probably expect. (Making an XMLReader that emits SAX events is possible and might be convenient.) This approach turns each CSV record into a single element by using attributes (with a sneak peek at a helper class we'll see later). It prints the output as XML text, which is probably not how you'd normally work with such data; the output is more naturally sent through a processing pipeline.

Example 3-4. Producing SAX2 events from CSV input

import java.io.*;
import java.util.StringTokenizer;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import gnu.xml.util.XMLWriter;

public class csv
{
    // stdin = (simple) CSV, stdout = XML
    public static void main (String argv [])
    {
    BufferedReader	in;
    XMLWriter	out;
    ErrorHandler	errs;
    String		line;

    try {
        in = new BufferedReader (new InputStreamReader (System.in));
        out = new XMLWriter (System.out);
        errs = new DefaultHandler () {
            public void fatalError (SAXParseException e) {
            System.err.println ("** parse error: "
                + e.getMessage ());
            }
        };

        out.startElement ("", "", "yoga", new AttributesImpl ());
        while ((line = in.readLine ()) != null)
        parseLine (line, out, errs);
        out.endElement ("", "", "yoga");
        out.flush ();
    } catch (Exception e) {
        System.err.println ("** error: " + e.getMessage ());
        e.printStackTrace (System.err);
        System.exit (1);
    }
    }

    // this doesn't handle quoted strings (with commas inside),
    // empty fields, tabs used as delimiters, or column headers.
    private static void parseLine (
    String		line,
    ContentHandler	out,
    ErrorHandler	errs
    ) throws SAXException
    {
    StringTokenizer	tokens = new StringTokenizer (line.trim (), ",");
    String		values [] = new String [5];

    // if there aren't five values, it's malformed
    if (tokens.countTokens () != 5) {
        errs.fatalError (
        new SAXParseException ("not enough values", null));
        return;
    }
    for (int i = 0; i < 5; i++)
        values [i] = tokens.nextToken ();

    // now that we parsed the line safely, report its contents

    // the AttributesImpl class is shown later
    AttributesImpl	atts = new AttributesImpl ();

    atts.addAttribute ("", "", "teacher", "CDATA", values [0]);
    atts.addAttribute ("", "", "time", "CDATA", values [1]);
    atts.addAttribute ("", "", "type", "CDATA", values [2]);
    atts.addAttribute ("", "", "date", "CDATA", values [3]);
    atts.addAttribute ("", "", "level", "CDATA", values [4]);

    out.ignorableWhitespace ("\n  ".toCharArray (), 0, 3);
    out.startElement ("", "", "class", atts);
    out.endElement ("", "", "class");
    }
}

The output of that program looks somewhat like this:

<yoga>
  <class teacher="daniela" time="4:30-5:45pm" type="ashtanga"
         date="sun" level="mixed"></class>
  <class teacher="(staff)" time="10:30am-12:00m" type="sivanenda"
         date="daily" level="open"></class>
  <class teacher="philippe" time="7-9:00pm" type="ashtanga"
         date="mon" level="mixed"></class>
  <class teacher="larry" time="4:30-5:45pm" type="ashtanga"
         date="wed" level="rocket"></class>
  <class teacher="mahadevi" time="6-8:00pm" type="sivanenda"
         date="wed" level="advanced"></class>
  <class teacher="savonn" time="7-8:30pm" type="vinyasa"
         date="wed" level="2-3"></class>
  <class teacher="kei" time="9:30-11am" type="vinyasa"
         date="thu" level="intermediate"></class>
  <class teacher="patti" time="7:30-9pm" type="iyenegar"
         date="thur" level="1-2"></class>
  <class teacher="regan" time="9:30-11am" type="bikram"
         date="fri" level="open"></class>
  <class teacher="mark" time="12m-2pm" type="ashtanga"
         date="sat" level="mysore"></class></yoga>

This included some ignorable whitespace to prevent the output from appearing as one big line of text; enabling pretty printing would do as well. Notice that the output needed to be flushed, else the JVM would normally exit with data still buffered in memory. We haven't yet looked at the endDocument() callback that would normally flush the data. Finally, notice that handling of any CSV conversion errors is delegated to a SAX error handler, which in this case adopts a very permissive strategy.

3.5.2.2. Turning objects into SAX events

For simple objects, something like the following "Address" example works. For a more complex object, such as a purchase order with multiple addresses for shipping and billing, you'll likely have routines that encode other data and use routines like this one as subroutines. You won't need to use any other handler interfaces, though you might want to embed comments or create CDATA boundaries using a LexicalHandler. Notice that startElement() calls always have matching endElement() calls, just as if the text was generated by an XML parser. This example declares and uses namespaces; you don't need to do that on the producer side if you patch them up later, but it's a reasonable practice to adopt. As used here, the AttributesImpl class just creates an empty set of attributes to pass on because null values can't be used:

static final String nsURI = "http://example.com/xml/address";

void toXML (Address addr, ContentHandler stream)
{
    char            temp [];
    Attributes      atts;

    // create an empty set of attributes
    atts = new AttributesImpl ();

    // <address xmlns="http://example.com/xml/address">
    stream.startPrefixMapping ("", nsURI);
    stream.startElement (nsURI, "address", "address", atts);

    // <street>...</street>
    stream.startElement (nsURI, "street", "street", atts);
    temp = addr.getStreet ().toCharArray ();
    stream.characters (temp, 0, temp.length);
    stream.endElement (nsURI, "street", "street");

    // <city>...</city>
    stream.startElement (nsURI, "city", "city", atts);
    temp = addr.getCity ().toCharArray ();
    stream.characters (temp, 0, temp.length);
    stream.endElement (nsURI, "city", "city");

    // <country>...</country>
    stream.startElement (nsURI, "country", "country", atts);
    temp = addr.getCountry ().toCharArray ();
    stream.characters (temp, 0, temp.length);
    stream.endElement (nsURI, "country", "country");

    // ... there would probably be more elements,
    // but not all application data in the "Address"
    // would be shared with the recipient.

    // </address>
    stream.endElement (nsURI, "address", "address");
    stream.endPrefixMapping ("");
}

If you're printing such output, you might want to add some ignorable whitespace to keep all the text from appearing on a single line. The resulting XML text will be easier to read, though having text without line breaks should not matter otherwise. (Better yet: use an XMLWriter with pretty-printing support.) If you are working with many namespaces, you may want to use the NamespaceSupport class (see Section 5.1.3, "The NamespaceSupport Class " in Chapter 5, "Other SAX Classes") to track and select the prefixes used in the element and attribute names you write.

It may also be a good idea to write "unmarshaling" code (taking such events and recreating, or looking up, application objects) at the same time you write marshaling code (like the preceding code, creating SAX events from application objects). That helps test the code: when round-trip processing works for many different data items (save a lot of test cases), you know it's behaving. Unmarshaling code can also be an appropriate place to test for semantic validity of data: you might have reason to trust that your current marshaling code is correct, but changes made next year could break something, and it's not good to expect everyone else will marshal correctly.

3.5.2.3. Data modeling concerns

As a rule of thumb, avoid assuming that your XML data model ought to match your application's data structures. Such policies can sometimes be appropriate, but more often, your application's internal data structures were optimized for something unrelated to communicating with other applications. Most systems that automatically marshal and unmarshal data structures (maybe using "reflection" in Java) will make such assumptions; they lead to tightly coupled systems. Tight coupling tends to cause fragility in the face of system evolution, since upgrades normally occur incrementally on widely distributed systems (such as almost all web-based applications).

For example, when you interchange the results of a complex set of queries from your database (perhaps for a large purchase order), it is typically appropriate to mask the exact relational structure used in your application. The recipient of your XML may well have adopted a different relational normalization. The recipient might not even expect to perform database operations on such data. Data displays may need to address usability issues that are completely unrelated to how applications "think" about the same data. Similar logic applies when the application data isn't stored in a database or is only partially stored in one.

On the other hand, if you're using XML to transfer a relation from one database to another, encoding a java.sql.ResultSet (or CSV table) into a series of elements (one element per table row, without duplications) may be exactly the right model. (The reverse transformation would be unmarshaling -- consuming XML to populate a database.) You won't always want to denormalize, even though the ability to easily do that is one of the great strengths of using XML to interchange data. Many common messaging scenarios involve the kind of data model that serves as input to normalization processes, and are oriented to individual cases not aggregates.

When you're encoding individual data items, such as integers, dates, or binary data encoded using BASE64, you should consider using the data-typing facilities in Part 2 of the W3C XML Schema specification (http://www.w3c.org/TR/xmlschema-2/). Those "simple" datatypes are intended to be used in many specifications. Its association with the particular schema system described in other parts of the W3C XML Schema specification can be viewed as a historical accident; you don't need to use W3C schemas to use these datatypes.

3.5.3. Producing Well-Formed Event Streams

If you are generating SAX2 events from any event producer that's not an actual XML parser (maybe by using an HTML parser or code that traverses data structures), you may need to ensure the event stream is legal before passing it to other components (maybe by printing it as XML text). There are issues of well formedess to think about: startElement() calls need matching endElement() calls, other calls require similar start/end nesting, carriage returns are prohibited in line ends, and more. Correct reporting of namespace information is important: prefixes must be declared and correctly used. Validity will also be an issue in many contexts as a policy of eliminating data format errors as early as possible. (It's cheaper to fix bugs before you ship them in products than afterward, and validation tools make some bugs easy to find.)

The particular issues you may have depend on what kind of event producer you use and what kinds of events you generate. DOM streams can easily be namespace-invalid; for example, prefixes are often undeclared or missing. Code that generates events directly is particularly prone to violate element nesting and closure requirements and to omit namespace declarations. Few tools prevent all kinds of illegal content; ]]> could appear in CDATA sections, and -- (two hyphens) within comments, both of which will prevent generation of legal XML text.

With high-quality producer-side code, you'll have fixed all those problems before the code is released. But you'll still probably want code that dynamically verifies that there's no problem to use when debugging or troubleshooting. If you adopt a good SAX2 event pipeline framework, it can easily support components that monitor event streams to ensure they meet those data integrity constraints or, in some cases (like namespaces), patch event streams so they are correct.

3.5.4. The XMLFilter Interface

SAX2 added the XMLFilter interface. XMLFilter is just an XMLReader that can be associated with a "parent" reader. What's interesting is the expectation that the parent is producing the events and the filter postprocesses them; the filter parses and modifies Infoset data, not XML text. From the perspective of your application code, a filter that you use as an XMLReader is doing some postprocessing of your parser requests, some processing on the XML data, then passing you the results; it's a preprocessor for infoset data.

The XMLFilter interface adds these methods to XMLReader:

void setParent(XMLReader parser)
XMLReader getParent()

The parent of an XMLFilter is accessed using standard JavaBeans property-naming conventions. Use this property to control which parser (or filter) generates the events to be filtered.

The role of the XMLFilter implementation is primarily to intercept and process SAX content events. Because its real work is to process those events, the code in such a filter is acting as a consumer. Implementing the XMLReader interface is a facade to make that consumer code look like a pull API (XMLReader) and let it intercept requests to an underlying parser. That is, it supports one kind of XML pipeline model.

Since the interesting issues are all on the consumer side, XMLFilter is discussed later with other kinds of SAX event pipeline models, in Section 4.5, "XML Pipelines ", in Chapter 4, "Consuming SAX2 Events", along with the XMLFilterImpl helper class.

If you're using these filters as event producers, you'll need to pay attention to a secondary role of an XMLFilter: intercepting and modifying parser requests. This kind of filter is a compound object. It consists of the filter, plus a reader (which might in turn be another filter), handler bindings, and settings for feature flags and properties. The interrelationships of these parts can get murky. In simple cases you can ignore the distinction, treating this type of SAX filter just like another reader. But in other cases you may need to remember that the filter and its parent are distinct objects with different behaviors.

For example, sometimes you'll find implementations of XMLFilter that don't use mechanisms such as the EntityResolver or ErrorResolver. When you need to use those mechanisms, you'd need to bind such objects to the parent parser. But most filters pass those objects on to the parent and may even need to use them internally, so you'd bind them to the filter instead. You'll need to know which kind of filter you have. In a similar way, if an underlying parser interns its strings, but the filter changes them (for example, swapping one namespace URI for another) and doesn't intern those strings, then code that talks to the filter can't use identity tests to replace the slower equality tests. The filter would have to expose a different setting for such feature flags than the parent parser.

Previous | Home | Next
3.4. The EntityResolver Interface | Book Index | 4. Consuming SAX2 Events
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Consuming SAX2 Events (SAX2)

 SAX2Search this book

Chapter 4. Consuming SAX2 Events

Contents:

More About ContentHandler
The LexicalHandler Interface
Exposing DTD Information
Turning SAX Events into Data Structures
XML Pipelines

Most of the power of SAX is exposed through event callbacks. In previous chapters you've seen some of the most widely used event callbacks as well as how to ensure that all the callbacks are generated and reported to application code.

This chapter presents the rest of the standard SAX event-handling interfaces (including the extension handlers), then talks about some of the common ways that event consumers use those interfaces. These interfaces are primarily implemented by application code that consumes events and needs to solve particular problems. You might also write custom event producers, which call these interfaces directly rather than expecting some type of XMLReader to issue them.

4.1. More About ContentHandler

In Section 2.3, "Basic ContentHandler Events", in Chapter 2, "Introducing SAX2", we looked at the most important APIs used to handle XML document content. Some other APIs were deferred to this section because they aren't used as widely. Depending on what problems you're solving, you may rely heavily on some of these additional methods.

4.1.1. Other ContentHandler Methods

Five ContentHandler callbacks were discussed in Chapter 2: Section 2.3.4, "Essential ContentHandler Callbacks" explained how characters and element boundaries were reported, and Section 2.6.4, "ContentHandler and Prefix Mappings" explained how namespace-prefix scopes were reported. But the interface has five other methods. Here's what they do and when you'll want to use them:

void setDocumentLocator (Locator l)

This is normally the first callback from a parser; the single parameter is a Locator, discussed later. Strictly speaking, SAX parsers are not required to provide a locator or to make this callback; however, you'd want to avoid parsers that don't provide this information. Your implementation of this callback will normally just save the locator; it can't do much more since it's the only SAX event callback that can't throw a SAXException:

class MyHandler implements ContentHandler ... {
    private Locator     locator;
    ...
    public void setDocumentLocator (Locator l)
    { locator = l; }
    ...
}

Use this object as discussed later in this chapter, in Section 4.1.2, "The Locator Interface ". It is the standard way to report the base URI of the XML text currently being parsed; that information is essential for resolving relative URIs. It's also essential for diagnostics that tell you where application code detects errors in large quantities of XML text.

void startDocument ()
void endDocument ()

These two callbacks bracket processing for a document, and they are normally used to manage application state associated with the document being parsed. If you're parsing a document, these methods will always be called once each, even when parsing is cut short by a thrown exception. No other methods have such guarantees.

startDocument() is always called before any data is reported from the parser, and is normally used to initialize application data structures. It will usually be the second callback from the parser; parsers that provide a Locator will report that first. You can't rely on a setDocumentLocator() call before startDocument(); structure your initialization code to do the real work in the callback guaranteed to be available.

endDocument() is always called to report that no more document data will be provided. The normal application response is to clean up all state associated with the current parse. The parser closes any input data streams you gave it using an InputSource (discussed later), so the application doesn't need to do that. Cleanup would include forgetting any saved Locator since that object is no longer usable when the parse is complete. Also, you'd likely close other files or sockets that were opened while processing this document:

class MyHandler implements ContentHandler ... {
    ...
    public void startDocument ()
    throws SAXException
    {
    // initialize data structures for ALL handlers here
    ...
    }
    public void endDocument ()
    throws SAXException
    {
    // free those same data structures
    locator = null;
    elementStack = null;
    ...
    }
    ...
}

These two calls are widely used in robust SAX code because they provide such good hooks to control memory usage and manage associated file descriptors. However, some SAX2 parsers have a bug that reduces the robustness offered by SAX; they won't correctly call endDocument() when parsing is aborted by throwing exceptions.

void processingInstruction (target, data)

Processing Instructions (PIs) are used in XML for data that doesn't obey the rules of a DTD. They can be placed anywhere in a document, including within the DTD, except inside other markup constructs like tags. Unlike comments, PIs are designed for applications to use. They're part of the document structure that programmatic logic must understand; they can follow rules, just not ones found in a DTD or schema. This method has two parameters:

String target

XML applications use this parameter to determine how to handle the PI. You can rely on the fact that it'll never be the string xml (in any combination of upper- and lowercase characters) because XML and text declarations are not processing instructions.

Some documents follow the convention that the target of a PI names a notation (perhaps the fully qualified URI found in its system identifier) and the meaning is associated with the notation rather than the name. That's a fine practice to follow, but it isn't essential. Most code just compares target names as strings, rather than use data reported with DTDHandler.notationDecl() to figure out what a target name should mean.

String data

This parameter is data associated with the PI, and it may be the null string if no data was provided after the target name. Some applications use the syntax of an attribute here; others don't bother.

Processing instructions are natural to use in template systems and other document-oriented applications.[19]

[19]For example, the syntax of PHP, the web page scripting tool, looks like a processing instruction, <?php ...?>. For various reasons, PHP is not actually an XML document syntax.

Processing instructions are normally safe to ignore when your processing doesn't recognize them (passing them on to any subsequent processing stage), or to store. If the parser does recognize them, it normally acts on then immediately. For example, an <?xml-stylesheet ...?> PI might select a particular XSLT stylesheet to use for generating a servlet's output. The processing instruction event is used later, in Example 6-9.

void ignorableWhitespace(buf,offset,len)

This is an optional callback, made by most parsers (including all that are validating) to report whitespace that separates elements in element content models, like those of the form (title,para*,sect1*) but not (#PCDATA|para|comment)*, ANY, or EMPTY. Whitespace before or after the document's root element is not treated as ignorable and is completely discarded. Providing this information is a requirement of the XML specification, since this kind of whitespace is defined to be markup rather than document content. If the parser doesn't see such a content model declaration for any reason, it can't use this callback; it'll use characters() instead, and applications will need to figure out if the whitespace is part of markup or part of content.

The parameters are exactly the same as those of the characters() callback, except that you know the characters in the specified range will all be spaces, tabs, or newlines. (Keep that in mind if you're directly producing ignorable whitespace to feed some event consumer. Using CRLF- or CR-style line ends here is a bug, though you might not see immediate consequences.) Like characters(), this method can be called several times in a row, to complete processing a single stretch of characters.

There are two popular ways to handle this callback. My favorite is to drop all the characters; they're only in the source document to make the elements lay out nicely, so they won't ever mean anything. There's rarely a reason to even look at the data, much less save it. The other option is to delegate handling and just call the characters() callback with the whitespace.

void skippedEntity (String name)

The parameter is a String that identifies an internal or external parsed entity. General entity names are presented as found in their declarations (dudley). Parameter entity names begin with a percent sign (%nell). The external DTD subset is special; it's an unnamed parameter entity and is reported with the name [dtd]. You might not be able to tell if the skipped entity was an internal or external entity, even using DeclHandler events.

You probably don't ever want to see this call, since it means that part of your document has been hidden. XML 1.0 processors are required to report this case; SAX 1.0 didn't, and most other parser-level APIs (such as DOM Level 2) still don't. This is a call that only nonvalidating parsers may issue, and even then only if they are not parsing all the external entities referred to in documents -- that is, where one or both of the external entities feature flags is set to false, to disable reading external general or parameter entities. No widely used Java parsers clear those flags by default, so this is a rare call in Java. However some C parsers, such as Expat (used in Mozilla), won't normally parse external entities, so the notion isn't exotic in all languages.

4.1.2. The Locator Interface

This useful interface is sometimes overlooked. It gives information that is essential for providing location-sensitive diagnostics and is often given to SAXParseException constructors. That same information is also needed to resolve relative URIs in document content or attribute values (such as xml:base). Parsers provide one instance of this class, which can be used inside event callbacks to find what entity triggered the event and approximately where. Use that locator only during such callbacks. There are only a few methods in this class.

String getSystemId ()

This is the most important method in this interface. It returns the base URI (system ID) for the entity being parsed; this is always an absolute URI. (However, versions of Xerces that are current at this writing have a bug here. They sometimes return nonabsolute URIs.) Use this method to identify the document or external entity in diagnostics or to resolve relative URIs (perhaps in conjunction with xml:base attributes).

If the parser doesn't know this value, null is returned. This normally indicates that the parser was not given such a URI inside of a InputSource encapsulating document text. That's bad practice except when it's unavoidable, such as parsing in-memory data or input to the POST method in a servlet.

int getLineNumber ()
int getColumnNumber ()

These two functions approximate the current position of a parser within an entity. The position reflected is where the relevant event's data ended. It is only an approximation for diagnostics, but most parsers do try to be accurate about the line number.

These numbers count up from 1 as appropriate for user-oriented diagnostics. Not all implementations will provide these values; the value -1 is returned to indicate that no value was provided.

String getPublicId ()

A public identifier may be provided with this method. Otherwise null is returned. This may be useful for diagnostics in some cases.

One common use for a locator is to report an error detected while an application processes document content. The SAXParseException class has two constructors that take locator parameters. (The descriptive string is always first, the locator is second, and an optional "root cause" exception is third.) Once you create such an exception, it can be thrown directly, which always terminates a parse. Or you pass it to an ErrorHandler to centralize error handling-policy in your application:

// "locator" was saved when setDocumentLocator() was called earlier
// or was initialized to null; this is safe in both cases
try {
    ...
    engine.setWarpFactor (11);
    ...
} catch (DriveException e) {
    SAXParseException	spe = new SAXParseException (
        "The warp engine's gonna blow!",
        locator,
        e);
    errHandler.error (e);
    // we'll get here whenever such problems are ignored
}

To resolve relative URIs in document content -- for example, one found in an <xhtml:a href="..."/> reference in a link checker -- you'd use code like this (ignoring xml:base complications):

public void startElement (String uri, String lname, String qname,
    Attributes atts) throws SAXException
{
    if (xhtmlURI.equals (uri)) {
    if ("a".equals (lname)) {
        String href = atts.getValue ("href");
        if (href != null) {
        // ASSUMES:  locator is nonnull
        System.out.println ("Found href to: " +
            new URI (new URI(locator.getSystemId ()), href));
        }
        // else presumably <xhtml:a name="..."/>
    }
    } ...
}

Some of the XMLReader implementations cannot possibly call ContentHandler.setDocumentLocator() with a Locator. When parsing in-memory data structures, such as a DOM document, a locator will normally be meaningless. When parsing in-memory buffers like a String (with a StringReader), there won't usually be a URI in the locator.

If your application supports the layered xml:base convention (which lets documents "lie" about their true locations for purposes of resolving relative URIs), it will need to track those attributes itself, as part of a context stack mechanism. (An example of such a stack is shown later, in Example 5-1.) Such attributes can sometimes help make up for SAX event sources that can't provide locator information, such as DOM-to-SAX producers. But they can confuse things too: in the following example, xml:base would apply to the top element and its direct children, but nothing within the external entity reference. (Let's assume, for the sake of discussion, that no element has an xml:base attribute.)

<top xml:base="http://www.example.com/moved/doc2.xml">
    <xhtml:a href="abc.xml"/>
    <xhtml:div> &external; </xhtml:div>
    <xhtml:a href="xyz.xml"/>
</top>

When character content of an element is reported, characters from different external entities will get different callbacks, so the locator can be used to tell those different entities apart from each other.

4.1.3. Internationalization Concerns

One of the goals of XML was to bring Unicode into widespread use so that the Web could really become worldwide in terms of people, not just technology. This brings several concerns into text management. You may not need to worry about these if you're working only in ASCII or with just one character encoding. While you're just starting out with Java and XML you should certainly avoid worrying about these details. Some other users of SAX2 will need to understand these issues. Since they surface primarily with ContentHandler event callbacks, we briefly summarize them here.

If your application works with MathML, or in various languages whose character sets gained support in Unicode 3.1 through the so-called Astral Planes, you will need to know that what Java calls a char is not really the same thing as a Unicode character or an XML character. If you aren't using such languages, you'll probably be able to ignore this issue for a while. Still, you might want to read about Unicode 3.1 to learn more about this and minimize trouble later. By the time you read this, the W3C may even have completed its "Blueberry" XML update, intended to allow the use of some such characters within XML names.

In the case of such characters, whose Unicode code point is above the value U+FFFF (the maximum 16-bit code point), these characters are mapped to two Java char values, called a surrogate pair. The char values are in a range reserved for surrogate characters, with a high surrogate always immediately followed by a low surrogate. (This is called a big-endian sequence.) Surrogate pairs can show up in several places in XML, and hence in SAX2: in character content, processing instructions, attribute values (including defaults in the DTD), and comments.

At this time, Java does not have APIs to explicitly support characters using surrogate pairs, although character arrays and java.lang.String will hold them as if the char values weren't part of the same character. The java.lang.Character class doesn't recognize surrogate pairs. The best precaution seems to be to prefer APIs that talk in terms of slices of character arrays (or Strings), rather than in terms of individual Java char values. This approach also handles other situations where more than one char value is needed per character.

Depending on the character encodings you're using and the applications you're implementing, you may also need to pay attention to the W3C Character Model (http://www.w3.org/TR/charmod/ at this writing) and Unicode Normalization Form C. Briefly, these aim to eliminate undesirable representations of characters and to handle some other cases where Unicode characters aren't the same as XML characters or a Java char, such as composite characters. For example, many accented characters are represented by composing two or more Unicode characters. Systems work better when they only need to handle one way to represent such characters, and Form C addresses that problem.

Previous | Home | Next
3.5. Other Kinds of SAX2 Event Producers | Book Index | 4.2. The LexicalHandler Interface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The LexicalHandler Interface (SAX2)

 SAX2Search this book

4.2. The LexicalHandler Interface

This extension interface is new in SAX2. It's in the org.xml.sax.ext package, which means among other things that it is optional and isn't supported by all SAX APIs and layers, such as DefaultHandler. However, any SAX2 parser that can be bootstrapped with JAXP supports this interface. Parsers that support LexicalHandler expose comment text and the boundaries of CDATA sections, DTDs, and most parsed entities. There is no setLexicalHandler() method; bind these handlers to parsers like this:

XMLReader	producer = ...;
LexicalHandler	handler = ...;

producer.setProperty ("http://xml.org/sax/properties/lexical-handler",
    handler);
// throws SAXNotSupportedException if parameter isn't a LexicalHandler
// throws SAXNotRecognizedException if parser doesn't support it.

The information this exposes is needed for applications that need more in the way of "round-tripping" support than the SAX2 core allows. That is, less of the information read by parsers will be completely discarded. The application needs SAX to provide more complete support for the XML Infoset (or for the XPath data model). To completely support DOM, XPath, or XSLT on top of a SAX2 parser, this interface is as necessary as the namespaces exposed in the SAX2 ContentHandler and Attributes interfaces. The downside is that much of this information is in the category of information applications shouldn't want to deal with. Be careful how you use these callbacks; don't assume that just because the information is available, you should use it.

LexicalHandler has the following methods:

void comment(buf,offset,len)

Reports characters inside a <!--...--> comment section (without the delimiting characters).For many applications, this event is the only reason to use this interface. This is almost the same convention ContentHandler uses to report character content or ignorable whitespace; the parameters are identical. Comments are always reported in a single callback. Two consecutive comment() calls means two consecutive comments, while two consecutive characters() calls just enlarge a given logical span of text.

char buf []

A character array that holds the comment text. As with the ContentHandler.characters() callback, you must ignore characters in this buffer that are outside of the specified range.

int offset

The index of the first comment character in the buffer.

int len

How many comment characters are in the buffer, beginning at the specified offset.

Comments show up in the XPath data model, so they are reflected in layers (such as XSLT, XPointer, and XLink) that build on XPath. Strictly speaking, applications should ignore comments except when they round-trip data provided during authoring. Instead, they should use processing instructions when they need to work with annotations. You might need to use comment data with HTML processors because it doesn't support processing instructions. For example, HTML documents often use comments to wrap CSS data, JavaScript code, or server-side includes.

There are two good ways to handle comments. One is just to discard them and make the implementation of this method do nothing. (I like that one!) The other is to create a new String using the method parameters and save the string somewhere. Avoid parsing comment content; if you're tempted to do that in new applications, try to use PIs (which were designed for such purposes).

public void comment (String buf, int offset, int len)
throws SAXException
{
    String value = new String (buf, offset, len);
    ... now that you have it, what do you want to do?
}
void startDTD(name, publicId, systemId)
void endDTD()

The startDTD() event reports the beginning of a document's DTD, and endDTD() reports the end. These events can be useful when you save DTD information, such as the partial support in DOM Level 2. It is also important when you create SAX event streams that may need to print as documents that include a DTD.

String name

The declared name of the root element for the document. It is never omitted, though for invalid documents it may not correspond to the name of the root element.

String publicId

Normalized version of the public ID declared for the external subset, or null if no such subset was provided.

String systemId

The system ID declared for the external subset, or null if no such ID was provided. Note that this URI is not absolutized.

When the end of the DTD is reported, all other declarations that should have been reported (with DeclHandler or DTDHandler callbacks) will have been reported. If any ContentHandler.skippedEntity() calls were made for external parameter entities, applications will normally infer that some declarations were not processed.

Parsers are not required to distinguish the internal and external subsets. There are two mechanisms applications can use, but both of them are optional. The natural method is to rely on external parameter entity boundary reports, using other methods in this interface. Not all parsers report those entities; you can check the lexical-handler/parameter-entities feature flag to see if this mechanism will work for you. The other mechanism compares base URIs as reported through the Locator.getSystemId() method; base URIs for external subset components will differ from those of the document itself. Most parsers support this method, but it's awkward to use for this purpose.

If you're saving DTD content, these methods will bracket a lot of work where you squirrel data away for later use. Otherwise, you'll probably arrange to ignore all the other DTD events and will only need to decide what to do with comments and processing instructions, if you don't just ignore them. Ignoring them within DTDs is a popular strategy even when they're not ignored elsewhere. This is because comments or PIs inside a DTD would seem to apply to DTD contents, while most applications are instead working with document contents.

void startCDATA()
void endCDATA()

These methods report the beginning and end of a <[CDATA[...]]> text section; the bracketing characters are not reported. Any content within a CDATA section is reported with characters() events; the < and & characters within CDATA sections are parsed like normal characters, not like delimiters for markup.

Most software has little reason to care whether character content is contained in CDATA sections. Unless you are trying to round-trip data while preserving those lexical artifacts (to simplify potential future work done with text editors), the right response to CDATA events is to ignore them.

void startEntity(String name)
void endEntity(String name)

These methods report the beginning and end of internal or external entity expansion. The entity is named using the same rules as the ContentHandler.skippedEntity() callback. If you need to indicate which kind of entity is being expanded, record information from the DeclHandler.externalEntityDecl() callback and consult it in these methods. (That means you'll likely really want an extended DefaultHandler or XMLFilterImpl that supports both of the standardized extension classes.)

Expansions of general entity references, like &dudley;, are reported everywhere except inside attribute values. Such expansions within entity values can't meaningfully be reported, since all markup within start tags is reported at the same time.

Not all parsers report expansion of parameter entities, like %nell;, in DTDs. There is a special parser feature flag (lexical-handler/parameter-entities) that determines whether parsers report such events. As with general entity references, not all parameter entity expansions can be meaningfully reported. Parameter entities that expand as part of markup declarations or conditional section markers won't be seen, since markup declarations are reported only in their entirety.

Previous | Home | Next
4. Consuming SAX2 Events | Book Index | 4.3. Exposing DTD Information
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Exposing DTD Information (SAX2)

 SAX2Search this book

4.3. Exposing DTD Information

SAX2 exposes DTD information through three different interfaces. Part of it is exposed through the LexicalHandler extension interface: the DTD's root element type declaration and boundaries of the various entities. The rest is exposed through two DTD-specific interfaces, presented here.

When you're working with streams of SAX event data, remember that all DTD event data is seen before the document data it describes. This means that if you need it inside the document, you'll need to plan ahead to save the DTD data. It also means that if you need to merge streams of event data, such DTD data may create a problem. Unless you know the DTD data in advance, you'd need to dam up the event stream until all data that needs to go into downstream DTD events is in hand. Only then can you send the events downstream (with the DTD first). Luckily, merging event streams with unknown DTD data isn't common.

DTD information is automatically used inside XML parsers when they parse XML documents. That includes expansion of conditional sections and parameter entities in DTDs, expanding general entities, and normalizing or defaulting attributes. Most DTD validation can be cleanly layered on top of SAX2 since these declaration callbacks provide all the most important information.[20] SAX2 enables application-level processing of DTD constraints; the only internal support it provides for DTDs is a feature flag to expose parser support for validation. When applications need to construct valid documents, they can use DTD information as they make changes, instead of needing to save the document and reparse the whole thing.

[20]The exceptions relate to lexical constraints that should arguably be well-formedness constraints. Entity nesting is supposed to match nesting of grammatical constructs within DTDs; that's a validity constraint. However, the analogous constraint in a document body affects well-formedness instead.

The support for working with DTDs provided by most XML tools is not as good as the support provided by SAX2. For example, DOM Level 2 provides weaker support, and the TRAX support for SAX (java.xml.transform.sax) doesn't support DeclHandler at all.

Note that while a fully featured SAX2 parser will let you re-create the internal subset, it will not let you round-trip any external parameter entities. That's because parameter entities will be expanded. You will not see conditional sections in external PEs, or declarations being built up from parameter entities. Instead, you'll see the actual declarations that apply to your documents. This may help you to understand exactly what a complex DTD is doing.

4.3.1. The DeclHandler Interface

This extension interface is new in SAX2. It's in the org.xml.sax.ext package, which means among other things that it is optional and not all SAX APIs support it. (DefaultHandler is one example of an API that does not.) However, any SAX2 parser that can be bootstrapped with JAXP must support this interface. There is no setDeclHandler() method; bind these handlers to parsers like this:

XMLReader	producer = ...;
DeclHandler	handler = ...;

producer.setProperty ("http://xml.org/sax/properties/
    declaration-handler",handler);
// throws SAXNotSupportedException if parameter isn't a DeclHandler.
// throws SAXNotRecognizedException if parser doesn't support it.

Parsers that support DeclHandler are essential for applications that need to work with declarations of elements and attributes or with parsed entities. DOM requires such support for parsed entities, although even Level 2 hides or ignores element and attribute type data. This interface is the most common way SAX2 exposes type constraints (the primary role of a Document Type Declaration) from DTDs, so if you need to see those constraints, you'll use this handler. It has four API callbacks:

void attributeDecl(eName,aName,type,mode,value)

This callback reports <!ATTLIST ... > declarations in a DTD. A given declaration produces one callback for each attribute in the declaration. Much of this information will also be provided through Attributes methods if an instance of that element appears in a document.

String eName

This is the name of the element whose attribute is being declared.

String aName

This is the name of the attribute associated with that element.

String type

This is one of the strings CDATA, ID, IDREF, IDREFS, NMTOKEN, NMTOKENS, ENTITY, or ENTITIES, or two types of enumerated values. Enumerated values are encoded with parenthesized strings such as (a|b|c) to indicate that strings a, b, or c are permissible. If the string is an enumeration of notation names, "NOTATION " (which includes one space) precedes that parenthesized string.

This type information is more complete than information you get through the Attributes object provided with startElement(), because Attributes reports only enumerations as being either NOTATION or NMTOKEN. However, at this time several widely available SAX2 parsers conform to a beta test version of this API and don't correctly report enumerations. You may need to get a bug-fixed version of your parser if you're depending on this support.

String mode

This describes the kind of default value applied to this attribute: #IMPLIED (the application determines the value), #REQUIRED (the value must be given; defaulting is not permitted), #FIXED (only one value is permitted), or null indicating that value is the default.

Unless the document provided a value, you won't see #IMPLIED attributes in the Attributes object provided with startElement(); if you need to know this information, save it when you get this callback.

String value

This parameter is either null or a string with the default value for this attribute. That might be the only permitted value if the attribute mode is #FIXED. The value will be reported exactly as applications will see it: normalized and with character and entity references replaced.

XML structure editors can use this information to constrain the choices presented to document authors so that only valid documents can be created. Other tools that construct documents will also benefit from having this information. When you're mostly reading documents rather than creating them, the most important data here tends to be declaration of ID, IDREF, and IDREFS attributes, which are used to build links within and between XML documents.

If more than one declaration for an attribute is provided, only the first one will be used. (The second one will be ignored; unlike the analogous case for element declarations, attribute redeclaration is not a validity error.) Normally code to implement this callback would first retrieve any existing per-element data structure, or it would create one (with a null content model) if none is yet known. Then if there is no record of an attribute with this name for that element, a per-attribute data structure instance would be created and saved in the element data structure, keyed by attribute name.

void elementDecl(name,model)

This method reports <!ELEMENT ... > declarations in a DTD.

String name

This is the element name.

String model

This is the element content model, with all whitespace removed. For example, element content models like (a,(b|c)+,d?), mixed content models like (#PCDATA|one|two|three)*, and simple models like ANY and EMPTY may all be found in the same document. Note that parsers may do more than just remove the whitespace, as long as an equivalent content model is reported.

Because the content model is provided as a string, applications using it must always parse it themselves. Similarly, if applications want to validate against that model, they must provide code to do that. Except for the case of element content, such work is straightforward. Validating element content models requires constructing and using some sort of finite state automaton, and it takes a bit of work to parse the model. Mixed content models are easier to handle since they can be parsed with a java.util.StringTokenizer and because the validation logic is simpler.

If more than one declaration for an element is provided, only the first one will be used. (The second one will be considered a validity error; element type redeclaration is not allowed.) Normally the code implementing this callback would create a new per-element data structure to save the name and content model and store it in data structure (hash table or other map) keyed by element name. Such a data structure might already exist if an element attribute was declared before the element. In this case, this callback just provides the content model, which was previously unknown.

void externalEntityDecl(name,publicId,systemId)

This callback reports <!ENTITY ... > declarations in a DTD for parsed external entities. These may be either general or parameter entities.

String name

This is the entity name; it is always provided. Names that start with % are parameter entities; all others are general entities.

String publicId

This is the public ID for the entity and can be omitted (provided as null). If public IDs are provided, any embedded whitespace is normalized, so these strings may be directly compared. They may be used to determine a location for the entity, for example, by using an SGML Formal Public Identifier with some sort of catalog.

String systemId

This is the system ID for the entity and is always provided. It is an absolute URI, which parsers normally use to retrieve the entity before parsing it. However, some SAX2 parsers have a bug, and won't report the absolute URI here.

Applications usually ignore all parameter entity declarations and use the org.xml.sax.EntityResolver when they want to provide local copies of these entities to a parser. If applications don't ignore these declarations, redeclaration should be ignored (it is not an error). XML editors may want to offer menus of external (and internal) entities when editing element content. And in some cases you may want to track external entities by name so that you can tell when LexicalHandler.startEntity() is reporting the start of one; this is useful for applications that use xml:base attributes to change applications' views of the actual URI that contains an element, using the Locator.getSystemId() method. (Perhaps the actual location was not known, or should for some reason be ignored.)

void internalEntityDecl(name,value)

This callback reports <!ENTITY ... > declarations in a DTD for (parsed) internal entities. These may be either general or parameter entities.

String name

This is the entity name. Names that start with % are parameter entities, all others are general entities.

String value

This is the entity value, which contains arbitrary XML content (including elements and nested entity references) that will be reparsed when this entity is expanded.

Applications normally ignore all parameter entity declarations. If applications don't ignore these declarations, redeclaration for a name should be be ignored (it is not an error). XML editors may want to offer menus of internal entities when they edit attribute values or element content. However, SAX2 does not report entity references inside the attribute values it parses. This means that you won't be able to re-create such text without heuristics.

4.3.2. The DTDHandler Interface

The DTDHandler interface was carried unchanged from SAX1 into SAX2 and is primarily useful for applications that work with two specific SGML notions: notations and unparsed entities. Some DTDs, such as XML DocBook, use notations in such traditional roles. DOM also requires such support. Use XMLReader.setDTDHandler() to bind this handler to a parser. You probably won't ever need to use it for new code. On the Web, those SGML notions correspond roughly to MIME types and URIs respectively, web concepts that are much more widely understood and supported. The interface has only two API callbacks, provided to meet specific requirements in the XML 1.0 specification:

void notationDecl(name,publicId,systemId)

This callback reports a <!NOTATION ...> declaration in a DTD.

String name

This is the notation name; it is always provided. These names are used explicitly in unparsed entity declarations and in some kinds of attribute declaration (elements can have one such attribute, used to associate type with the element). Also, some applications follow a convention that they may be used to identify processing instruction targets.

String publicId

This is the public ID for the notation and may be omitted (provided as null). If public IDs are supplied, then any embedded whitespace is normalized, so these strings may be directly compared. These may be used to assign a meaning to the notation, for example, by using an SGML Formal Public Identifier in a role much like a MIME type.

String systemId

This is the system ID for the notation and may be omitted (provided as null). When provided, it is an absolute URI. However, some SAX2 parsers have a bug, and won't report the absolute URI here. These may be used to assign a meaning to the notation, for example, by using a URI to identify a type or command.

In addition to assigning types to unparsed entities, a NOTATION attribute may also associate a type with an element or processing instruction. Some DTDs provide extensive catalogs of notation declarations specifically for such uses.

Note that notation declarations are the one place in XML syntax where you can provide a public ID without a system ID, and that at least one identifier (public or system) must always be provided. If applications don't ignore these declarations, redeclaration should be ignored (it is not an error).

void unparsedEntityDecl(name,publicId,systemId,notation)

This callback reports <!ENTITY ... > declarations with NDATA annotations to associate them with a notation (such as jpeg or png). Unparsed entities are used only in attributes that are declared to be of type ENTITY or ENTITIES.

String name

This is the name of the unparsed entity; it is always provided.

String publicId

This is the public ID for the notation and may be omitted (provided as null). If public IDs are provided, any embedded whitespace is normalized, so these strings may be directly compared. These may be used to assign a location to the entity, for example, by using an SGML Formal Public Identifier in a role much like a URN.

String systemId

This is the system ID for the notation and is always provided. It is normally an absolute URI. However, some SAX2 parsers have a bug, and won't report the absolute URI here. These may be used to assign a location to the entity.

String notation

This is the name of the notation associated with the entity; it is always provided. The role of these names is much like that of an external MIME type annotation for the entity.

In XML, unparsed entities are declared to parsers but pass through them without being parsed. Classic examples of unparsed entities include JPEG or PNG image files. Such entities may also be used for XML text that just doesn't need to be parsed in a given processing stage. If applications don't ignore these declarations, redeclaration should be be ignored (it is not an error).

Most XML applications that care about unparsed entities and notations do so because they interface with SGML systems that use them or are migrating such systems to use the XML generation of tools. XML editors supporting this functionality might use these event callbacks to create menus of notations or unparsed entities when they are editing attributes that hold such values.

Applications that use this interface will normally use the callbacks to create two tables, keyed by entity or notation name respectively, that are used to interpret element attributes. More rarely, notations will be used to determine the operation corresponding to a given processing instruction target name. Secure applications will never use notations to directly encode system commands, but will always redirect through application controlled tables. For example, it would be foolish to rely on system IDs found in a document. System IDs such as rm -rf /, when run through a Unix or Linux shell, would remove all files accessible through the local system.

Previous | Home | Next
4.2. The LexicalHandler Interface | Book Index | 4.4. Turning SAX Events into Data Structures
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Turning SAX Events into Data Structures (SAX2)

 SAX2Search this book

4.4. Turning SAX Events into Data Structures

As described earlier, one of the great strengths of SAX is that it lets applications use appropriate data structures, instead of forcing the use of generic data structures. In Section 3.5.2, "Push Mode Event Production" in Chapter 3, "Producing SAX2 Events", we looked at the problem of producing SAX events from data structures. Here we look at the reverse process: producing data structures from SAX events. This is a process that most SAX applications handle to one degree or another. One of the most traditional names for this process is unmarshaling; it's also sometimes called deserializing. (I tend to avoid using the latter term with Java except when talking about RMI.)

We'll first look at how to turn SAX into generic DOM (and DOM-like) data structures. If you're working with such data structures, you may find it's advantageous to build them using SAX. With SAX, you can easily discard data you don't need, filtering it out so you don't need to pay its costs. Afterward we'll look briefly at some of the concerns associated with working with data structures that are more specialized to your application.

4.4.1. SAX-to-DOM Consumers

It's easy to turn a SAX event stream into a complete DOM document tree, or into a DOM-like data structure such as DOM4J or JDOM. Most open source DOM parsers build those data structures directly from SAX event streams. (Xerces has the only such DOM I know that doesn't work that way.) Building a DOM document from a SAX2 event stream requires implementing all four event consumer interfaces: ContentHandler, of course; LexicalHandler to report boundaries of entity references and CDATA sections as well as comments; and both DeclHandler and DTDHandler to provide the subset of DTD information that DOM requires. The implementations of those interfaces must use nonstandard DOM functions, because key functionality is missing from public DOM APIs. This means that if you're using generic code to construct a DOM tree, you won't be able to implement every behavior DOM specifies. If that doesn't seem like a feature to you, you'll need builder code that's specialized to a particular DOM implementation.

Table 4-1 shows the classes that various DOM implementations provide for turning a SAX2 event stream into a DOM tree.[21] Most classes have configuration options to let you discard some of the minimally useful data, instead saving it and making your application code ignore it later. Except as noted, they implement all four consumer interfaces. Each one has a way to present the DOM data it produces, usually with a getDocument() method; consult documentation (or source code) for full information.

[21]As presented in Chapter 3, in Section 3.5.1, "DOM-to-SAX Event Production (and DOM4J, JDOM)", most of these packages also support DOM-to-SAX event producers.

Table 4-1. SAX-to-DOM consumer classes

Implementation | Class name | Comment
Crimson | org.apache.crimson.tree.XmlDocumentBuilder | Implements all the event consumer handlers.
DOM4J | org.dom4j.io.SAXContentHandler | Extends DefaultHandler; does not implement DeclHandler.
GNUJAXP | gnu.xml.dom.Consumer | Uses the gnu.xml.pipeline framework.
JDOM | org.jdom.input.SAXHandler | Extends DefaultHandler.

Example 4-1 uses the DOM implementation from Crimson to illustrate how easy it is to construct a DOM tree from SAX events.

Example 4-1. Converting SAX events to a DOM document (Crimson)

public Document SAX2DOM (String uri)
throws SAXException, IOException
{
    XmlDocumentBuilder	consumer;
    XMLReader		producer;

    consumer = new XmlDocumentBuilder ();

    producer = XMLReaderFactory.createXMLReader ();
    producer.setContentHandler (consumer);
    producer.setDTDHandler (consumer);
    producer.setProperty
    ("http://xml.org/sax/properties/lexical-handler",
    consumer);
    producer.setProperty
    ("http://xml.org/sax/properties/declaration-handler",
    consumer);

    producer.parse (uri);
    return consumer.getDocument ();
}

4.4.2. Pruning Noise Data from a DOM Tree

For various historical reasons, DOM provides much information that just adds overhead to applications. When you build a DOM with SAX2, it's particularly easy to prune that information out of DOM trees: you can simply arrange never to deliver it! Similar techniques are frequently used when feeding SAX event data to a component. It's often easier to let the component see only parts of the Infoset that you care about than to remove the resulting data noise later.

The simplest example of this would be just to hook up the ContentHandler to a SAX parser and ignore the other three handlers. The resulting DOM will not have DTD information, but that's no loss, because even DOM Level 2 doesn't provide enough of the DTD information to be useful. (You can save more complete DTD information using custom SAX handlers, if you need it.) Because the LexicalHandler isn't provided, you won't see comment nodes or entity reference nodes (or their read-only children which really complicate your code). Also, any CDATA text nodes will be transparently merged with any adjacent "normal" text nodes. A DOM without such information is a lot easier to work with; your code won't need to handle special cases that come from storing such data. It will also need somewhat less memory and take less time to construct the DOM tree.

To further streamline your data, override ignorableWhitespace() and discard whitespace characters. While such events won't always be available even for documents that include DTDs, discarding "ignorable" characters can save significant amounts of memory. The savings vary widely based on DTDs and documents; documents that use mostly elements with element content models (often, but not always, data-oriented DTDs) have the biggest savings. Space savings of ten percent aren't unreasonable and are coupled with some time savings for DOM tree construction, but such savings are highly data dependent. (You may be able to discard processing instructions, depending on your application.)

Discarding lots of the DOM data is so common that when you use JAXP to build a DOM tree, you can configure it to automatically discard some of the data. (Unfortunately, the default is to include all of that data. You might not even need to strip out the events yourself. That configuration information gets sent directly to the SAX handler code that builds the DOM, and you can usually use it directly without needing to subclass. Example 4-2, a modified version of the previous example, shows this less noisy setup.

Example 4-2. Converting SAX events to DOM, discarding noise (Crimson)

public Document SAX2DOM (String uri)
throws SAXException, IOException
{
    XmlDocumentBuilder	consumer;
    XMLReader		producer;

    consumer = new XmlDocumentBuilder ();
    consumer.setIgnoreWhitespace (true);

    producer = XMLReaderFactory.createXMLReader ();
    producer.setContentHandler (consumer);

    producer.parse (uri);
    return consumer.getDocument ();
}

4.4.3. Building a Partial DOM

Often an even better solution for working with DOM is not to build an entire org.w3c.dom.Document object. You can build just the individual subtrees you need, never paying memory for the rest. Unfortunately, the classes listed earlier are set up to build entire document objects, so they won't help. However, it's easy to use SAX events to assemble trees of DOM nodes.

Here's one way to do it. This example defines an interface that exposes an element type using a namespace URI and a local name. It also exposes an event handler method to call with a DOM subtree that holds only such elements and their children. In effect, DOM subtrees are streamed, rather than SAX events. Such a model could work well with documents that are huge but highly regular, if the subtrees were processed then immediately discarded to save memory. Such structures might represent a series of composite records built from database queries, for example.

Example 4-3 uses JAXP to bootstrap an empty DOM document, which is used as a factory to create DOM elements and text nodes. The factory should be used for attributes too, in a more complete example, and perhaps for processing instructions. Notice how the SAX document traversal exactly matches a walk over the DOM tree being constructed, and how the partial DOM tree serves as only the state that's needed. Also, that DOM handles namespaces slightly differently than SAX does. If you need to build DOM trees with SAX, your code doesn't need to be much more complicated than this (other than passing attributes along) unless you try to implement all the gingerbread ornamenting the data model exposed by DOM.

Example 4-3. Using SAX to stream DOM subtrees

import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

// a kind of event handler
interface DomListener
{
    public String getURI ();
    public String getLocalName ();
    public void processTree (Element tree) throws SAXException;
}

public class DomFilter extends DefaultHandler
{
    private Document	factory;
    private Element	current;
    private DomListener	listener;

    public DomFilter (DomListener l)
    { listener = l; }

    public void startDocument ()
    throws SAXException
    {
    // all this just to get an empty document;
    // we need one to use as a factory
    try {
        factory = DocumentBuilderFactory
        .newInstance ()
        .newDocumentBuilder ()
        .newDocument ();
    } catch (Exception e) {
        throw new SAXException ("can't get DOM factory", e);
    }
    }

    public void startElement (String uri, String local,
    String qName, Attributes atts)
    throws SAXException
    {
    // start a new subtree, or ignore
    if (current == null) {
        if (!listener.getURI ().equals (uri))
        return;
        if (!listener.getLocalName ().equals (local))
        return;
        current = factory.createElementNS (uri, qName);

    // Add to current subtree, descend.
    } else {
        Element	e;

        if ("".equals (uri))
        e = factory.createElement (qName);
        else
        e = factory.createElementNS (uri, qName);
        current.appendChild (e);
        current = e;
    }
    // NOTE:  this example discards all attributes!
    // They ought to be saved to the current element.
    }

    public void endElement (String uri, String local, String qName)
    throws SAXException
    {
    Node	parent;

    // ignore?
    if (current == null)
        return;
    parent = current.getParentNode ();

    // end subtree?
    if (parent == null) {
        current.normalize ();
        listener.processTree (current);
        current = null;

    // else climb up one level
    } else
        current = (Element) current.getParentNode ();
    }

    // if saving, append and continue
    public void characters (char buf [], int offset, int length)
    throws SAXException
    {
    if (current != null)
        current.appendChild (factory.createTextNode (
        new String (buf, offset, length)));
    }
}

You can use similar techniques to construct other kinds of data structures and to perform more interesting filter functions. For example, perhaps more than one element type is interesting, or some types of elements should be reported through different event handler callbacks. It's also easy to transform the data as you read it; the DOM trees you construct don't need to match the document structure that the parser reports.

4.4.4. Turning SAX Events into Custom Data Structures

If your application data structure or interchange syntax is already defined, you may not be able to unmarshal it using software based on the numerous schema-oriented tools. However, lots of software uses SAX to do this efficiently. Once you understand how SAX models data in XML documents, you can treat unmarshaling much like any other parsing problem. It's closely associated with marshaling your data structures to XML. Here we'll look at some of the issues you may want to consider when transforming XML into your data structures.

You may find that some individual data items, such as integers and dates, use the low-level encoding rules that are specified in Part 2 of the W3C XML Schema specification (http://www.w3c.org/TR/xmlschema-2/). Those encodings are low-level policy decisions, and they're conceptually independent of the rest of the W3C Schema; you can use them even if you don't buy the W3C approach to those schemas. Some other schema systems, such as Relax-NG, incorporate those low-level encoding policies without adopting more problematic parts of the W3C XML Schema specification. Your application might likewise want to use these policies.

One basic high-level encoding issue is how closely the XML structures and application structures should match. For example, an element will be easier to unmarshal by mapping its attributes (or child elements) directly to properties of a single application object rather than by mapping them to properties of several different objects. The latter design is more complex, and for many purposes it could be much more appropriate, but such unmarshaling code needs more complex state.

Regularity of the various structures is another issue. It's usually less work to handle regular structures, since it's easy to create general methods and reuse them. Bugs are less frequent and more easily found than when every transformation involves yet another special case.

You'll need to figure out how much state you need to track and what techniques you will use. You might be able to use extremely simple parsing state machines; one of these is shown later, in Example 6-2. Sometimes it might easier to unmarshal fragments into an intermediate form (as in the DOM subtrees example earlier), and map that form to your application structure before discarding them.

Often some sort of recursive-descent parsing algorithm that explicitly tracks the state of your parsing activities will be useful. It will often be helpful to keep a stack of pending elements and attributes, as shown later (in Example 5-1). But since the XML structures might not map directly to your application structures, you might also need to stack objects you're in various stages of unmarshaling.

The worst scenario is when neither the XML text nor the application data structures are very regular. Software to work with that kind of system quickly gets fragile as it grows, and you'll probably want to change some of your application constraints.

Previous | Home | Next
4.3. Exposing DTD Information | Book Index | 4.5. XML Pipelines
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Pipelines (SAX2)

 SAX2Search this book

4.5. XML Pipelines

In Chapter 2, "Introducing SAX2", Section 2.2.3, "XMLWriter: an Event Consumer" briefly discussed the concept of an XML pipeline. In that simple case, it involved reading, transforming, and then writing XML text. This concept is a powerful model for working with SAX; it is the natural framework for developing SAX components. These components won't usually be JavaBeans-style components, intended for use with graphical code builder tools, but they will still be specialized and easily reusable.

Exactly what is a SAX event pipeline? It's a series of components, each a pipeline stage connected so consumers act as producers for the next stage, as shown in Figure 4-1. The components pass events through, perhaps changing them on the fly to filter, reorganize, augment, or otherwise transform the data as it streams through. (The term filter is sometimes used to mean the same thing as a stage, though it's only one type of role for a stage.) The first producer could be a parser, or some other program component. The last consumer will probably have some defined output, such as XML text (XMLWriter), a DOM document (using the classes shown earlier), or an application-specific data structure. Intermediate stages in the pipeline have at least one pipeline stage as output, and they might produce other outputs such as data structures. Or they might only be used to analyze or condition the inputs to later stages.

Figure 4-1. SAX2 event pipeline

Pipeline stages can be used to create functional layers, or they can simply be used to define clean module boundaries. Some stages may work well with fragments of XML, while others may expect to process entire documents. The order in which processing tasks occur could be critically important or largely incidental. Stages can be application specific or general purpose. In addition to reading and writing XML, examples of such general-purpose stages include:

  • Cleaning up namespace information to re-create prefix declarations and references, replace old URIs with current ones, or give unqualified names a namespace.

  • Performing XSLT transformations.

  • Validating against an appropriate DTD or schema.

  • Transforming input text to eliminate problematic character representations. (Several recent W3C specifications require using Unicode Normalization Format C.)

  • Supporting the xml:base model for determining base URIs.

  • Passing data through pipeline stages on remote servers.

  • Implementing XInclude or similar replacements for DTD-based external entity processing.

  • Performing well-formedness tests to guard against sloppy producers (parsers won't need this).

More application-specific pipeline stages might include:

  • Performing validation using procedural logic with access to system state.

  • Collecting links, to support tasks such as verifying they all work.

  • Unmarshaling application-specific data structures.

  • Stripping out data that later processing must never see. For example, SOAP 1.1 messages must never include processing instructions or DTDs, and some kinds of XHTML rendering engines must not see <font> tweaks.

This process is different from how a work flow is managed in a data structure API such as DOM. In both cases you can assemble work-flow components, with intermediate work products represented as data structures. With SAX, those work-flow components would be pipelines; pipeline stages wouldn't necessarily correspond to individual work-flow components, although they might. With a data structure API, the intermediate work products must always use that API; with SAX they can use whatever representation is convenient, including XML text or a specialized application data structure.

Beyond defining the event consumer interfaces and how to hook them up to XML parsers, SAX includes only limited support for pipelines. That is primarily through the XMLFilterImpl class. The support is limited in part because XMLFilterImpl doesn't provide full support for the two extension handlers so that by default it won't pass enough of the XML Infoset to support some interesting tasks (including several in the previous lists).

In the rest of this section we talk about that class, XSLT and the javax.xml.transform package, and about a more complete framework (the gnu.xml.pipeline package), to illustrate one alternative approach.

You might also be interested in the pipeline framework used in the Apache Cocoon v2 project. Cocoon is designed for managing large web sites based on XML. One difference between the current Cocoon pipeline framework and the GNU pipeline framework is that Cocoon excludes the two SAX DTD-handling interfaces, making Cocoon pipelines unsuitable for tasks that need such DTD information. (Examples include DTD-based validation and parts of the XML Base URI specification that require detection of external entity boundaries.) At this writing, Cocoon 2.0 has just shipped its first stable release, ending its beta cycle.

4.5.1. The XMLFilterImpl Class

The XMLFilterImpl class is new in SAX2, though a similar layer was in use on top of SAX1 parsers. Think of this class as a hybrid between an event consumer and an event producer, which can be used in either mode:

  • In its event consumer role, it's a base class that forwards events to another consumer. Callers push events through the filter, which postprocesses them. Subclasses would normally override methods for those events and invoke the superclass methods when they choose to pass them on (after postprocessing the data to be reported).

  • In its event producer role, it's a specialized XMLReader that registers itself as the consumer for a parent reader and delegates parsing to that parent. Callers pull data through the filter by calling parse(); it looks like a SAX parser that preprocesses Infoset data before reporting it.

When you subclass XMLFilterImpl, you'll primarily be concerned with its role as an event consumer because you'll be writing event handler code. The bulk of the work in a filter is event handling. When you need to filter DeclHandler or LexicalHandler events, it won't know how to handle them. You'll have to add code to handle those events; get the code to that SAX class, and follow the model used for ContentHandler support. The following code snippet shows how this is set up. It supports the producer side (parsing a document and automatically filtering its events). It also shows the consumer-side infrastructure, meaning events are normally passed through untouched, but subclasses will override methods to intercept events and change how they get handled:

public class ExtendedFilter extends XMLFilterImpl
    implements LexicalHandler, DeclHandler
{
    DeclHandler		declHandler;
    LexicalHandler	lexicalHandler;

    private static String	declID =
        "http://xml.org/sax/properties/declaration-handler";
    private static String	lexicalID =
        "http://xml.org/sax/properties/lexical-handler";

    public void setProperty (String uri, Object handler)
    throws SAXNotRecognizedException, SAXNotSupportedException
    {
    if (declID.equals (uri))
        declHandler = (DeclHandler) handler;
    else if (lexicalID.equals (uri))
        lexicalHandler = (LexicalHandler) handler;
    else
        super.setProperty (uri, handler);
    }

    // support producer mode operations
    public void parse (InputSource in)
    throws SAXException, IOException
    {
    XMLReader	parent = getParent ();

    if (parent != null) {
        parent.setProperty (declID, this);
        parent.setProperty (lexicalID, this);
    }
    super.parse (in);
    }

    // support consumer mode operations
    public void comment (char buf [], int offset, int length)
    throws SAXException
    {
    if (lexicalHandler != null)
        lexicalHandler.comment (buf, offset, length);
    }

    // ... likewise for other LexicalHandler and DeclHandler methods
}

When you're using such a filter just as a consumer, you'll have to register it as a handler for the event classes you're interested in, using methods like setContentHandler() as you would for any other event consumer. In such a case there's never any confusion about which XMLReader to use to parse since any filter component is only postprocessing.

When you use an XMLFilterImpl to produce events, you need to provide a parent parser, probably by using XMLFilter.setParent(). When you invoke parse(), the filter sets itself up to proxy all of the SAX core event handler methods (as shown earlier for one of the extension methods) as well as EntityResolver and ErrorHandler. You'll need to pay particular attention that you invoke the filter, instead of that "real" parser. It's easy to run into bugs that way, particularly if you're chaining multiple filters together. Although every filter stage has a parse() method, you only want to invoke it on the last postprocessing stage. It's easy to get confused about that.

Some XMLFilter implementations only operate in producer mode. That is unfortunate since it means that they only accept input like a parser; they can't be used to postprocess SAX events.

4.5.2. XMLFilter Examples

This book includes some examples that use XMLFilterImpl as a base class, supporting both filter modes:

  • Example 6-3 shows a custom handler interface, delivering application-specific unmarshaled data. This interface can be used either to postprocess or to preprocess SAX events, without additional setup.

  • Example 6-9 replaces processing instructions with the content of an included document so that downstream stages won't know about the substitution. When used to postprocess events, the handler may need to be set up with appropriate EntityHandler and ErrorHandler objects.

Sun is developing a "Multi-Schema Validator" engine, which uses SAX filters to implement validators for schema systems including RELAX (also called ISO RELAX), TREX, RELAX-NG (combining the best of RELAX and TREX), and W3C XML schemas. This work ties in to the org.iso_relax.verifier framework for validator APIs (at http://iso-relax.sourceforge.net), which also supports using SAX objects (such as filters and content handlers) that validate schemas.

If you're using RDDL (http://www.rddl.org) as a convention for associating resources with XML namespaces, you may find the org.rddl.sax.RDDLFilter class to be useful. It parses RDDL documents and lets you determine the various resources associated with namespaces, such as a DTD, a preferred CSS or XSLT stylesheet, or the schema using any of several schema languages. This is another "producer-mode only" filter.

4.5.3. The javax.xml.transform.sax Package

The javax.xml.transform APIs provide ways to apply XSLT transforms to XML data. The top level APIs work with the "pull" model, and map one XML representation into another one with a Transformer.transform(source,result) call. Those representations can include XML text, DOM trees, or some kinds of SAX event streams. Except for that SAX support, you can look at the package as supporting three-stage pipelines, with the middle stage always XSLT (or else a null transform). The javax.xml.transform.sax APIs let you integrate XSLT into longer SAX pipelines in several ways, including one flexible pure "push" mode.

The SAXTransformerFactory class is important for most such pipeline usage. You could use code like this to set up to get a factory matching the code fragments shown later:

import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.*;

String			stylesheetURI = ...;
String			documentURI = ...;
ContentHandler		contentHandler = ...;
LexicalHandler		lexicalHandler = ...;
TransformerFactory	tf
SAXTransformerFactory	stf;
SAXSource		stylesheet;

tf = TransformerFactory.newInstance ();
if (!tf.getFeature (SAXTransformerFactory.FEATURE)
    || !tf.getFeature (SAXSource.FEATURE))
    throw new Exception ("not enough API support");
stylesheet = new SAXSource (new InputSource (stylesheetURI));
stf = (SAXTransformerFactory) tf;

Most Java XSLT engines, such as SAXON (available at http://saxon.sourceforge.net) and Xalan (available at http://xml.apache.org/xalan-j) fully support the additional SAX-oriented APIs, although that is not required.

4.5.3.1. SAX in Push-Mode with XSLT

The approach that's most flexible involves a TransformerHandler initialized to apply a specific XSLT transform. These are event consumer stages, set up to push their results through to other stages. They support only the ContentHandler, LexicalHandler and DTDHandler interfaces, but not DeclHandler. This is best used in conjunction with the SAXResult class, which packages both non-DTD SAX handlers so they can collect the output of a transform. After getting the factory as shown in the preceding code, make sure it supports SAXResult, then get and use the handler in a manner such as the following:

XMLReader		producer;
SAXResult		out;
TransformerHandler	handler;

if (!tf.getFeature (SAXResult.FEATURE))
    throw new Exception ("not enough API support");

handler = stf.newTransformerHandler (stylesheet);
out = new SAXResult ();
out.setContentHandler (contentHandler);
out.setLexicalHandler (lexicalHandler);
// no DTD support from the SAXResult class!!
handler.setResult (out);

producer = XMLReaderFactory.createXMLREader ();
producer.setContentHandler (handler);
producer.setDTDHandler (handler);
producer.setProperty ("http://xml.org/sax/properties/lexical-handler",
    handler);

producer.parse (inputURI);

This style of usage is particularly well suited to XML pipelines. It's just a DTD-deprived pipeline stage, except that the output setup needs a non-SAX class. The reason that approach is particularly useful for pipeline processing is that both the input and output to the XSLT transform use SAX event streams, so it can easily be spliced between any two parts of an event pipeline. It also means you can use "push" mode event producers, which invoke SAX callbacks directly.

4.5.3.2. SAX in Pull-Mode with XSLT

You can also get an pull-style API, using an XMLFilter that is initialized to apply a specific XSLT tran form. Such filters may be used as event producers, only at one end of a SAX pipeline. After getting the factory as shown in the previous code listing, you would make sure it supports this functionality, then get and use the filter like this.

XMLFilter    producer;

if (!tf.getFeature (SAXTransformerFactory.FEATURE_XMLFILTER))
    throw new Exception ("not enough API support");

producer = stf.newXMLFilter (stylesheet);
producer.setContentHandler (contentHandler);
producer.setProperty ("http://xml.org/sax/properties/lexical-handler",
    lexicalHandler);

producer.parse (inputURI);

Such a call would use the XSLT stylesheet to preprocess input to the handlers you provide. The SAXResult class, shown here, supports a similar processing model. If your transformer can accept one of those, a pull-mode Transformer.transform() call pushes preprocessed results into a ContentHandler and LexicalHandler, like the XMLFilter.parse() call.

You can also use SAX in a pull-mode Transformer.transform() call by using a SAXSource object. That lets you provide an InputSource (as shown earlier) as well as an XMLReader, which may be set up with a particular ErrorHandler and EntityResolver (not shown). To use that in a SAX event pipeline, you can make that reader be an XMLFilter that preprocesses the input to the XSLT transform.

You can combine both SAXSource and SAXResult objects to get a kind of "pull" mode pipeline including one XSLT transform stage, without even needing to use the SAXTransformerFactory class. To get multiple XSLT transform stages without needing intermediate storage (XML text, a DOM tree, or so on), use the TransformerHandler class as shown earlier, postprocessing results through in a SAXResult. Or if you prefer, package an XMLFilter from a SAXTransformerFactory to preprocess data through a SAXSource that you provide to the Transformer.transform() call. (I recommend sticking to the pure TransformerHandler approach, since it's not as confusing.)

4.5.4. The gnu.xml.pipeline Framework

This framework takes a different approach to building pipelines than XMLFilterImpl or XMLFilter. Two key characteristics are its built-in support for all the SAX2 handlers, including the extension handlers, and its exclusive focus on the postprocessing model. In addition, it has several utility filters and some factory methods that can automate construction and initialization of pipelines. The core interface is EventConsumer:

public interface EventConsumer
{
    public ContentHandler getContentHandler ();
    public DTDHandler getDTDHandler ();

    public Object getProperty (String id)
    throws SAXNotRecognizedException;

    public void setErrorHandler (ErrorHandler handler);
}

With that interface, pipelines are normally set up beginning with the last consumer and then working toward the first consumer. There is a formal convention that states pipeline stages have a constructor that takes an EventConsumer parameter, which is used to construct pipelines from simple textual descriptions (which look like Unix-style command pipelines). That convention makes it easy to construct a pipeline by hand, as shown in the following code. Stages are strongly expected to share the same error handling; the error handler is normally established after the pipeline is set up, when a pipeline is bound to an event producer.

There is a class that corresponds to the pure consumer mode XMLFilterImpl, except that it implements all the SAX2 event consumer interfaces, not just the ones in the core API. LexicalHandler and DeclHandler are fully supported. This class also adds convenience methods such as the following:

public class EventFilter
    implements EventConsumer, ContentHandler, DTDHandler,
        LexicalHandler, DeclHandler
{
    ... lots omitted ...

    // hook up all event consumer interfaces to the producer
    // map some known EventFilters into XMLReader feature settings
    public static void bind (XMLReader producer, EventConsumer consumer)
    { /* code omitted */ }

    // wrap a "consumer mode" XMLFilterImpl
    public void chainTo (XMLFilterImpl next)
    { /* code omitted */ }

    ... lots omitted ...
}

Example 4-4 shows how one simple event pipeline works using the GNU pipeline framework. It looks like it has three pipeline components (in addition to the parser), but in this case it's likely that two of them will be optimized away into parser feature flag settings: NSFilter restores namespace-related information that is discarded by SAX2 parser defaults (bind() sets namespace-prefixes to true and discards that filter), and ValidationFilter is a layered validator that may not be necessary if the underlying parser can support validation (in which case the validation flag is set to true and the filter is discarded). Apart from arranging that validation errors are reported and using the GNU DOM implementation instead of Crimson's, this code does exactly what the first SAX-to-DOM example above does.[22]

[22]There is a generic DomConsumer class that bootstraps using whatever JAXP sets up as the default DOM. Such a generic consumer can't know the implementation-specific back doors needed to implement all the bells and whistles DOM demands.

Example 4-4. SAX events to DOM document (using GNU DOM)

import gnu.xml.pipeline.*;

public Document SAX2DOM (String uri)
throws SAXException, IOException
{
    DomConsumer	consumer;
    XMLReader	producer;

    consumer = new gnu.xml.dom.Consumer ();
    consumer = new ValidationConsumer (consumer);
    consumer = new NSFilter (consumer);

    producer = XMLReaderFactory.createXMLReader ();
    producer.setErrorHandler (new DefaultHandler () {
    public void error (SAXParseException e)
    throws SAXException
        { throw e; }
    });
    EventFilter.bind (producer, consumer);

    producer.parse (uri);
    return consumer.getDocument ();
}

There are some interesting notions lurking in this example. For instance, when validation is a postprocessing stage, it can be initialized with a particular DTD and hooked up to an XMLReader that walks DOM nodes. That way, that DOM content can be incrementally validated as applications change it. Similarly, application code can produce a SAX event stream and validate content without saving it to a file. This same postprocessing approach could be taken with validators based on any of the various schema systems.

There are a variety of other utility pipeline stages and support classes in the gnu.xml.pipeline package. One is briefly shown later (in Example 6-7). Others include XInclude and XSLT support, as well as a TeeConsumer to send events down two pipelines (like a tee joint used in plumbing). This can be useful to save output for debugging; you can write XML text to a file, or save it as a DOM tree, and watch the events that come out of a particular pipeline stage to find problematic areas.

Even if you don't use that GNU framework, you should keep in mind that SAX pipeline stages can be used to package significant and reusable XML processing components.

Previous | Home | Next
4.4. Turning SAX Events into Data Structures | Book Index | 5. Other SAX Classes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Other SAX Classes (SAX2)

 SAX2Search this book

Chapter 5. Other SAX Classes

Contents:

Helper Classes
SAX1 Support

The preceding chapters have addressed all of the most important SAX2 classes and interfaces. You may need to use a handful of other classes, including simple implementations of a few more interfaces and SAX1 support. This chapter briefly presents those remaining classes and interfaces.

Your parser distribution should have SAX2 support, with complete javadoc for these classes. Consult that documentation if you need more information than found in this book. The API summary in Appendix A, "SAX2 API Summary" should also be helpful.

5.1. Helper Classes

There are several classes in the org.xml.sax.helpers package that you will probably find useful from time to time.

5.1.1. The AttributesImpl Class

This is a general-purpose implementation of the SAX2 Attributes interface. As well as reading attribute information (as defined in the interface), you can write and modify it. This class is quite handy when your application code is producing SAX2 events, perhaps because it is converting data structures to a SAX event stream.

Remember the attributes provided to the ContentHandler.startElement() event callback are only valid for the duration of that call. If you need a copy of those attributes for later use, it's simplest to use this class; just create a new instance using the copy constructor. That copy constructor is one of the most widely used APIs in this class, other than the Attributes methods.

It's often handy to keep a stack around to track the currently open elements and attributes. If you support xml:base, you'll also want to track base URIs for the document and for any external parsed entities. This is easy to implement using another key method provided by this class, addAttribute(). Example 5-1 shows how to maintain such a stack with xml:base support. It shows full support for XML namespaces, unlike Example 2-2, which is simple and attribute-free (shown in Chapter 2, "Introducing SAX2" in Section 2.3, "Basic ContentHandler Events").

Example 5-1. Maintaining an element and attribute stack

import java.io.IOException;
import java.net.URL;
import java.util.Hashtable;
import org.xml.sax.*;
import org.xml.sax.ext.*;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;

public class XStack extends DefaultHandler
    implements LexicalHandler, DeclHandler
{
    static class StackEntry
    {
    final String	 nsURI, localName;
    final String	 qName;
    final Attributes atts;
    final StackEntry parent;

    StackEntry (
        String namespace, String local,
        String name,
        Attributes	attrs,
        StackEntry	next
    ) {
        this.nsURI = namespace;
        this.localName = local;
        this.qName = name;
        this.atts = new AttributesImpl (attrs);
        this.parent = next;
    }
    }

    private Locator		locator;
    private StackEntry		current;
    private Hashtable		extEntities = new Hashtable ();

    private static final String	xmlNamespace
    = "http://www.w3.org/XML/1998/namespace";

    private void addMarker (String label, String uri)
    throws SAXException
    {
    AttributesImpl	atts = new AttributesImpl ();

    if (locator != null && locator.getSystemId () != null)
        uri = locator.getSystemId ();

    // guard against InputSource objects without system IDs
    if (uri == null)
        throw new SAXParseException ("Entity URI is unknown", locator);

    // guard against illegal relative URIs (Xerces)
    try { new URL (uri); }
    catch (IOException e) {
        throw new SAXParseException ("parser bug: relative URI",
                     locator);
    }

    atts.addAttribute (xmlNamespace, "base", "xml:base", "CDATA", uri);
    current = new StackEntry ("", "", label, atts, current);
    }

    // walk up stack to get values for xml:space, xml:lang, and so on
    public String getInheritedAttribute (String uri, String name)
    {
    String		retval = null;
    boolean		useNS = (uri != null && uri.length () != 0);

    for (StackEntry here = current;
        retval == null && here != null;
        here = here.parent) {
        if (useNS)
        retval = here.atts.getValue (uri, name);
        else
        retval = here.atts.getValue (name);
    }
    return retval;
    }

    // knows about XML Base recommendation, and xml:base attributes
    // can be used in callbacks for elements, PIs, comments,
    // characters, ignorable whitespace, and so on.
    public URL getBaseURI ()
    throws IOException
    {
    return getBaseURI (current);
    }

    private URL getBaseURI (StackEntry here)
    throws IOException
    {
    String		uri = null;

    while (uri == null && here != null) {
        uri = here.atts.getValue (xmlNamespace, "base");
        if (uri != null)
        break;
        here = here.parent;
    }

    // marker for document or entity boundary?  absolute.
    if (here.qName.charAt (0) == '#')
        return new URL (uri);

    // else it might be a relative uri.
    int		offset = uri.indexOf (":/");

    if (offset == -1 || uri.indexOf (':') < offset)
        return new URL (getBaseURI (here.parent), uri);
    else
        return new URL (uri);
    }

    // from ContentHandler interface
    public void startElement (
    String		namespace,
    String		local,
    String		name,
    Attributes	attrs
    ) throws SAXException
    { current = new StackEntry (namespace, local, name, attrs,
                   current); }

    public void endElement (String namespace, String local, String name)
    throws SAXException
    { current = current.parent; }

    public void setDocumentLocator (Locator l)
    { locator = l; }

    public void startDocument ()
    throws SAXException
    { addMarker ("#DOCUMENT", null); }

    public void endDocument ()
    { current = null; }

    // DeclHandler interface

    public void externalEntityDecl (String name, String publicId,
           String systemId)
    throws SAXException
    {
    if (name.charAt (0) == '%')
        return;
    // absolutize URL
    try {
        URL	url = new URL (locator.getSystemId ());
        systemId = new URL (url, systemId).toString ();
    } catch (IOException e) {
        // what could we do?
    }
    extEntities.put (name, systemId);
    }

    public void elementDecl (String name, String model) { }
    public void attributeDecl (String element, String name,
            String type, String mode, String defaultValue) {}
    public void internalEntityDecl (String name, String value) { }

    // LexicalHandler interface
    public void startEntity (String name)
    throws SAXException
    {
    String	uri = (String) extEntities.get (name);
    if (uri != null)
        addMarker ("#ENTITY", uri);
    }

    public void endEntity (String name)
    throws SAXException
    { current = current.parent; }

    public void startDTD (String root, String publicId, String systemId) {}
    public void endDTD () {}
    public void startCDATA () {}
    public void endCDATA () {}
    public void comment (char buf[], int off, int len) {}
}

With such a stack of attributes, it's easy to find the current values of inherited attributes like xml:space, xml:lang, xml:base, and their application-specific friends. For example, an application might have a policy that all unspecified attributes with #IMPLIED default values are inherited from some ancestor element's value or are calculated using data found in such a context stack.

Notice how this code added marker entries on the stack with synthetic xml:base attributes holding the true base URIs for the the document and external general entities. That information is needed to correctly implement the recommendation, and lets the getBaseURI() work entirely from this stack. If you need such functionality very often, you might want to provide a more general API, not packaged as internal to one handler implementation.

5.1.2. The LocatorImpl Class

This is a general-purpose implementation of the Locator interface. As well as reading location properties (as defined in the interface), you can write and modify them. It's part of SAX1 and is still useful in SAX2.

The locator provided by the ContentHandler.setDocumentLocator() can be used during any event callback, but the values it returns will change over time. If you need a copy of those values for later use, it's simplest to use this class; just create a new instance using the copy constructor. More typically, you will pass the locator to the constructor for some kind of SAXException, or just save the current base URI to use with relative URIs you find in document (or attribute) content.

5.1.3. The NamespaceSupport Class

When your code needs to track namespaces or their prefixes, use this SAX2 class. One audience for this class is authors of XML parsers; that's probably not you. More likely you're writing code that, like XPath or W3C's XML schemas, needs to parse prefixed names when they're found in attribute values or element content; this class can help. Or you may be writing code to select or generate element or attribute name prefixes for output. (If you only need to put those names in element or attribute names, you should be able to package that work in an event filter component that postprocesses your output and ensures that its namespace content matches XML 1.0 rules.)

What this class does is maintain a stack of namespace contexts, in which each context holds a set of prefix-to-URI mappings; the contexts normally correspond to an element. This is the right model to use when you're writing an XML parser. If you try to use this class in a layer on top of a SAX2 parser, you'll notice a slight mismatch: all the prefix-mapping events for an element's namespace context precede the startElement() events for that element. That is, you'll need to create and populate new contexts before you see the element that signifies a new context.[23] One simple way to work around this is with a Boolean flag indicating whether a new context is active yet.

[23]This is true unless xmlns* attributes get reported with startElement(), and you only use that form of the prefix-mapping events.

To use this class with a SAX2 parser that's set to report namespace prefix mappings, you have to modify some of your ContentHandler callbacks to maintain that stack of contexts. This is done in much the same way as you produce those callbacks yourself:

  1. Instantiate a NamespaceSupport object using its default constructor (the only one). A good time to do this is when you start your event stream, at the ContentHandler.startDocument() event callback. When you do this, set a Boolean contextActive flag to false, so that you'll create a new context for the root element.

  2. When you get (or make) a ContentHandler.startPrefixMapping(prefix,uri) event, see if contextActive is true. If not, call pushContext() and set that flag to true. Then call declarePrefix(prefix,uri). (It returns false if you give it illegal inputs.)

  3. At the end of any ContentHandler.startElement() event, see if contextActive is true. If not, call pushContext(). Then set that flag to false, forcing any child elements' namespace declarations to create a new context.

  4. Finally, at the end of any ContentHandler.endElement() event, call popContext().

  5. Call reset() to forcibly reset all state before you reuse the class. Doing this at the end of the ContentHandler.endDocument() callback should work.

If you follow these rules, you can use processName() to interpret element and attribute names that you find according to the current prefix bindings, or you can use getPrefix() to choose a prefix given a particular namespace URI:

String [] processName(qName,parts,isAttribute)

Use this method to find the namespace name corresponding to a qualified element or attribute name (perhaps as found inside an attribute value or element content). Parameters are:

String qName

This is the qualified name, such as units:currency or fare, that is being examined.

String parts[3]

This is a three-element array. If this method succeeds in processing the name, the first array element will hold the namespace URI, the second will hold the local (unprefixed) name, and the third will hold the qName you passed in. The first and second string may also be empty strings, if the qName has no prefix and if no default namespace URI is applicable.

String isAttribute

Pass this value as true if the qName parameter identifies an attribute; otherwise, pass this as false. This information is needed because unprefixed element names are interpreted using any default namespace URI, but attribute names are not.

If this method succeeds, the parts parameter is filled out and returned. Otherwise the name includes a reference to an undeclared prefix, and null will be returned.

String getPrefix(String uri)

Use this method to choose a prefix to use when constructing a qualified name. This returns a currently defined prefix associated with the specified namespace URI or null if no such prefix is defined. When no such prefix is defined, the default namespace URI (associated with element names that have no prefixes) might still be appropriate. If so, then getURI("") will return this URI.

Consult the class documentation (javadoc) for full details about the methods on this class.

Previous | Home | Next
4.5. XML Pipelines | Book Index | 5.2. SAX1 Support
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX1 Support (SAX2)

 SAX2Search this book

5.2. SAX1 Support

This section provides a brief overview of the SAX1 classes and migration support and of differences between SAX1 and SAX2. SAX1 is a subset of SAX2, so SAX2 is backward compatible. The only reason you might not want to have the SAX2 classes and interfaces in your class path is to avoid compiler warnings telling you when you're using now-deprecated APIs.

You shouldn't be using SAX1 APIs to write new code, but you may need to maintain or migrate older code written using these classes. As soon as possible, plan a maintenance step that involves switching to the new SAX2 versions of the APIs. This may include getting rid of some "home-brew" solutions for namespace support. (Some applications have found previously unsuspected bugs when they've made such changes; be alert!) This section has been written to highlight those changes.

If your parser supplier hasn't provided SAX2 support by now, it's probably also time to switch suppliers; however, you can use the ParserAdapter class to make these changes without changing parsers. In fact, if you're using ParserFactory to get the system default parser and haven't set a SAX2 XMLReader default, the reference XMLReaderFactory distribution will automatically wrap the SAX1 parser you've probably already identified using the org.xml.sax.parser system property. That is, just putting the SAX2 classes in your class path normally lets you start using SAX2 without needing to change your application configuration. (You can go the other way around with an XMLReaderAdapter if you want to use a more current parser while letting the application code continue to use older SAX1 APIs.)

You'll most likely be interested in these classes if you're working with an older, SAX1-based application or tool, such as the XT 0.5 XSLT engine. This includes applications written to the JAXP 1.0 API specification, which doesn't include SAX2 support. If so, the main difference you'll see is that SAX1 has a much simpler way of naming elements and attributes: it only needs to support the qName (qualified name) access style, not the namespace-aware style. This eliminates some opportunities for confusion, unless you're writing namespace-aware applications.

The following classes provide SAX1 support:

org.xml.sax.Parser

This interface corresponds to the SAX2 XMLReader. It uses the DocumentHandler interface (instead of ContentHandler) and has no "getter" methods for handlers or the entity resolver. The SAX2 feature and property management methods are not available. There is a setLocale() method to control the locale used with diagnostics, which was dropped in SAX2.

With SAX1, there was no standard way to indicate whether a parser validated or not. SAX1 applications had to be written to not rely on having validity errors reported, unless either a configuration mechanism enforced the use of a validating parser (specifying validating or nonvalidating classes) or use of some specific implementation's alternative configuration mechanism was hardwired.

Similarly, SAX1 had no standard way to provide the additional infoset data that SAX2 shows using the DeclHandler and LexicalHandler interfaces. Applications needing such support needed to use implementation-specific APIs.

org.xml.sax.DocumentHandler

This interface corresponds to the SAX2 ContentHandler interface. Namespace information is not available on the element callbacks, and startElement() uses AttributeList. Prefix-mapping scopes are not reported. In SAX2, skipped entities are reported; this was an XML 1.0 conformance requirement that was not met by the SAX1 API. SAX1 will not report skipped entities even if you were to wire it into a SAX2 environment.

org.xml.sax.HandlerBase

This class corresponds to the SAX2 DefaultHandler class, except that it's a core class, not a helper class. (Consider that an evolutionary accident.) It supports the older DocumentHandler interface.

org.xml.sax.AttributeList

This interface corresponds to the SAX2 Attributes interface. It doesn't include namespace information and is accordingly much simpler. The only name for an attribute is what the namespace specification called the "qName." (In SAX2, providing the qName is optional unless the namespace-prefixes property has been set, but most parsers provide it at all times.)

org.xml.sax.helpers.AttributeListImpl

This class corresponds to the SAX2 AttributesImpl class. It doesn't include namespace information and is accordingly much simpler.

org.xml.sax.helpers.ParserAdapter

This class is intended to help migrate SAX1 parser implementations to the SAX2 namespace-aware API. If you have a SAX1 parser (perhaps it turns some non-XML data into a SAX1 event stream), you can use this class to bring it into the SAX2 world.

org.xml.sax.helpers.ParserFactory

This class corresponds to the SAX2 XMLReaderFactory class. It returns a SAX1 Parser and it is controlled only using the org.xml.sax.parser system property. It throws many more exceptions than its SAX2 analogue.

org.xml.sax.helpers.XMLReaderAdapter

This class supports backward migration of SAX2 parsers into SAX1-based applications. You probably won't ever need to use it.

If your environment supports SAX1 but not SAX2, you can just add the SAX2 version of sax.jar to your class path, somewhere before the older SAX1 files. (Otherwise, you might get package-sealing violations, because the JVM might mix versions of the package. It may be best if you remove older copies of the SAX1 classes from your class path.) If you set the SAX1 org.xml.sax.parser system property to point to a SAX1 parser so that applications can rely on org.xml.sax.helpers.ParserFactory bootstrapping, you'll be glad that the SAX2 org.xml.sax.helpers.XMLReaderFactory knows how to use this property as a backup in case no default SAX2 parser has been configured.

Previous | Home | Next
5. Other SAX Classes | Book Index | 6. Putting It All Together
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Putting It All Together (SAX2)

 SAX2Search this book

Chapter 6. Putting It All Together

Contents:

Rich Site Summary: RSS
XML and Messaging
Including Subdocuments

The preceding chapters have shown most of what you'll need to know to use SAX2 effectively, but as individual techniques, in small bits and pieces. In this chapter, we'll look at more substantial examples, which tie those techniques together. The examples here should help you to understand the kinds of modules you'll need to put together similar SAX2-based applications. You'll also see some of the options you have for building larger processing tasks from SAX components.

6.1. Rich Site Summary: RSS

One of the first popular XML document standards is hidden in the guts of web site management toolsets. It dates to back when XML wasn't fully crystallized. Back then, there was a lot of interest in using XML to address a widespread problem: how to tell users about updates to web sites so they didn't need to read the site several times a day. A "channel" based model was widely accepted, building on the broadcast publishers' analogy of a web site as a TV channel. Microsoft shipped an XML-like format called Channel Definition Format (CDF), and other update formats were also available, but the solution that caught on was from Netscape. It is called RSS. This originally stood for "RDF Site Summary,"[24] but it was simplified and renamed the "Rich Site Summary" format before it saw any wide adoption.

[24]RDF stands for Resource Description Framework. For more information, see http://www.w3.org/RDF/.

RSS 0.91 was the mechanism used to populate one of the earliest customizable web portals, My Netscape. The mechanism is simple: RSS presents a list of recently updated items from the web site, with summaries, as an XML file that could be fetched across the Web. Sites could update static summary files along with their content or generate them on the fly; site management tools could do either task automatically. It was easy for sites to create individualized views that aggregated the latest news from any of the numerous web sites providing RSS feeds.

There's essentially been a fork in the development of RSS. In recent surveys, about two thirds of the RSS sites use "RSS Classic," based on the 0.91 DTD and often with 0.92 extensions. (Mostly, the 0.92 spec removed limits from the non-DTD parts of the 0.91 spec.) Relatively recently, "New RSS" was created. Also called "RSS 1.0" (though not with the support of all the developers who had been enhancing RSS), this version is more complex. It uses RDF and XML namespaces and includes a framework with extension modules to address the complex content syndication and aggregation requirements of larger web sites. RSS toolkits tend to support both formats, but RDF itself is still not widely adopted. This is what part of one "RSS Classic" feed looks like, from the URL http://xmlhack.com/rss.php:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
   "http://my.netscape.com/publish/formats/rss-0.91.dtd">
<rss version="0.91">
<channel>
    <title>xmlhack</title>
    <link>http://www.xmlhack.com</link>
    <description>Developer news from the XML community</description>
    <language>en-us</language>
    <managingEditor>editor@xmlhack.com</managingEditor>
    <webMaster>webmaster@xmlhack.com</webMaster>
    <item>
       <title>BEEP implementation for .NET/C#</title>
       <link>http://www.xmlhack.com/read.php?item=1470</link>
    </item>
    <item>
      <title>MinML-RPC, Sandstorm XML-RPC framework</title>
      <link>http://www.xmlhack.com/read.php?item=1469</link>
    </item>
    <item>
      <title>XSLT as query language</title>
      <link>http://www.xmlhack.com/read.php?item=1467</link>
    </item>
    <item>
      <title>Exclusive XML Canonicalization in Last Call</title>
      <link>http://www.xmlhack.com/read.php?item=1466</link>
    </item>
   <!--many items were deleted for this example-->
</channel>
</rss>

In this section we use some of the techniques we've seen earlier and will look at both sides (client and server) of some simple RSS tools for RSS Classic. A full RSS toolset would need to handle New RSS, and would likely need an RDF engine to work with RDF metadata. Such RDF infrastructure should let applications work more with the semantics of the data, and would need RDF schema support. That's all much too complex to show here.[25]

[25] If you're interested the RDF approach, look at sites like the Open Directory Project, at http://www.dmoz.org/, to see one way of using RDF.

First we'll build a simple custom data model, then write the code to marshal and unmarshal it, and finally see how those components fit into common types of RSS applications. In a microcosm, this is what lots of XML applications do: read XML into custom data structures, process them, and then write out more XML.

6.1.1. Data Model for RSS Classic

Here are the key parts of the RSS 0.91 DTD; it also incorporates the HTML 4.0 ISO Latin/1 character entities, which aren't shown here, and various other integrity rules that aren't expressed by this DTD:

<!ELEMENT rss (channel)>
<!ATTLIST rss
          version     CDATA #REQUIRED> <!-- must be "0.91"> -->

<!ELEMENT channel (title | description | link | language | item+
        | rating? | image? | textinput? | copyright?
        | pubDate? | lastBuildDate? | docs? | managingEditor?
        | webMaster? | skipHours? | skipDays?)*>

<!ELEMENT image (title | url | link | width? | height? | description?)*>

<!ELEMENT item (title | link | description)*>

<!ELEMENT textinput (title | description | name | link)*>

<!ELEMENT title (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT link (#PCDATA)>
<!ELEMENT url (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT rating (#PCDATA)>
<!ELEMENT language (#PCDATA)>
<!ELEMENT width (#PCDATA)>
<!ELEMENT height (#PCDATA)>
<!ELEMENT copyright (#PCDATA)>
<!ELEMENT pubDate (#PCDATA)>
<!ELEMENT lastBuildDate (#PCDATA)>
<!ELEMENT docs (#PCDATA)>
<!ELEMENT managingEditor (#PCDATA)>
<!ELEMENT webMaster (#PCDATA)>
<!ELEMENT hour (#PCDATA)>
<!ELEMENT day (#PCDATA)>
<!ELEMENT skipHours (hour+)>
<!ELEMENT skipDays (day+)>

In short, the DTD includes a wrapper that gives the version, one channel with some descriptive data, and a bunch of items. RSS 0.92 changes it slightly. Data length limits (which a DTD can't describe) are removed, and a bit more. If you're working with RSS, you should know that most RSS feeds incorporate at least a few of those 0.92 extensions and have your code handle the issues. And if you're generating an RSS feed for your web site, you'll want to know that many aggregators present the image as the channel's icon, along with the newest items and the text input box, to provide quick access to your site.

When you work with XML-based systems and SAX, one of the first things you'll want to do is decide on the data structures you'll use. Sometimes you'll have a pre-existing data structure that must be matched; in cases like this RSS code, you have the luxury of a blank slate to write on. I'm a big believer in designing appropriate data structures, rather than expecting some development tool to come up with a good answer; as a rule, a good "manual" design beats code generator output in any maintainable system. In the case of RSS Classic, simple structures like those shown in Example 6-1 can do the job:

Example 6-1. RSS data structures

import java.util.Vector;

public class RssChannel {

    // (optional, not part of RSS) URI for the RSS file
    public String	sourceUri;

    // Five required items
    public String	description = "";
    public Vector	items = new Vector ();
    public String	language = "";
    public String	link = "";
    public String	title = "";

    // Lots of optional items
    public String	     copyright = "";
    public String	     docs = "";
    public RssImage	     image;
    public String	     lastBuildDate = "";
    public String	     managingEditor = "";
    public String	     pubDate = "";
    public String	     rating = "";
    // public Days		skipDays;
    // public Hours		skipHours;
    public RssTextInput	     textinput;
    public String	     webMaster = "";

    // channels have a bunch of items
    static public class RssItem
    {
    public String	description = "";
    public String	link = "";
    public String	title = "";
    }

    // Text input is used to query the channel
    static public class RssTextInput
    {
    public String	description = "";
    public String	link = "";
    public String	name = "";
    public String	title = "";
    }

    // Image used for the channel
    static public class RssImage
    {
    public String	link = "";
    public String	title = "";
    public String	url = "";

    // optional
    public String	description = "";
    public String	height = "";
    public String	width = "";
    }
}

Note that these classes didn't include any methods; methods can be added later, as application code determines what's really necessary. There are a variety of features that would be good to constrain this way, which you'll see if you look at the RSS specifications. Even pure "value objects" benefit from such internal consistency checks. For example, you may prefer to use beans-style accessor functions, but they would only complicate this example. (So would the class and field documentation, which has been deleted for simplicity.)

There's one type of code that is certainly needed but was intentionally put into different classes: marshaling data to RSS and unmarshaling it from RSS. Such choices are design policies; while it's good to keep marshaling code in one place, that place doesn't need to be the data structure class itself. It's good to separate marshaling code and data structure code because it's easier to support several different kinds of input and output syntax. Examples include different versions of RSS, as well as transfers to and from databases with JDBC. To display RSS in a web browser, different versions of HTML may be appropriate. Sometimes, embedding a stylesheet processing instruction into the XML text may be the way to go. Separate marshaling code needs attention when data structures change, but good software maintenance procedures will ensure that's never a problem.

6.1.2. Consuming and Producing RSS Parsing Events

Earlier chapters have touched on ways to marshal and unmarshal data with SAX. This section shows these techniques more completely, for a real-world application data model.

Example 6-2 shows what SAX-based unmarshaling code can look like, without the parser hookup. In this case it's set up to be the endpoint on a pipeline. This just turns infoset "atoms" into RSS "molecules" and stops. Note that it isn't particularly thorough in how it handles all the various types of illegal, or just unexpected, RSS that's found on the Web, although it handles many RSS Classic sites perfectly well. For example, the controls to skip fetches on particular days (perhaps weekends) or hours (nonbusiness hours) aren't usually supported, so they're just ignored here. With a more complex DTD, unmarshaling might not be able to rely on such a simple element stacking scheme; you might need to stack the objects you're unmarshaling and use a more complex notion of context to determine the appropriate actions to take.

Example 6-2. Unmarshaling SAX events into RSS data

import java.util.Stack;

import RssChannel.RssItem;
import RssChannel.RssImage;
import RssChannel.RssTextInput;

public class RssConsumer extends DefaultHandler {
    private RssChannel          channel;
    private RssItem             item;
    private RssImage            image;
    private RssTextInput        input;

    private Stack               stack;
    private Locator             locator;

    public RssChannel getChannel ()
    { return channel; }

    private String getCurrentElementName ()
    { return (String) stack.peek (); }

    // only need a handful of ContentHandler methods

    public void setDocumentLocator (Locator l)
    { locator = l; }

    public void startDocument () throws SAXException
    {
    channel = new RssChannel ();
    if (locator != null)
        channel.sourceUri = locator.getSystemId ();
    stack = new Stack ();
    }

    public void startElement (
    String		namespace,
    String		local,
    String		name,
    Attributes	attrs
    ) throws SAXException
    {
    stack.push (name);

    if ("item".equals (name))
        item = new RssItem ();
    else if ("image".equals (name))
        image = new RssImage ();
    else if ("textinput".equals (name))
        input = new RssTextInput ();
    // parser misconfigured?
    else if (name.length () == 0)
        throw new SAXParseException ("XML names not available", locator);
    }

    public void characters (char buf [], int off, int len)
    throws SAXException
    {
    String	top = getCurrentElementName ();
    String	value = new String (buf, off, len);

    if ("title".equals (top)) {
        if (item != null)
        item.title += value;
        else if (image != null)
        image.title += value;
        else if (input != null)
        input.title += value;
        else
        channel.title += value;
    } else if ("description".equals (top)) {
        if (item != null)
        item.description += value;
        else if (image != null)
        image.description += value;
        else if (input != null)
        input.description += value;
        else
        channel.description += value;
    } else if ("link".equals (top)) {
        if (item != null)
        item.link += value;
        else if (image != null)
        image.link += value;
        else if (input != null)
        input.link += value;
        else
        channel.link += value;

    } else if ("url".equals (top)) {
        image.url += value;

    } else if ("name".equals (top)) {
        input.name += value;

    } else if ("language".equals (top)) {
        channel.language += value;
    } else if ("managingEditor".equals (top)) {
        channel.managingEditor += value;
    } else if ("webMaster".equals (top)) {
        channel.webMaster += value;

    } else if ("copyright".equals (top)) {
        channel.copyright += value;
    } else if ("lastBuildDate".equals (top)) {
        channel.lastBuildDate += value;
    } else if ("pubDate".equals (top)) {
        channel.pubDate += value;
    } else if ("docs".equals (top)) {
        channel.docs += value;
    } else if ("rating".equals (top)) {
        channel.rating += value;

    } // else ignore ... skipDays and so on.
    }

    public void endElement (
    String		namespace,
    String		local,
    String		name
    ) throws SAXException
    {
    if ("item".equals (name)) {
        // patch item.link
        channel.items.addElement (item);
        item = null;
    } else if ("image".equals (name)) {
        // patch image.link
        // (patch image.url)
        channel.image = image;
        image = null;
    } else if ("textinput".equals (name)) {
        // patch input.link
        channel.textinput = input;
        input = null;
    } else if ("channel".equals (name)) {
        // patch channel.link
    }
    }
}

If you think in terms of higher-level parsing events, rather than in terms of data structures, you might want to define an application-level event handler interface and package your code as an XMLFilterImpl, as shown in Example 6-3. This is the "atoms into molecules" pattern for handlers, as sketched in Chapter 3, "Producing SAX2 Events". In the case of RSS, both item and channel might reasonably be expected to be "molecules" that get reported individually as application-level events. If you report finer grained structures (like item) it might be it easier to assemble higher-level data structures, but we won't show that here.

Example 6-3. Building SAX events into an RSS event handler

public interface RssHandler {
    void channelUpdate (RssChannel c) throws SAXException;
}

public class RssConsumer extends XMLFilterImpl {
    // ... as above (notice different base class!) but also:

    private RssHandler          handler;

    public static String        RssHandlerURI =
        "http://www.example.com/properties/rss-handler";

    public void setProperty (String uri, Object value)
    throws SAXNotSupportedException, SAXNotRecognizedException
    {
    if (RssHandlerURI.equals (uri)) {
        if (value instanceof RssHandler) {
        handler = (RssHandler) value;
        return;
        }
        throw new SAXNotSupportedException ("not an RssHandler");
    }
    super.setProperty (uri, value);
    }

    public Object getProperty (String uri)
    throws SAXNotSupportedException, SAXNotRecognizedException
    {
    if (RssHandlerURI.equals (uri))
        return handler;
    return super.getProperty (uri);
    }

    public void endDocument ()
    throws SAXException
    {
    if (handler == null)
        return;
    handler.channelUpdate (getChannel ());
    }
}

A filter written in that particular way can be used almost interchangeably with the handler-only class shown earlier in Example 6-2. In fact it's just a bit more flexible than that, though it may not be a good pipeline-style component. That's because it doesn't pass the low-level events through consistently; the ContentHandler methods this implements don't pass their events through to the superclass, but all the other methods do. That's easily fixed, but it's likely that you'd either want all the XML atoms to be visible (extending the XML Infoset with RSS-specific data abstractions) or none of them (and use an RSS-only infoset).

Example 6-4 shows what the core marshaling code can look like, without the hookup to an XMLWriter or the XMLWriter setup. For simplicity, this example takes a few shortcuts: it doesn't marshal the channel's icon description or most of the other optional fields. But notice that it does take care to write out the DTD and provide some whitespace to indent the text. (It uses only newlines for end-of-line; output code is responsible for mapping those to CRLF or CR when needed.) Also, notice that it just generates SAX2 events; this data could be fed to an XMLWriter, or to the RssConsumer class, or to any other SAX-processing component.

Example 6-4. Marshaling RSS data to SAX events

import java.util.Enumeration;
import org.xml.sax.*;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import RssChannel.RssItem;

public class RssProducer implements RssHandler
{
    private static char		lineEnd [] = { '\n', '\t', '\t', '\t' };
    private ContentHandler	content;
    private LexicalHandler	lexical;

    public RssProducer (ContentHandler n)
    { content = n; }

    public void setLexicalHandler (LexicalHandler l)
    { lexical = l; }

    private void doIndent (int n)
    throws SAXException
    {
    n++;	// NL
    if (n > lineEnd.length)
        n = lineEnd.length;
    content.ignorableWhitespace (lineEnd, 0, n);
    }

    private void element (int indent, String name, String val, Attributes
            atts)
    throws SAXException
    {
    char	contents [] = val.toCharArray ();

    doIndent (indent);
    content.startElement ("", "", name, atts);
    content.characters (contents, 0, contents.length);
    content.endElement ("", "", name);
    }

    public void channelUpdate (RssChannel channel)
    throws SAXException
    {
    AttributesImpl		atts = new AttributesImpl ();

    content.startDocument ();
    if (lexical != null) {
        lexical.startDTD ("rss",
        "-//Netscape Communications//DTD RSS 0.91//EN",
         "http://my.netscape.com/publish/formats/rss-0.91.dtd");
        lexical.endDTD ();
    }

    atts.addAttribute ("", "", "version", "CDATA", "0.91");
    content.startElement ("", "", "rss", atts);
    atts.clear ();
    doIndent (0);
    content.startElement ("", "", "channel", atts);

    // describe the channel
    // four required elements
    element (1, "title", channel.title, atts);
    element (1, "link", channel.link, atts);
    element (1, "description", channel.description, atts);
    element (1, "language", channel.language, atts);

    // optional elements
    if ("" != channel.managingEditor)
        element (1, "managingEditor", channel.managingEditor, atts);
    if ("" != channel.webMaster)
        element (1, "webMaster", channel.webMaster, atts);
    // ... and many others, notably image/icon and text input

    // channel contents: at least one item
    for (Enumeration e = channel.items.elements ();
        e.hasMoreElements ();
        /**/) {
        RssItem	item = (RssItem) e.nextElement ();
        doIndent (1);
        content.startElement ("", "", "item", atts);
        if ("" != item.title)
        element (2, "title", item.title, atts);
        if ("" != item.link)
        element (2, "link", item.link, atts);
        if ("" != item.description)
        element (2, "description", item.description, atts);
        doIndent (1);
        content.endElement ("", "", "item");
    }

    content.endElement ("", "", "channel");
    content.endElement ("", "", "rss");
    content.endDocument ();
    }
}

Since this code implements the RssHandler interface shown earlier, an instance of this class could be assigned as the RSS handler for the XMLFilter shown here. That could be useful if you wanted to round-trip RSS data. Round-tripping data can be a good way to test marshaling and unmarshaling code. You can create collections of input documents, and automatically unmarshal or remarshal their data. If you compare inputs and outputs, you can ensure that you haven't discarded any important information or added inappropriate text.

6.1.3. Building Applications with RSS

One of the most fundamental things you can do in an RSS application is act as a client: fetch a site's summary data and present it in some useful format. Often, your personal view of a web site is decorated with pages or sidebars that summarize the latest news as provided by other sites; they fetch RSS data, cache it, and reformat it as HTML or XHTML so your web browser shows it. That is, the web server acts as a client to RSS feeds and generates individualized pages that you can read on and click on the latest headlines.

Example 6-5 is a simple client that dumps its output as text. It's simple to write a servlet or JSP that does this for a set of RSS feeds, formatting them as nice XHTML sidebar tables so that a site's pages will be more useful.[26]

[26]If you do this in a server, you should handle one very important task that's not shown here: cache the RSS data! Do not make servers fetch the summary before each page view. That makes for a very slow user experience and can overload remote RSS feeds.

There are two basic techniques to use to create such a cache. One is to put a caching proxy between your server and all the RSS feeds. The other is to write a page cache module, preferably one that uses HTTP "conditional GET" (the If-Modified-Since HTTP header field) to avoid excess cache updates. You can save RssChannel data or store channel information in a local database, as variants of the page cache technique.

One extremely important point shown here is this code uses a resolver to force the use of a local copy of the RSS DTD. Servers should always use local copies of DTDs. Some RSS applications got a rude reminder of that fact in April 2001, when Netscape accidentally removed the DTD when it reorganized its web site. Suddenly, those badly written applications stopped working on many RSS feeds! Of course, those that were properly set up with local copies of that DTD had no problems at all.

Example 6-5. An RSS data dump

import gnu.xml.util.Resolver;
import java.io.File;
import java.util.Hashtable;
import org.xml.sax.*;
import org.xml.sax.helpers.XMLReaderFactory;
import RssChannel.RssItem;

public class RssMain
{
    private static String featurePrefix =
    "http://xml.org/sax/features/";

    // Invoke with one argument, a URI or filename
    public static void main (String argv [])
    {
    if (argv.length != 1) {
        System.err.println ("Usage: RssMain [file|URL]");
        System.exit (1);
    }

    try {
        XMLReader		reader;
        RssConsumer		consumer;
        Hashtable		hashtable;
        Resolver		resolver;

        reader = XMLReaderFactory.createXMLReader ();

        consumer = new RssConsumer ();
        reader.setContentHandler (consumer);

        // handle the "official" DTD server being offline
        hashtable = new Hashtable (5);
        hashtable.put (
        "-//Netscape Communications//DTD RSS 0.91//EN",
        Resolver.fileNameToURL ("rss-0_91.dtd"));
        resolver = new Resolver (hashtable);
        reader.setEntityResolver (resolver);

        // we rely on qNames, and 0.91 doesn't use namespaces
        reader.setFeature (featurePrefix + "namespace-prefixes", true);
        reader.setFeature (featurePrefix + "namespaces", false);

        argv [0] = Resolver.getURL (argv [0]);
        reader.parse (argv [0]);

        RssChannel		channel = consumer.getChannel ();

        System.out.println ("Partial RSS 0.91 channel info");
        System.out.println ("SOURCE = " + channel.sourceUri);
        System.out.println ();

        System.out.println ("         Title: " + channel.title);
        System.out.println ("   Description: " + channel.description);
        System.out.println ("          Link: " + channel.link);
        System.out.println ("      Language: " + channel.language);
        System.out.println ("     WebMaster: " + channel.webMaster);
        System.out.println ("ManagingEditor: "
                   + channel.managingEditor);
        System.out.println ();

        System.out.println ("    Item Count: " + channel.items.size ());
        for (int i = 0; i < channel.items.size (); i++) {
        RssItem		item = (RssItem)
                    channel.items.elementAt (i);
        System.out.println ("ITEM # " + i);
        if (item != null) {
            System.out.println ("         Title: " + item.title);
            System.out.println ("   Description: "
                           + item.description);
            System.out.println ("          Link: " + item.link);
        }
        }

    // Good error handling is not shown here, for simplicity
    } catch (Exception e) {
        System.err.println ("Whoa: " + e.getMessage ());
        System.exit (1);
    }
    System.exit (0);
    }
}

Besides servlets that present RSS data in HTML form to a web site's clients, another kind of servlet is important in the world of RSS applications: servlets that deliver a site's own RSS feed as XML. Servers often arrange that the current channel data is always ready to serve at a moment's notice. You've probably worked with sites that give you HTML forms to publish either short articles (web log entries or discussion follow-ups) or long ones (perhaps XML DocBook source that's then formatted). When such forms post data through a servlet, it's easy to ensure the servlet updates the site's RSS channel data when it updates other site data for those articles.

While the mechanics of such a servlet would be specific to the procedures used at a given web site, almost any site could use code like that in Example 6-6 to actually deliver the RSS feed. Notice the XML text is delivered with an encoding that any XML parser is guaranteed to handle, using CRLF-style line ends (part of the MIME standard for text/* content types), and this sets the Last-Modified HTTP timestamp so it supports HTTP caches based on either "conditional GET" or on explicit timestamp checks with the HEAD request.

Example 6-6. Servlet generating RSS data

import gnu.xml.util.XMLWriter;
import javax.servlet.http.*;

// a "Globals" class is used here to access channel and related data

public class RssGenServlet extend HttpServlet
{
    public void doGet (HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
    {
    RssProducer	producer;
    XMLWriter	consumer;

    response.addDateHeader ("Last-Modified", Globals.channelModified);

    response.setContentType ("text/xml;charset=UTF-8");
    consumer = new XMLWriter (response.getWriter ());
    consumer.setEOL ("\r\n");

    try {
        producer = new RssProducer (consumer);
        producer.setLexicalHandler (consumer);
        producer.channelUpdate (Globals.channel);
    } catch (SAXException e) {
        throw new ServletException (e);
    }
    }
}

As RSS 1.0 starts to become more widely supported and more RSS/RDF modules are defined, more clever RSS-based services will become available. For example, RSS aggregator services may begin to be able to dynamically create new channels with information filtered from many other channels. That is, you could be able to define a channel that the aggregator will fill with new articles on a particular topic, listed in any of several hundred RSS feeds. Today, you'd have to scan each feed yourself to do that. Such smarter services would also have better reasons to cache information. Today, such a service would have a hard time knowing which articles to remember while you were away on vacation, since there would be far too many articles to remember them all.

Previous | Home | Next
5.2. SAX1 Support | Book Index | 6.2. XML and Messaging
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML and Messaging (SAX2)

 SAX2Search this book

6.2. XML and Messaging

Most technologies that fueled the "Internet Revolution" of the past few years have been around in one form or another for decades; they were just inaccessible to the volumes of people that were able to use them with mass market web browsers. Some of those technologies are now being re-created: they are updated to work better in today's Internet, which is a larger and more varied world than the earlier versions they were born into. In this section we will look at why XML is an important part of the re-creation of messaging technologies and at some of the roles Java plays in this process. We also look at how lightweight SAX2-based infrastructure supports XML messaging over the Web without requiring developers to master new toolkits.

6.2.1. XML/Internet Versus Older Technologies

Many more developers work with web servers than have ever worked with Remote Procedure Call (RPC) or message-queuing technologies. However, the problem is largely unchanged: the core issue is still how to exchange messages reliably and securely with services operated by other organizations. The combination of XML and web-based messaging has several basic technical benefits compared to those earlier technology generations, especially most forms of RPC:

HTTP-based protocols have truly global reach

HTTP is in essence a text-based RPC protocol: clients issue requests to objects identified by web server URIs, and those servers dynamically compute the responses. Because it's text-based, HTTP can be (and is) easily supported by almost all programming languages. Because of HTTPS (HTTP over SSL, a security protocol), HTTP security has been at least as good as any available with commercial RPC services. HTTP/HTTPS is now the most ubiquitous and functional RPC transport in the world.

XML is a more accessible and extensible message-encoding technology

Previous technologies generally focused on binary-oriented technologies, which often rigidly defined the set of possible messages. In practice, most technologies were restricted to particular programming environments because developers needed an API toolkit to generate the correct binary data. XML has a clear win here since essentially every such environment supports text input and output. And unlike other encodings, XML doesn't impose any inherent policy on what such text means, which makes it more flexible. SAX is able to leverage that flexibility because it is data-structure agnostic. Much of the work in XML messaging is to establish and promote particular policies; SAX can support all the important ones.

The Internet biases toward larger, coarse-grained messages

Before the Internet, applications were optimized for private local area networks (LANs) or for low-speed, application-specific wide area networks (WANs). Neither optimization point is a good match for today's typical Internet link (56 kbps modem, or megabit links for some home use and most enterprises). Two key Internet issues are network latency and reliability. Using HTTP with XML provides an opportunity to develop newer systems using a design policy that works with the Internet rather than against it: use bigger messages, less often. This is the antithesis of many RPC systems, which bias toward constant exchange of small messages just like they were local procedure calls.

XML favors loose coupling

RPC-based systems were often developed to assume that clients and servers are in the same organization. Some even assumed only one vendor's product would be used. That is, developers often aimed for a monoculture and tended to characterize diversity as either a commercial threat, an inefficiency, a security problem, or just a support headache. Actually, diversity is a source of strength: human groups that are diverse are more adaptable and more resilient because they have more resources to draw on. Because XML messaging focuses on protocols and message formats, rather than vendor-specific implementations or APIs, it promotes diversity. That reduces inappropriate coupling and makes systems less vulnerable to the problems of any particular implementation.

In short, as the limitations of earlier messaging infrastructures became well known, organizations of all sizes were investing in new, web-based technology. Internet-savvy applications were developed with HTTP technology, and the flexibility of XML as well as its introduction to the web developer community, made it the inevitable choice for the most widely deployed messaging technologies.

While much of the current work is focused on business applications, notably business-to-business integration, that's hardly the only type of application it benefits. There's also interest in peer-to-peer (P2P) protocols built with XML. P2P is usefully viewed as just messaging policies for applications that have finally escaped from the "client or server" straitjacket. Now, essentially anyone can run a server and act as a publisher for information they have produced. These new publishing systems are most naturally built with the same XML and HTTP technologies adopted elsewhere.

Another interesting way to compare these models is that while the RPC model moves computations to where the data lives, the Web model moves the data to where the computation lives. That has been called the "representational state transfer" (REST) model. When code is downloaded, a third model can be said to come into effect. The design of distributed systems needs to balance among all these alternatives and not focus exclusively on any single model.

6.2.2. Roles for Java in XML Messaging

Since Java was the first true "Internet-integrated" programming environment and had XML support very early, it's no surprise that a huge amount of XML messaging work is done in Java. There are a variety of higher-level XML APIs and tools, all of which define particular messaging policies and frameworks. This book may seem somewhat iconoclastic in its perspective on such tools: many of them are overkill. Most applications will be fine without any of the heavier-weight items on the API smorgasbord (for any language!); a lighter meal will often be the healthier solution, even on an expense account budget. There's plenty of scope for innovative applications written without such toolkits, and it's easier to spread them if they don't depend on first deploying lots of complex new infrastructure.

From an interoperability perspective, the most interesting work is language-neutral development of protocols. Some such initiatives hide or limit use of XML, such as XML-RPC. Others, notably BEEP and SOAP, let applications provide their own payloads, although SOAP is usually coupled with synchronous RPC-style messaging and payloads using W3C XML schema and precluding full use of XML, such as DTDs. BEEP is a standards-track peer-to-peer Internet protocol, building on decades of community experience and supporting both synchronous and asynchronous messaging models. http://www.beepcore.org has a wealth of relevant information, including protocol specifications and toolkits in many languages including Java. And as presented in various parts of this book, it's easy to use HTTP/HTTPS directly with SAX; that approach is very lightweight. Many applications can define XML messages and pass them using HTTP without needing additional policies or APIs; it's only a small stretch to use SMTP and email queues if you need asynchronous queuing.

To develop lightweight XML-based applications, get a JDK, an HTTP/HTTPS servlet engine, an XML toolset with SAX2 support, and probably a relational database that you can access through JDBC. That's enough for quite a lot of web services. When you need to get beyond HTTP-centric models, look at protocol frameworks like BEEP, which has long had Java support. Remember to carefully document and review your XML messages and protocols and to keep that documentation current. That is important for maintaining your software, and such good practices will help uncover design bugs early in system life cycles, when they're easy to fix.

6.2.3. XML Messaging over HTTP with SAX2

HTTP is a request/response protocol, loosely called an "RPC transport." Strictly speaking, RPC touches on APIs in some programming language and makes them location transparent, but here we use the term in a broader request/response sense. HTTP has several operations, sent to a particular server port (typically 80 for nonencrypted HTTP) and directed to a particular URI. For the purposes of XML messaging, the most important HTTP operations are GET and POST.

HTTP's GET request asks the server to return the data associated with the request's URI, as modified by various header fields. Other than the request itself, this is a one-way data transfer, from server to client; the data is returned using MIME as a typed envelope. For the purposes of this book, that data is most interesting when it's XML text. Web browsers normally issue GET to retrieve documents, and in Java when you read data from a java.net.URL you are normally issuing a GET. In particular, when a client passes a URI to the SAX XMLReader.parse(uri) call, the call uses GET underneath. It's easy to dynamically generate XML content from Java servlets, as shown in Section 6.1.3, "Building Applications with RSS " earlier in this chapter.

HTTP's POST request is more interesting. POST is very similar in structure to GET, but it provides something that GET doesn't have: the request includes MIME-encoded data. (Again, that's most interesting when it's XML text.) That is, unlike GET, POST is a two-way data transfer; XML can be sent to the server as part of the request, as well as returned in the response. Another key difference is that GET is idempotent: clients are expected to reissue GET calls, which must not change significant server state. If you wanted to transfer money between bank accounts, POST is the call to use since it's expected to execute exactly once.

It's a bit messy to issue XML-in/XML-out POST requests from Java clients. We'll discuss how to do this in relatively pure SAX, but first let's look at this process using a SAX-friendly API library. No matter how you actually transfer this data, the real work of your application will be to turn the SAX events into application work. You'll likely connect code resembling this example to application-specific code that marshals and unmarshals custom data structures needed to do its work.

The gnu.xml.pipeline.CallFilter class packages the entire process as a pipeline stage, sending its input events as a POST request and parsing the POST response to produce output events. That makes it easy to use POST as a generic processing component. For example, in a batch processing scenario you might want to POST an XML file to a server and print its response. Such a server might schedule work as described in the particular document, and it could easily have access to resources or privileges unavailable to your client. This request can be issued programmatically or in some cases using a standard command-line tool.

Example 6-7 shows one way to send an XML file to a server and save its XML response as another file. As mentioned, the NSFilter class can be (and in this case, is) optimized away. It's just making sure the namespace prefix information in the event stream isn't missing anything important.

Example 6-7. Exchanging XML with a server (GNU pipeline version)

import gnu.xml.pipeline.*;
import gnu.xml.util.Resolver;
import org.xml.sax.*;
import org.xml.sax.helpers.XMLReaderFactory;

public class CallFile
{
    // argv [0] == in.xml (filename)
    // argv [1] == url for posting service
    public static void main (String argv [])
    {
    try {
        EventConsumer	         out;
        XMLReader		in;

        out = new TextConsumer (System.out);
        out = new NSFilter (out);
        out = new CallFilter (argv [1], out);
        out = new NSFilter (out);

        in = XMLReaderFactory.createXMLReader ();
        EventFilter.bind (in, out);

        in.parse (Resolver.fileNameToURL (argv [0]));

    } catch (Exception  e) {
        e.printStackTrace ();
        System.exit (1);
    }
    }
}

If you want to do the same thing without using that pipeline framework, you have more work to do. You'll be driving the java.net.URLConnection directly, ensuring the text encodings are correct. And you won't have a generic way to group all SAX handlers together; you'd need to create an analogue of gnu.xml.pipeline.EventConsumer or, as shown in Example 6-8, write code that knows the specific output class it's talking to.

Example 6-8. Exchanging XML with a server (SAX-only version)

import java.io.*;
import java.net.*;
import gnu.xml.util.Resolver;
import gnu.xml.util.XMLWriter;
import org.xml.sax.*;
import org.xml.sax.helpers.XMLReaderFactory;

public class CallFile
{
    // argv [0] == in.xml (filename)
    // argv [1] == url for posting service
    public static void main (String argv [])
    {
    try {
        XMLReader		in;
        Caller		caller;
        XMLWriter		out;

        out = new XMLWriter (System.out);
        caller = new CallWriter (new URL (argv [1]), out);

        in = XMLReaderFactory.createXMLReader ();
        in.setFeature (featurePrefix + "namespace-prefixes", true);
        bindAll (in, caller);

        in.parse (Resolver.fileNameToURL (argv [0]));

    } catch (Exception  e) {
        e.printStackTrace ();
        System.exit (1);
    }
    }

    private static void bindAll (XMLReader in, Object out)
    throws SAXException
    {
    if (out instanceof ContentHandler)
        in.setContentHandler ((ContentHandler) out);
    if (out instanceof DTDHandler)
        in.setDTDHandler ((DTDHandler) out);
    try {
        if (out instanceof DeclHandler)

        in.setProperty
            ("http://xml.org/sax/properties/
            declaration-handler", out);
    } catch (SAXNotRecognizedException e) { /* IGNORE */ }
    try {
        if (out instanceof LexicalHandler)
        in.setProperty
            ("http://xml.org/sax/properties/
            lexical-handler", out);
    } catch (SAXNotRecognizedException e) { /* IGNORE */ }
    }

    // print input to server
    // block till response
    // print output as XML text to stdout
    private static class CallWriter extends XMLWriter
    {
    private URL		target;
    private URLConnection	conn;
    private XMLWriter	next;

    CallWriter (URL url, XMLWriter out)
    {
        super ((Writer)null);
        target = url;
        next = out;
    }

    // Connect to remote object and set up to send it XML text
    public synchronized void startDocument () throws SAXException
    {
        try {
        conn = target.openConnection ();
        conn.setDoOutput (true);

        // "text/*" expects DOS-style EOL
        next.setEOL ("\r\n");
        conn.setRequestProperty ("Content-Type",
                "text/xml;charset=UTF-8");

        setWriter (new OutputStreamWriter (
            conn.getOutputStream (),
            "UTF8"), "UTF-8");

        } catch (IOException e) {
        fatal ("can't write (POST) to URI: " + target, e);
        }
        super.startDocument ();
    }

    // finish sending request
    // receive the POST response
    public void endDocument () throws SAXException
    {
        super.endDocument ();

        try {
        InputSource source = new InputSource
            (conn.getInputStream ());
        XMLReader   producer = XMLReaderFactory.createXMLReader ();
        String      encoding;

        producer.setFeature (featurePrefix +
            "namespace-prefixes", true);
        encoding = Resolver.getEncoding (conn.getContentType ());
        if (encoding != null)
            source.setEncoding (encoding);
        bindAll (producer, next);
        producer.parse (source);

        } catch (IOException e) {
        fatal ("I/O Exception reading response, "
            + e.getMessage (), e);
        }
    }
    }
}

In that example scenario, you might also be able to just use binary file I/O and trust that the inputs and outputs are actually XML. But in general, inputs won't be sitting in files, and output processing will involve more than creating a new file. Both the CallFilter and CallWriter classes shown here are structured to be reusable.

On the server side, it's also easy to handle POST. In fact, you've seen all you need to know already! We saw how to pull XML data out of the POST request using the XmlReader.parse(InputSource) method in Chapter 3, "Producing SAX2 Events", in Section 3.1.2.2, "Providing entity text". Writing XML data in the response works exactly like it does for a GET, as shown earlier in this chapter in Section 6.1.3, "Building Applications with RSS ". The main XML-specific issue is to handle the character encoding correctly, as shown in both of those examples. (UTF-8 is the safest over-the-wire encoding.) It's safe to use the application/xml MIME content type whenever you pass XML using HTTP, since there are fewer things that can (and will!) go wrong. You should also make sure to use CRLF-style line ends whenever you use a text/* MIME content type. You might want to pay attention to some servlet-specific issues, such as structuring your code to support connection keepalives or (less commonly) on-the-fly compression of response data.

In many cases it's probably good to have servlets' doPost() methods save input to persistent storage, so that some other thread can pick it up as work item, and then just use the response data to acknowledge the request. The client would collect any additional requests later, either when it polled or when the server called back to the client (with another POST). That approach avoids tying up connections for a long time and creates a framework whereby many component failures will be transparently recovered from. Using such an atomic transaction model correctly can let you avoid the need for transactional roll-back mechanisms to recover from common system failure modes.

Previous | Home | Next
6. Putting It All Together | Book Index | 6.3. Including Subdocuments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Including Subdocuments (SAX2)

 SAX2Search this book

6.3. Including Subdocuments

In XML, external parsed entities are used to merge one file into another. This mechanism is used to partition larger XML documents (such as this book) into smaller ones (such as this chapter). Such external entities aren't quite the same as actual XML documents. They do not have DTDs; they have zero or more top-level elements instead of exactly one; and they have text declarations at the top instead of XML declarations.[27]

[27]These might show only the text encoding <?xml encoding='Big5'?> is a legal text declaration. To be an XML declaration, it would need to include a version first, like version='1.0'; it's good practice is to include both. Documents that use encoding declarations with no version number cannot be opened as XML directly. They can only be included in XML documents by way of an entity.

Those entities are in some ways awkward to use. Some people don't like to use DTDs, and their tools might not let them declare and create references to such entities. In any case, DTDs add the requirement that such entities be declared in advance. When you're building big documents out of little ones, widely spreading such knowledge can be undesirable. It's often easier to keep a local reference accurate than to update the remote declarations it depends on. Also, documents nest inside others, and small changes nested inside one document could force updates to many DTDs if the document is included in several others. In short, external parsed entities aren't as easy or natural to use as the #include "filename" syntax widely known to C/C++ developers. This is often viewed as a problem.

The response is obvious: use some other part of XML syntax to define a more natural inclusion construct. There's a W3C draft called XInclude, which doesn't quite do this (in the most current draft). XInclude uses element syntax, which is fine, but it doesn't just define a simple and familiar inclusion mechanism. XInclude supports the XPointer superset of XPath to embed almost arbitrary fragments of XML text. In effect, W3C's XInclude is a generalized linking model, and one which depends on significant infrastructure. The model hasn't met with widespread acceptance, and in any case is too complex to use for an example here. That's really too bad; normal inclusion is a strict streaming model, ideal for implementing with SAX, and the model of including fragments is exotic pretty much everywhere except within the linking community.

Here we show how to implement a variant of XInclude, which can replace many uses of external entities because it doesn't use XPointer. To emphasize the difference, we'll use a different syntax:

<?XInclude http://www.example.com/data/included.xml?>
    <!-- instead of what XInclude uses: -->
<xi:include
    xmlns:xi="http://www.w3.org/2001/XInclude"
    href="http://www.example.com/data/included.xml"
    parse='xml'
    encoding='euc-jp'
    >
    content of xi:include is ignored,
    the whole element gets replaced
</xi:include>

This example highlights several different SAX2 mechanisms. It uses the XMLFilterImpl class in two different modes and pays careful attention to the data it passes through. The different modes are as follows:

  • The outer filter must be used as a mixed event producer and consumer, with access to the full stream of event data as well as any ErrorHandler and EntityResolver objects in use. If it's not used this way, it won't be correct; it's hard to know such things about a SAX Filter unless they are discussed in the class documentation.

    The outer filter proxies the Locator so that applications see the right event locations and base URIs. It usually forwards the events to the true recipient of the event stream, but it will also handle nested inclusions when they are sent from the inner filter.

  • The inner filter is used as a pure event consumer. It cooperates with the outer filter to keep the proxy working correctly, and is set up to strip out DTD-related events and forward the rest to the outer filter.

The code in Example 6-9 takes a few shortcuts but implements the essential inclusion functionality.

Example 6-9. XInclude processing instruction

import java.io.IOException;
import java.net.URL;
import java.util.Vector;
import org.xml.sax.*;
import org.xml.sax.ext.*;
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.helpers.XMLReaderFactory;

public final class XI extends XMLFilterImpl
implements LexicalHandler, Locator
{
    // Act as a proxy for whatever the current locator is.
    private Locator		locator;

    // to avoid circular inclusion
    private Vector		pending = new Vector (5, 5);

    private LexicalHandler	lexicalHandler;

    private static String	lexicalID =
        "http://xml.org/sax/properties/lexical-handler";

    public void setDocumentLocator (Locator l)
    {
    locator = l;
    super.setDocumentLocator (this);
    }

    public String getSystemId ()
    { return (locator == null) ? null : locator.getSystemId (); }
    public String getPublicId ()
    { return (locator == null) ? null : locator.getPublicId (); }
    public int getLineNumber ()
    { return (locator == null) ? -1 : locator.getLineNumber (); }
    public int getColumnNumber ()
    { return (locator == null) ? -1 : locator.getColumnNumber (); }

    // Inner Filter Class: manage the current locator,
    // and filter out events that would be incorrect to report
    private class Scrubber extends XMLFilterImpl implements LexicalHandler
    {
    Locator		savedLocator;
    LexicalHandler	next;

    Scrubber (Locator l, LexicalHandler n)
        { savedLocator = l; next = n; }

    // maintain proxy locator
    // only one startDocument()/endDocument() pair per event stream
    public void setDocumentLocator (Locator l)
        { locator = l; }
    public void startDocument ()
        { }
    public void endDocument ()
        { locator = savedLocator; }

    private void reject (String message) throws SAXException
        { throw new SAXParseException (message, locator); }

    // only the DTD from the base document gets reported
    public void startDTD (String root, String publicId, String systemId)
    throws SAXException
        { reject ("DTD: " + systemId); }
    public void endDTD ()
    throws SAXException
        { reject ("DTD"); }
    // ... so this should never happen
    public void skippedEntity (String name) throws SAXException
        { reject ("entity: " + name); }

    // since we rejected DTDs, only built-in entities can be reported
    public void startEntity (String name)
    throws SAXException
        { next.startEntity (name); }
    public void endEntity (String name)
    throws SAXException
        { next.endEntity (name); }

    // other lexical events cause no worries
    public void startCDATA () throws SAXException
        { next.startCDATA (); }
    public void endCDATA () throws SAXException
        { next.endCDATA (); }
    public void comment (char buf[], int off, int len)
        throws SAXException
        { next.comment (buf, off, len); }
    }

    // count is zero in the document prologue and epilogue
    private int		count;

    public void startElement (String u, String l, String q, Attributes a)
    throws SAXException
    { count++; super.startElement (u, l, q, a); }

    public void endElement (String u, String l, String q)
    throws SAXException
    { --count; super.endElement (u, l, q); }

    public void startDocument () throws SAXException
    { pending.addElement (locator.getSystemId ());
         super.startDocument (); }

    public void endDocument () throws SAXException
    { pending.clear (); super.endDocument (); }

    // handle  processing instructions
    public void processingInstruction (String target, String data)
    throws SAXException
    {
    if ("XInclude".equals (target)) {
        // this should do full XML base processing
        // instead we just handle relative and absolute URLs
        try {
        URL		url = new URL (getSystemId ());

        url = new URL (url, data.trim ());
        data = url.toString ();
        } catch (Exception e) {
        throw new SAXParseException (
            "XInclude, can't use URI: " + data, locator, e);
        }
        xinclude (data);
    } else
        super.processingInstruction (target, data);
    }

    // this might be called from startElement too
    private void xinclude (String uri)
    throws SAXException
    {
    XMLReader	helper;
    Scrubber	scrubber;

    if (count == 0)
        throw new SAXParseException (
            "XInclude, illegal location", locator);
    if (pending.contains (uri))
        throw new SAXParseException (
            "XInclude, circular inclusion", locator);

    // start with another parser acting just like us
    helper = XMLReaderFactory.createXMLReader ();
    helper.setEntityResolver (this);
    helper.setErrorHandler (this);

    // Set up the proxy locator and inner filter.
    scrubber = new Scrubber (locator, this);
    locator = null;
    scrubber.setContentHandler (this);
    helper.setContentHandler (scrubber);
    helper.setProperty (lexicalID, scrubber);

    // we INTEND to discard DTDHandler and DeclHandler events

    // Merge the included document, except its DTD
    try {
        pending.addElement (uri);
        helper.parse (uri);
    } catch (java.io.IOException e) {
        SAXParseException	err;
        ErrorHandler	handler;

        err = new SAXParseException (uri, locator, e);
        handler = getErrorHandler ();
        if (handler != null)
        handler.fatalError (err);
        throw err;
    } finally {
        pending.removeElement (uri);
    }
    }

    // LexicalHandler interface
    public void startEntity (String name)
    throws SAXException
    { if (lexicalHandler != null) lexicalHandler.startEntity (name); }

    public void endEntity (String name)
    throws SAXException
    { if (lexicalHandler != null) lexicalHandler.endEntity (name); }

    public void startDTD (String root, String publicId, String systemId)
    throws SAXException
    { if (lexicalHandler != null) lexicalHandler.startDTD (root, publicId,
              systemId); }

    public void endDTD () throws SAXException
    { if (lexicalHandler != null) lexicalHandler.endDTD (); }
    public void startCDATA () throws SAXException
    { if (lexicalHandler != null) lexicalHandler.startCDATA (); }
    public void endCDATA () throws SAXException
    { if (lexicalHandler != null) lexicalHandler.endCDATA (); }
    public void comment (char buf[], int off, int len) throws SAXException
    { if (lexicalHandler != null) lexicalHandler.comment (buf, off, len); }

    // so this works as a "consumer"
    public void setProperty (String uri, Object handler)
    throws SAXNotRecognizedException, SAXNotSupportedException
    {
    if (lexicalID.equals (uri))
        lexicalHandler = (LexicalHandler) handler;
    else
        super.setProperty (uri, handler);
    }

    // so this works as a "producer"
    public void parse (InputSource in)
    throws SAXException, IOException
    {
    XMLReader	parent = getParent ();

    if (parent != null)
        parent.setProperty (lexicalID, this);
    super.parse (in);
    }
}

The most significant shortcut in this code is that, to simplify the example, XML Base isn't supported. That's easily fixed using the technique shown earlier, in Example 5-1. Similarly, the namespace reporting and validation modes of the default parser are assumed to be OK; they should be copied or specified as part of this event consumer's API.

Merging SAX event streams from two different sources is quite simple, except for DTD-related information. One basic problem is structural: DTD events may be reported only at the beginning of a SAX event stream, and the chance to do that has been lost by the time an included document is processed. Another basic problem is semantic: the events from the two sources could easily conflict with each other. Neither of those problems can be solved with a pure stream processing model, unless the included documents use the same DTD as the base document. Accordingly, this example treats DTD events from included streams as errors.

The best way to use XML inclusions is with XML text that doesn't use DTDs, perhaps using "XML 1.0 plus Namespaces" rules to help assign meaning to individual elements and attributes. Eliminating DTDs means some important bits of the XML Infoset will be unavailable, such as the attribute-typing information that tells you which elements are used as IDs. If all the files in question are themselves well-formed XML documents with both version and encoding in any XML declarations (and without a DTD), they can easily be included without significant restrictions. Such an inclusion facility can be convenient in a variety of application contexts, such as template-driven document processing and other cases where it's important to build larger documents from smaller ones.

Previous | Home | Next
6.2. XML and Messaging | Book Index | A. SAX2 API Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX2 API Summary (SAX2)

 SAX2Search this book

Appendix A. SAX2 API Summary

Contents:

The org.xml.sax Package
The org.xml.sax.helpers Package
The org.xml.sax.ext Package

This appendix provides a quick reference to each of the SAX2 APIs presented in this book. It shows API signatures and provides a brief overview for each interface, class, and exception in alphabetical order.

Full documentation for these APIs is available for you to download or browse online at the SAX web site (http://sax.sourceforge.net/), and it should also be available with documentation for your SAX parser.

A.1. The org.xml.sax Package

The org.xml.sax package holds the interfaces and exceptions that are at the core of SAX, including some deprecated SAX1 APIs.

A.1.1. The AttributeList Interface

This SAX1 interface is not used in SAX2; the Attributes interface, which supports namespace identifiers, is used instead.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public interface AttributeList
{
    public int getLength();
    public String getType(int index);
    public String getValue(int index);
    // access to name info
    public String getName(int index);
    // access by XML 1.0-style names
    public String getType(String qName);
    public String getValue(String qName);
}

A.1.2. The Attributes Interface

This interface groups all the attributes associated with a given element in the ContentHandler.startElement() call. Attribute characteristics are frequently accessed using an indexed access model, though you can also determine the index, type, or value of an attribute given XML 1.0-style (qName) or namespace-style (uri, localName) versions of its name.

For more information, refer to Section 2.3.3, "The Attributes Interface " in Chapter 2, "Introducing SAX2".

public interface Attributes
{
    public int getLength();
    public String getType(int index);
    public String getValue(int index);
    // access to name info
    public String getQName(int index);
    public String getLocalName(int index);
    public String getURI(int index);
    // access by XML Namespace-style names
    public int getIndex(String uri, String localName);
    public String getType(String uri, String localName);
    public String getValue(String uri, String localName);
    // access by XML 1.0-style names
    public int getIndex(String qName);
    public String getType(String qName);
    public String getValue(String qName);
}

A.1.3. The ContentHandler Interface

This is the primary SAX2 handler interface, which is used in almost all applications.

For more information, refer to Section 2.3.4, "Essential ContentHandler Callbacks" and Section 2.6.4, "ContentHandler and Prefix Mappings", both in Chapter 2, "Introducing SAX2", as well as Section 4.1.1, "Other ContentHandler Methods " in Chapter 4, "Consuming SAX2 Events".

public interface ContentHandler
{
    // bookkeeping
    public void setDocumentLocator(Locator locator);
    public void startDocument() throws SAXException;
    public void endDocument() throws SAXException;
    // content events
    public void startElement(String uri, String localName, String qName,
        Attributes attributes)
    throws SAXException;
    public void endElement(String uri, String localName, String qName)
    throws SAXException;
    public void characters(char buf[], int offset, int length)
    throws SAXException;
    public void processingInstruction(String target, String data)
    throws SAXException;
    // extra info
    public void ignorableWhitespace(char buf[], int offset, int length)
    throws SAXException;
    public void startPrefixMapping(String prefix, String uri)
    throws SAXException;
    public void endPrefixMapping(String prefix) throws SAXException;
    public void skippedEntity(String name) throws SAXException;
}

A.1.4. The DocumentHandler Interface

This SAX1 interface is not used in SAX2; the ContentHandler interface, which reports namespace identifiers and scopes as well as skipped entities, is used instead.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public interface DocumentHandler
{
    // bookkeeping
    public void setDocumentLocator(Locator locator);
    public void startDocument() throws SAXException;
    public void endDocument() throws SAXException;
    // content events
    public void startElement(String qName, AttributeList attributes)
    throws SAXException;
    public void endElement(String qName) throws SAXException;
    public void characters(char buf[], int offset, int length)
    throws SAXException;
    public void processingInstruction(String target, String data)
    throws SAXException;
    // extra info
    public void ignorableWhitespace(char buf[], int offset, int length)
    throws SAXException;
}

A.1.5. The DTDHandler Interface

This interface is used to report information that is useful to some SGML-derived applications.

For more information, refer to Section 4.3.2, "The DTDHandler Interface " in Chapter 4, "Consuming SAX2 Events".

public interface DTDHandler
{
    public void notationDecl(String notationName,
        String publicId, String systemId)
    throws SAXException;
    public void unparsedEntityDecl(String entityName,
        String publicId, String systemId, String notationName)
    throws SAXException;
}

A.1.6. The EntityResolver Interface

This interface encapsulates a strategy for resolving public or system identifiers for parsed entities into data that a parser will read. It is commonly used to ensure that local copies of DTDs are used, instead of DTDs accessed across a network link that may be saturated or unavailable. It can resolve general entities, used to store non-DTD parts of a document in separate storage units.

For more information, refer to Section 3.4, "The EntityResolver Interface" in Chapter 3, "Producing SAX2 Events".

public interface EntityResolver
{
    public InputSource resolveEntity(String publicId,
    String systemId)
    throws SAXException, java.io.IOException;
}

A.1.7. The ErrorHandler Interface

This interface encapsulates a strategy for handling different kinds of errors. Parsers use its methods when reporting errors, and have default policies that are used if the application's strategy doesn't result in throwing an exception. Applications can benefit from sharing the same mechanism to report their own errors. Implementations typically use the problem's severity to choose first whether to emit a diagnostic, and then whether to throw the parameter (to terminate processing) or return (to continue processing).

For more information, refer to Section 2.5.2, "ErrorHandler Interface" in Chapter 2, "Introducing SAX2".

public interface ErrorHandler
{
    public void error(SAXParseException x) throws SAXException;
    public void fatalError(SAXParseException x) throws SAXException;
    public void warning(SAXParseException x) throws SAXException;
}

A.1.8. The HandlerBase Interface

This SAX1 class is not used in SAX2; the org.xml.sax.helpers.Defaulthandler class, which supports SAX2 features, is used instead.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public class HandlerBase
    implements EntityResolver, DTDHandler,
        DocumentHandler, ErrorHandler
{
    public HandlerBase();
    // DocumentHandler (SAX1)
    public void setDocumentLocator(Locator locator);
    public void startDocument() throws SAXException;
    public void endDocument() throws SAXException;
    public void startElement(String qName,
    AttributeList attributes)
    throws SAXException;
    public void endElement(String qName) throws SAXException;
    public void characters(char buf[], int offset, length)
    throws SAXException;
    public void ignorableWhitespace(char buf[], int offset,
    length)
    throws SAXException;
    public void processingInstruction(String target,
    String data)
    throws SAXException;
    // DTDHandler ... NOTE: no "throws SAXException"!
    public void notationDecl(String notationName,
    String publicId, String publicId);
    public void unparsedEntityDecl(String entityName,
    String publicId, String publicId, notationName);
    // EntityResolver
    public InputSource resolveEntity(String publicId,
    String publicId);
    throws SAXException;
    // ErrorHandler
    public void error(SAXParseException x) throws SAXException;
    public void fatalError(SAXParseException x) throws SAXException;
    public void warning(SAXParseException x) throws SAXException;
}

A.1.9. The InputSource Class

This class is used to encapsulate entities for consumption by an XMLReader (or a SAX1 Parser). Applications should make every effort to provide a usable system identifier (an absolute URI, rather than null). This will ensure that relative URIs can be properly resolved so that diagnostics are meaningful. Given that identifier, SAX parsers can do the rest, possibly with assistance from an EntityResolver.

For more information, refer to Section 3.1.2, "The InputSource Class" in Chapter 3, "Producing SAX2 Events".

public class InputSource {
    public InputSource();
    public InputSource(String systemId);
    public InputSource(java.io.InputStream in);
    public InputSource(java.io.Reader in);
    // getters
    public String getPublicId();
    public String getSystemId();
    public java.io.InputStream getByteStream();
    public String getEncoding();
    public java.io.Reader getCharacterStream();
    // setters
    public void setPublicId(String publicId);
    public void setSystemId(String systemId);
    public void setByteStream(java.io.InputStream in);
    public void setEncoding(String encodingName);
    public void setCharacterStream(java.io.Reader in);
}

A.1.10. The Locator Interface

An event producer may invoke the ContentHandler.setDocumentLocator() call to provide one of these objects. It may then be used inside event callbacks, until the final ContentHandler.endDocument() call, to determine the location of the data that triggered the event. A common use is to figure out the base URI used to resolve relative URIs found in document content. This is true even when xml:base attributes have been used to override the real base URI of the document. Another common use is to construct SAXParseException objects to construct application-level diagnostics.

For more information, refer to Section 4.1.2, "The Locator Interface " in Chapter 4, "Consuming SAX2 Events".

public interface Locator
{
    public String getPublicId();
    public String getSystemId();
    public int getLineNumber();
    public int getColumnNumber();
}

A.1.11. The Parser Interface

This SAX1 interface is no longer used in SAX2; the XMLReader is used instead.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public interface Parser
{
    // setters
    public void setLocale(java.util.Locale locale)
    throws SAXException;
    public void setEntityResolver(EntityResolver resolver);
    public void setDTDHandler(DTDHandler dtdHandler);
    public void setDocumentHandler(DocumentHandler docHandler);
    public void setErrorHandler(ErrorHandler errHandler);
    // parsing
    public void parse(InputSource in) throws SAXException,
    java.io.IOException;
    public void parse(String uri) throws SAXException,
    java.io.IOException;
}

A.1.12. SAXException

This is the base SAX exception class. It can wrap another exception, and (like most exceptions) a descriptive message.

For more information, refer to Section 2.5.1, "SAX2 Exception Classes" in Chapter 2, "Introducing SAX2".

public class SAXException extends Exception {
    public SAXException(String message);
    public SAXException(Exception cause);
    public SAXException(String message, Exception cause);
    // getters
    public Exception getException();
    public String getMessage();
}

A.1.13. SAXNotRecognizedException

This exception is used to report that the identifier for a feature flag, or parser property, is not recognized. When this doesn't indicate a mistyped identifier, it means that the parser isn't exposing that particular information.

For more information, refer to Section 2.5.1, "SAX2 Exception Classes" in Chapter 2, "Introducing SAX2".

public class SAXNotRecognizedException extends SAXException {
    public SAXNotRecognizedException(String message);
}

A.1.14. SAXNotSupportedException

This exception is used to report that while the identifier for a feature flag, or parser property, was recognized, setting the value was not practical. For example, read-only values can't be changed to nondefault values, handler properties need to implement the appropriate interface, and some values can't be changed while parsing, or accessed except while parsing.

For more information, refer to Section 2.5.1, "SAX2 Exception Classes" in Chapter 2, "Introducing SAX2".

public class SAXNotSupportedException extends SAXException {
    public SAXNotSupportedException(String message);
}

A.1.15. SAXParseException

This type of SAXException is reported to the ErrorHandler, and adds information that is useful for locating (and hence fixing) problems in input text. That information may include the line and character offset in the entity being parsed, the entity's URL, and any public identifier associated with the entity. When used to report application-level errors, any exception caused by problematic data can be encapsulated, and associated with such Locator information to help pinpoint problematic input data. It's safe to provide null Locator or Exception objects.

For more information, refer to Section 2.5.1, "SAX2 Exception Classes" as well as Section 2.5.3, "Errors and Diagnostics", which are both located in Chapter 2, "Introducing SAX2".

public class SAXParseException extends SAXException {
    public SAXParseException(String message, Locator where);
    public SAXParseException(String message, Locator where,
    Exception cause);
    public SAXParseException(String message,
    String publicId, String systemId, int line, int column);
    public SAXParseException(String message,
    String publicId, String systemId, int line, int column,
    Exception cause);
    // getters
    public String getPublicId();
    public String getSystemId();
    public int getLineNumber();
    public int getColumnNumber();
}

A.1.16. The XMLFilter Interface

This interface encapsulates the notion that one XMLReader may process the output of another one before delivering it. The XMLFilterImpl helper class is substantially more interesting, since it does the real work and can be used in a postprocessing mode as well.

For more information, refer to Section 3.5.4, "The XMLFilter Interface" in Chapter 3, "Producing SAX2 Events".

public interface XMLFilter extends XMLReader
{
    public void setParent(XMLReader parent);
    public XMLReader getParent();
}

A.1.17. The XMLReader Interface

A SAX2 "parser" will normally be packaged as an implementation of this interface. Such a parser often takes XML text as input, though it need not. Some parsers need a DOM Document as input, and others parse non-XML text and report it as if it were XML, to leverage the SAX event processing model.

For more information, refer to Section 3.1.1, "The XMLReader Interface" in Chapter 3, "Producing SAX2 Events". For quick reference to the standard SAX2 feature and property identifiers, refer to Section 3.3.2, "XMLReader Feature Flags" and Section 3.3.1, "XMLReader Properties", both in Chapter 3, "Producing SAX2 Events".

public interface XMLReader
{
    // getters
    public ContentHandler getContentHandler();
    public DTDHandler getDTDHandler();
    public EntityResolver getEntityResolver();
    public ErrorHandler getErrorHandler();
    public boolean getFeature(String uri)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public Object getProperty(String uri)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    // setters
    public void setContentHandler(ContentHandler contentHandelr);
    public void setDTDHandler(DTDHandler dtdHandler);
    public void setEntityResolver(EntityResolver resolver);
    public void setErrorHandler(ErrorHandler errHandler);
    public void setFeature(String uri, boolean value)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public void setProperty(String uri, Object value)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    // parsing
    public void parse(InputSource in)
    throws java.io.IOException, SAXException;
    public void parse(String uri)
    throws java.io.IOException, SAXException;
}

Previous | Home | Next
6.3. Including Subdocuments | Book Index | A.2. The org.xml.sax.helpers Package
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The org.xml.sax.helpers Package (SAX2)

 SAX2Search this book

A.2. The org.xml.sax.helpers Package

The org.xml.sax.helpers package holds support classes, including vendor-neutral bootstrapping support and some support for the original SAX1 APIs. These classes are in a sense optional but are provided by all widely available implementations. They're also required for conformance with Sun's JAXP API.

A.2.1. The AttributeListImpl Interface

This SAX1 class is not used in SAX2; the AttributesImpl class is used instead.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public class AttributeListImpl implements AttributeList {
    public AttributeListImpl();
    public AttributeListImpl(AttributeList original);
    // AttributeList (accessors only)
    public int getLength();
    public String getName(int index);
    public String getType(int index);
    public String getValue(int index);
    public String getType(String qName);
    public String getValue(String qName);
    // mutators
    public void setAttributeList(AttributeList original);
    public void addAttribute(String qName, String type, String value);
    public void removeAttribute(String qName);
    public void clear();
}

A.2.2. The AttributesImpl Class

This class can be a convenient way to snapshot attribute information using the copy constructor. Since the attributes provided by an event producer are only valid during the particular ContentHandler.startElement() call that provides them, applications may need such snapshots. The class also supports construction of arbitrary attribute sets for filtering or event production.

For more information, refer to Section 5.1.1, "The AttributesImpl Class " in Chapter 5, "Other SAX Classes".

public class AttributesImpl implements Attributes {
    public AttributesImpl();
    public AttributesImpl(Attributes original);
    // Attributes (accessors only)
    public int getLength();
    public String getURI(int index);
    public String getLocalName(int index);
    public String getQName(int index);
    public String getType(int index);
    public String getValue(int index);
    public int getIndex(String uri, String localName);
    public int getIndex(String qName);
    public String getType(String uri, String localName);
    public String getType(String qName);
    public String getValue(String uri, String localName);
    public String getValue(String qName);
    // setters
    public void setLocalName(int index, String localName);
    public void setQName(int index, String qName);
    public void setType(int index, String type);
    public void setURI(int index, String uri);
    public void setValue(int index, String value);
    // mutators
    public void addAttribute(String uri, String localName,
    String qName,
            String type, String value);
    public void clear();
    public void removeAttribute(int index);
    public void setAttribute(int index, String, String,
    String, String, String);
    public void setAttributes(Attributes original);
}

A.2.3. The DefaultHandler Class

This class provides stub implementations of all the standard SAX2 handlers, including ErrorHandler, and the EntityResolver. Those stub implementations do nothing, except that ErrorHandler.fatalError() throws its argument. Extension handler callbacks are not supported; if you need DeclHandler or LexicalHandler stubs you'll need to provide them yourself (perhaps by subclassing).

For more information, refer to Section 2.3.1, "The DefaultHandler Class" in Chapter 2, "Introducing SAX2".

public class DefaultHandler
    implements EntityResolver, DTDHandler, ContentHandler,
    ErrorHandler
{
    public DefaultHandler();
    // ContentHandler
    public void setDocumentLocator(Locator locator);
    public void startDocument() throws SAXException;
    public void endDocument() throws SAXException;
    public void startElement(String uri, String localName,
        String qName, Attributes)
    throws SAXException;
    public void endElement(String uri, String localName,
    String qName)
    throws SAXException;
    public void characters(char buf[], int offset, int length)
    throws SAXException;
    public void ignorableWhitespace(char buf[], int offset,
    int length)
    throws SAXException;
    public void processingInstruction(String target, String data)
    throws SAXException;
    public void startPrefixMapping(String prefix, String uri)
    throws SAXException;
    public void endPrefixMapping(String prefix) throws
    SAXException;
    public void skippedEntity(String name) throws SAXException;
    // DTDHandler
    public void notationDecl(String notationName,
        String publicId, String systemId)
    throws SAXException;
    public void unparsedEntityDecl(String entityName,
        String publicId, String systemId, String
    notationName)
    throws SAXException;
    // EntityResolver
    public InputSource resolveEntity(String publicId,
    String publicId);
    throws SAXException;
    // ErrorHandler
    public void error(SAXParseException x) throws SAXException;
    public void fatalError(SAXParseException x) throws
    SAXException;
    public void warning(SAXParseException x) throws SAXException;
}

A.2.4. The LocatorImpl Class

This class can provide a convenient way to snapshot locator information. Since the locator provided by an event producer may report different values during each event callback, applications may need such snapshots.

For more information, refer to Section 5.1.2, "The LocatorImpl Class " in Chapter 5, "Other SAX Classes".

public class LocatorImpl implements Locator {
    public LocatorImpl();
    public LocatorImpl(Locator);
    // Locator
    public String getPublicId();
    public String getSystemId();
    public int getLineNumber();
    public int getColumnNumber();
    // setters
    public void setPublicId(String publicId);
    public void setSystemId(String systemId);
    public void setLineNumber(int line);
    public void setColumnNumber(int column);
}

A.2.5. The NamespaceSupport Class

This class helps implement stacks of XML namespace context data. It's mostly useful for applications that need to handle element or attribute names within document content (including attributes) or for parser writers.

For more information, refer to Section 5.1.3, "The NamespaceSupport Class " in Chapter 5, "Other SAX Classes".

public class NamespaceSupport {
    // fixed uri for the "xml" prefix
    public static final String XMLNS;
    public NamespaceSupport();
    // manipulate binding stack
    public void reset();
    public void pushContext();
    public void popContext();
    public boolean declarePrefix(String prefix, String uri);
    public String [] processName(String qName, String parts[],
        boolean isAttribute);
    // access currently visible prefix bindings
    public String getURI(String prefix);
    public java.util.Enumeration getPrefixes();
    public String getPrefix(String uri);
    public java.util.Enumeration getPrefixes(String uri);
    public java.util.Enumeration getDeclaredPrefixes();
}

A.2.6. The ParserAdapter Class

This class is used to convert SAX1 Parser objects into XMLReader objects by converting SAX1 event callbacks into SAX2 callbacks. It uses the NamespaceSupport class internally to track namespaces so it can report them for elements and attributes, as required by SAX2. If you need to make a SAX1 parser report handling of validation or external entities though feature flags, you can subclass ParserAdapter and override the appropriate methods.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public class ParserAdapter implements XMLReader,
    DocumentHandler {
    public helpers.ParserAdapter() throws SAXException;
    public helpers.ParserAdapter(Parser sax1);
    // XMLReader getters
    public boolean getFeature(String uri)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public ContentHandler getContentHandler();
    public DTDHandler getDTDHandler();
    public EntityResolver getEntityResolver();
    public ErrorHandler getErrorHandler();
    public Object getProperty(String uri)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    // XMLReader setters
    public void setContentHandler(ContentHandler contentHandler);
    public void setDTDHandler(DTDHandler dtdHandler);
    public void setEntityResolver(EntityResolver resolver);
    public void setErrorHandler(ErrorHandler errHandler);
    public void setFeature(String uri, boolean value)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public void setProperty(String uri, Object value)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    // XMLReader parsing
    public void parse(String uri) throws java.io.IOException,
    SAXException;
    public void parse(InputSource in) throws java.io.IOException,
    SAXException;
    // DocumentHandler (internals -- don't use)
    public void setDocumentLocator(Locator locator);
    public void startDocument() throws SAXException;
    public void endDocument() throws SAXException;
    public void startElement(String qName, AttributeList
    attributes)
    throws SAXException;
    public void endElement(String qName) throws SAXException;
    public void characters(char buf[], int offset, int length)
    throws SAXException;
    public void ignorableWhitespace(char buf[], int offset,
    int length)
    throws SAXException;
    public void processingInstruction(String target,
    String data)
    throws SAXException;
}

A.2.7. The ParserFactory Class

This SAX1 interface is not used in SAX2; the XMLReaderFactory is used instead. The org.xml.sax.parser system property was used to configure the default SAX1 parser.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public class ParserFactory {
    public static Parser makeParser()
    throws ClassNotFoundException, IllegalAccessException,
        InstantiationException, NullPointerException,
    ClassCastException;
    public static Parser makeParser(String classname)
    throws ClassNotFoundException, IllegalAccessException,
        InstantiationException, ClassCastException;
}

A.2.8. The XMLFilterImpl Class

This class implements all the standard SAX2 events received from its parent XMLReader by passing them on to the handlers (or EntityResolver) registered with it. It only supports filtering core events, because it ignores the two extension handlers for declaration and lexical events.

This means you can use it in two modes. First, it can be a base class for simple consumer pipelines, unless you need information that's provided using extension handlers. Second, you can package a filter with a parser, so it can produce events like an XMLReader that just happens to do a bit of extra work (such as cleaning up input data).

For more information, refer to Section 4.5.1, "The XMLFilterImpl Class " in Chapter 4, "Consuming SAX2 Events".

public class XMLFilterImpl
    implements XMLFilter, EntityResolver, DTDHandler,
    ContentHandler, ErrorHandler
{
    public XMLFilterImpl();
    public XMLFilterImpl(XMLReader parent);
    public void setParent(XMLReader parent);
    // EntityResolver
    public InputSource resolveEntity(String publicId,
    String publicId);
    throws SAXException;
    // DTDHandler
    public void notationDecl(String notationName,
        String publicId, String systemId)
    throws SAXException;
    public void unparsedEntityDecl(String entityName,
        String publicId, String systemId,
    String notationName)
    throws SAXException;
    // ContentHandler
    public void setDocumentLocator(Locator locator);
    public void startDocument() throws SAXException;
    public void endDocument() throws SAXException;
    public void startElement(String uri, String localName,
    String qName,
        Attributes attributes)
    throws SAXException;
    public void endElement(String uri, String localName,
    String qName)
    throws SAXException;
    public void characters(char buf[], int offset, int length)
    throws SAXException;
    public void ignorableWhitespace(char buf[], int offset,
    int length)
    throws SAXException;
    public void processingInstruction(String target, String data)
    throws SAXException;
    public void startPrefixMapping(String prefix, String uri)
    throws SAXException;
    public void endPrefixMapping(String prefix) throws
    SAXException;
    public void skippedEntity(String name) throws SAXException;
    // ErrorHandler
    public void error(SAXParseException x) throws SAXException;
    public void fatalError(SAXParseException x) throws
    SAXException;
    public void warning(SAXParseException x) throws SAXException;
    // XMLFilter
    public XMLReader getParent();
    // XMLReader
    public ContentHandler getContentHandler();
    public DTDHandler getDTDHandler();
    public EntityResolver getEntityResolver();
    public ErrorHandler getErrorHandler();
    public boolean getFeature(String uri)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public Object getProperty(String uri)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public void setContentHandler(ContentHandler contentHandler);
    public void setDTDHandler(DTDHandler dtdHandler);
    public void setEntityResolver(EntityResolver resolver);
    public void setErrorHandler(ErrorHandler errHandler);
    public void setFeature(String uri, boolean value)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public void setProperty(String uri, Object value)
    throws SAXNotRecognizedException,
    SAXNotSupportedException;
    public void parse(InputSource in)
    throws java.io.IOException, SAXException;
    public void parse(String uri)
    throws java.io.IOException, SAXException;
}

A.2.9. The XMLReaderAdapter Class

This class is used to convert SAX2 XMLReader objects into Parser objects by converting SAX2 event callbacks into SAX1 callbacks.

For more information, refer to Section 5.2, "SAX1 Support " in Chapter 5, "Other SAX Classes".

public class XMLReaderAdapter implements Parser,
    ContentHandler {
    public XMLReaderAdapter() throws SAXException;
    public XMLReaderAdapter(XMLReader reader);
    // Parser
    public void setLocale(java.util.Locale locale) throws
    SAXException;
    public void setEntityResolver(EntityResolver resolver);
    public void setDTDHandler(DTDHandler dtdHandler);
    public void setDocumentHandler(DocumentHandler docHandler);
    public void setErrorHandler(ErrorHandler errHandler);
    public void parse(String uri) throws java.io.IOException,
    SAXException;
    public void parse(InputSource in) throws java.io.IOException,
    SAXException;
    // ContentHandler (internals -- don't use)
    public void setDocumentLocator(Locator locator);
    public void startDocument() throws SAXException;
    public void endDocument() throws SAXException;
    public void startElement(String uri, String localName,
    String qName,
        Attributes attributes)
    throws SAXException;
    public void endElement(String uri, String localName,
    String qName)
    throws SAXException;
    public void characters(char buf[], int offset, int length)
    throws SAXException;
    public void ignorableWhitespace(char buf[], int offset,
    int length)
    throws SAXException;
    public void processingInstruction(String target,
    String data)
    throws SAXException;
    public void startPrefixMapping(String prefix, String uri);
    public void endPrefixMapping(String prefix);
    public void skippedEntity(String name) throws SAXException;
}

A.2.10. The XMLReaderFactory Class

This factory is the parser-independent bootstrapping API for SAX2. The reference implementation uses the org.xml.sax.driver system property (or META-INF/services/org.xml.sax.driver resource in the class path) to determine the package-qualified name of the environment's default implementation for the no-parameters call. Most implementations maintain that behavior, but some resource-constrained environments can use simpler policies with less configurability.

For more information, refer to Section 3.2.1, "The XMLReaderFactory Class" in Chapter 3, "Producing SAX2 Events".

public final class XMLReaderFactory {
    public static XMLReader createXMLReader() throws
    SAXException;
    public static XMLReader createXMLReader(String classname)
    throws SAXException;
}

Previous | Home | Next
A. SAX2 API Summary | Book Index | A.3. The org.xml.sax.ext Package
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The org.xml.sax.ext Package (SAX2)

 SAX2Search this book

A.3. The org.xml.sax.ext Package

The org.xml.sax.ext package holds extension interfaces that not all SAX2 parsers are expected to implement. These classes are in a sense optional but are provided by all widely used implementations and are required by Sun's JAXP API.

Unlike the handlers in the SAX core, these handlers do not have type-safe routines to bind them to XMLReader objects. They are identified using URIs, and bindings are accessed using the getProperty() and setProperty() methods.

A.3.1. The DeclHandler class

This is the primary way SAX2 exposes typing constraints from an XML Document Type Declaration. It also reports entity declarations.

For more information, refer to Section 4.3.1, "The DeclHandler Interface " in Chapter 4, "Consuming SAX2 Events".

public interface DeclHandler
{
    // data typing
    public void attributeDecl(String element, String string
    attribute,
        String type, String mode, String defaultValue )
    throws SAXException;
    public void elementDecl(String name, String model) throws
    SAXException;
    // entity info
    public void externalEntityDecl(String name,
        String publicId, String systemId)
    throws SAXException;
    public void internalEntityDecl(String name, String value)
    throws SAXException;
}

A.3.2. The LexicalHandler Interface

Many parsers expose certain data even though it is, for most purposes, not part of the information that an XML document intends to convey to applications. This interface exposes some such data.

For more information, refer to Section 4.2, "The LexicalHandler Interface " in Chapter 4, "Consuming SAX2 Events".

public interface LexicalHandler
{
    public void startDTD(String root, String publicId,
    String systemId)
    throws SAXException;
    public void endDTD() throws SAXException;
    public void startEntity(String name) throws SAXException;
    public void endEntity(String name) throws SAXException;
    public void startCDATA() throws SAXException;
    public void endCDATA() throws SAXException;
    public void comment(char buf[], int offset, int length)
    throws SAXException;
}

Previous | Home | Next
A.2. The org.xml.sax.helpers Package | Book Index | B. SAX2 and the XML Infoset
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX2 and the XML Infoset (SAX2)

 SAX2Search this book

Appendix B. SAX2 and the XML Infoset

Contents:

Event Producer Issues
Event Consumer Issues
Document Information Item
Element Information Items
Attribute Information Items
Processing Instruction Information Items
Unexpanded Entity Reference Information Items
Character Information Items
Comment Information Items
Document Type Declaration Information Item
Unparsed Entity Information Items
Notation Information Items
Namespace Information Items

This appendix shows how the various parts of the XML Infoset are made available through the SAX2 event consumer APIs. Think of it as a structural index for concepts in SAX2, or for the underlying XML information structure. Use it when you're trying to develop SAX2-based software that needs access to particular data. It can also be viewed as an Infoset conformance statement for SAX2; it will help you to understand what parts of the XML Infoset aren't supported by SAX2 and to see where SAX2 lets you access information beyond what the Infoset addresses. The Infoset is not a data structure; what's important is that the information be provided, not randomly accessible.

The presentation here is the same as used in the Infoset specification itself; the structure and order are identical. Information items are similar to object types, and each is presented in its own section. Information items consist of sets of named [properties], each of which is presented in a table. Properties can have one or more values, sometimes ordered, which are provided in SAX2 using consumer callbacks. You should be able to make sense of this without reading the infoset specification if you know XML, but you'll need it to understand some details.

As of this writing, the XML Infoset (http://www.w3.org/TR/xml-infoset/) has recently been finalized. This appendix was written using the 24 October 2001 "Recommendation," which omits almost all declarations found in the DTD. Some other W3C specifications use related data models, like the XPath Data Model. The W3C approach to XML Schemas augments this core Infoset with additional data-typing information items, defining the Post-Schema-Validation Infoset (PSVI) items and properties associated with schema-valid XML text. Most of those PSVI properties relate to data-typing models.

B.1. Event Producer Issues

Although the focus of this appendix is on how SAX2 event consumers see Infoset data, you may also need to pay attention to some producer-side issues beyond ensuring that the event stream itself is legal (and perhaps valid). As the Infoset specification puts it, "synthetic" infosets might have inconsistencies that real ones (from XML documents) don't. If you produce a synthetic infoset, by writing SAX events directly rather than by using a parser, make sure the event stream is properly constructed.

As noted earlier, you should make sure you always provide the document URI when you invoke XMLReader.parse(). Not only is this needed to correctly absolutize relative URIs found in the document's DTD (for notations and all types of external entities) and to provide accurate diagnostics, but it is essential for computing [base URI] properties in the document entity.

The namespace-prefixes feature on XMLReader instances has a problematic default; set its value to true unless you're comfortable with parsers hiding [namespace attributes] and [prefix] properties. (In this book, this is called mixed mode namespace support.) SAX2 parsers aren't required to support setting this feature value to true, but most do. If your parser doesn't support this, you can re-create prefixes and declarations, but they normally won't correspond to the original versions. This appendix assumes you kept the default setting (true) for the namespaces feature flag.

Some SAX2 XMLReader implementations may not produce all of this information. Most of today's widely used SAX2 parsers are fully featured, so in practice this won't be a common problem. However, information provided through the optional SAX2 extension callbacks DeclHandler or LexicalHandler might not be available. Similarly, reporting of [base URI] ingredients through a Locator is also optional.

The SAX2 ErrorHandler exposes some data that is not addressed by the XML Infoset: validity and well-formedness errors. Exposing such information is required for parser conformance to the XML 1.0 specification.

Previous | Home | Next
A.3. The org.xml.sax.ext Package | Book Index | B.2. Event Consumer Issues
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Event Consumer Issues (SAX2)

 SAX2Search this book

B.2. Event Consumer Issues

The primary Infoset concern for SAX2 event consumers is to understand how the stream of events represents the information structures used in the Infoset. Applications need to track some state if they need access to some of those structures or random access to anything. It's typical to track only a few items, and ignore the rest as being incidental background noise. Streaming processing discards items as soon as possible.

You really shouldn't care, but since the String datatype can't handle more than two gigabytes of data, and strings are used to pass certain document data to applications, there's a chance that some documents could cause trouble by overflowing that limit. If you encounter such a document, consult a pathologist. There really isn't much you can do about this.

B.2.1. Structural Issues

The [children] properties are arbitrarily sized, ordered sequences of information items, which are presented in document order by SAX2 event callbacks. Most other information items are not ordered, such as [notations], [unparsed entities], and [attributes] properties. Only [children] properties would need to be stored in order-preserving data structures.

While most information items are provided through a single callback, some of the more complex ones involve matched, and (except in one case) cleanly nested, pairs of calls to start() and end() the item. Such items include the Document itself, its Document Type Declaration, Elements, and Namespace Information. To track those items, applications implement some kind of context stack tracking.

The [parent] properties of some information items are implicitly encoded through such SAX2 nested event reports. Except for items that can be direct children of the Document or Document Type Information Items, applications often push stack entries when startElement() is called and pop them when endElement() is called.

The children of Document and Document Type Information Items have curious restrictions: they don't always match the actual text structure. For example, information items for notations and unparsed entities are found in the Document Information Item, but they're textually part of the Document Type; and comments are stripped out of DTDs. You can use more natural structures in your applications if the descriptive Infoset structure seems awkward.

Other complex information items are implicitly decoded from DTD declarations. To track such items, applications must save declarations during DTD processing, to ensure that they can be correlated with information in the body of a document. Examples of such items include [notation] properties for Unparsed Entities and processing instructions, most properties for Unexpanded Entity References, and [references] properties of attributes.

B.2.2. Base URIs, xml:base, and Locator Data

Some information items have a [base URI] property that is computed according to xml:base rules. Except for two cases, these rules amount to using Locator.getSystemId() to find the absolute base URI; the producer needs to provide this information. SAX2 effectively augments every information item with this information, as well as line and column location within such entities. (However, applications can cause this information to be lost if they provide InputSource objects without including those base URIs as the system IDs.)

The two exceptional cases are for Elements and for processing instructions within the document element. In these instances, the computation is complex because xml:base attributes can play a role; it is demonstrated in Example 5-1. Consumers must be able to invoke Locator.getSystemId() to get the entity's URI in LexicalHandler.startEntity() when the entity is shown to be external using DeclHandler.externalEntityDecl(). And they must also maintain a stack of URIs, augmenting it with xml:base values.

Application code should use Locator information to generate meaningful diagnostics. However, conforming applications will use the URI computed with xml:base when absolutizing relative URIs found in attribute values, character data, processing instructions, or (primarily for HTML legacy data models) comments. Except for the startDTD() call, all system identifiers reported through SAX are delivered as absolute URIs. An upcoming extension feature flag will probably let that behavior be changed, so you can choose whether the parser or the application absolutizes the URIs. Meanwhile, you should be aware that some SAX parsers have bugs in how they report such identifiers.

Previous | Home | Next
B. SAX2 and the XML Infoset | Book Index | B.3. Document Information Item
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Document Information Item (SAX2)

 SAX2Search this book

B.3. Document Information Item

The Document Information Item is the root of the information found in an XML document. There is only one such root item.

This information item begins with the ContentHandler.startDocument() call and ends with the ContentHandler.endDocument() call. Many SAX2 event calls are used to construct its children or constituents.

Property

Callbacks

Explanation

[children]

See the sections for each type of Information Item: Document Type Declaration (one, if present), Element (one), processing instruction (possibly many), Comment (possibly many).

[document element]

This is the element in the [children] property.

[notations]

See the section on Notation Information Items. (Unordered.)

[unparsed entities]

See the section on Unparsed Entity Information Items. (Unordered.)

[base URI]

Locator.getSystemId(), or XMLReader.parse()

Locator may be used during the startDocument() callback (and earlier callbacks, unless they were made in the context of an external parameter entity).

Alternatively, for any parsers that don't provide a Locator, applications using an XMLReader are responsible for providing this information (if it exists) to the parse() method. This is passed directly as the string parameter or indirectly as the systemId property of an InputSource.

[character encoding scheme]

unavailable; or InputSource.getEncoding()

Normally this property is unavailable; it won't affect the interpretation of character data in Java. However, applications will in rare cases provide this to the parser when they call XMLReader.parse(InputSource) to start parsing. It's likely that an upcoming extension API will provide this information.

[standalone]

XMLReader.getFeature()

It's likely that an upcoming extension API will provide this information using an is-standalone feature flag.

[version]

unavailable

You can probably assume the value of this property is "1.0" for now. It's likely that an upcoming extension API will provide this information.

[all declarations processed]

ContentHandler.skippedEntity(): LexicalHandler.endDTD()

When endDTD() is invoked, the value of this property is known. If no external parameter entities are reported as skipped, then the value is true. If the parser doesn't support the lexical handler, then the later call to startElement() may be used instead of endDTD().

Because text in Java is always accessed using UTF-16 character strings or arrays, most applications won't need to worry about encoding issues; the SAX2 parser handles that. However, there are cases when encoding may matter:

Input normalization

Some recent XML standards require that text be normalized. For example, XML Canonicalization (as used in digital signature applications) requires the use of Unicode Normalization Form C; some other W3C specifications have the same requirement. Text originally represented in UTF-8 or UTF-16 might need further normalization to remove some deprecated character codes that can be represented using those encodings.

Such encoding data is required on a per-entity basis, not a per-document basis as implied by the Infoset specification. And for internal entity expansions or defaulted attributes, you'll need to normalize if the encoding associated with the original definition supported denormalized text.

Output encoding

When using an output encoding that is not based on the Unicode character set, you may not be able to represent XML names that use particular characters. For example, ASCII cannot handle element or attribute names using accented characters (used in Europe and Latin America) or using ideographic characters (used in Asia).

The preferred encoding solution is to always use UTF-8 or UTF-16 when outputting XML, so that such problems cannot occur and so that all XML processors can work with such output. Similar logic applies to display systems like window systems: prefer font rendering systems that use Unicode over those tied to some specific encoding.

Previous | Home | Next
B.2. Event Consumer Issues | Book Index | B.4. Element Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Element Information Items (SAX2)

 SAX2Search this book

B.4. Element Information Items

An Element Information Item holds the most frequently needed data in an XML document. There is one top-level element, associated with the Document Information Item, and all but a handful of information items are its descendants.

This information item starts with a ContentHandler.startElement() call, and ends with a ContentHandler.endElement() call.

Property | Callbacks | Explanation
[namespace name] | ContentHandler.startElement(), namespaceURI parameter |
[local name] | ContentHandler.startElement(), localName parameter |
[prefix] | ContentHandler.startElement(), qName parameter (when available) | The QName (namespace-prefixed name) includes any prefix available; for example, a QName xhtml:a uses the prefix xhtml.
[children] |  | See the sections for each type of information item: Element, Processing Instruction, Unexpanded Entity Ref, Character, Comment.
[attributes] | ContentHandler.startElement(), attributes parameter, DeclHandler.attributeDecl() | When the [namespace attributes] property value is accessible, both groups of attributes are intermixed. Values that are #IMPLIED, but not specified in the document text, are only visible through the attributeDecl() callback. If you need to know about such attributes, record them during DTD processing.
[namespace attributes] | ContentHandler.startElement(), attributes parameter (when available) | If the namespace-prefixes feature flag is true, these attributes are mixed with the [attributes] property. They're the ones with QName values of xmlns, or starting with xmlns:. [28] Otherwise, this data is unavailable.
[in-scope namespaces] |  | See the section on Namespace Information Items.
[base URI] | computed using xml:base | In the absence of xml:base attributes, this is normally the value that Locator.getSystemId() exposes during the startElement() callback.
[parent] |  | Applications must keep track of this information item if it is needed.

Manually associate these with the namespace URI http://www.w3.org/2000/xmlns/

Previous | Home | Next
B.3. Document Information Item | Book Index | B.5. Attribute Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Attribute Information Items (SAX2)

 SAX2Search this book

B.5. Attribute Information Items

The Attribute Information Items are the contents of the [attributes] property in the element information item. Although the attributes are presented in an order through the Attributes class, there is no expectation that this order reflects an order in the document or its DTD.

Property

Callbacks

Explanation

[namespace name]

Attributes.getURI()

[local name]

Attributes.getLocalName()

[prefix]

Attributes.getQName() (when available)

The QName (namespace-prefixed name) includes any prefix available; for example, a QName xhtml:href uses the prefix xhtml.

[normalized value]

Attributes.getValue()

If you're generating a stream of Infoset data programmatically, don't forget to normalize these values correctly. The XML specification explains how to normalize this text; it mostly translates whitespace (but not character references) into space characters and eliminates unneeded spaces for values that aren't CDATA.

[specified]

unavailable

SAX2 does not distinguish between attribute values that were specified in document text and those that have been defaulted from a DTD. It's likely that an upcoming extension API will provide this information.

[attribute type]

Attributes.getType(), DeclHandler.attributeDecl()

For most types of attribute, getType() gives all the type data needed, but you may want to distinguish types that are actual CDATA versus (invalid) ones that just look like CDATA because the attribute was not declared.

Attribute values that are constrained to an enumerated set are reported with special syntax in attributeDecl() callbacks. Enumerations use a parenthesized syntax, like (true|false), to enumerate all possibilities. NOTATION enumerations prepend the string "NOTATION " (with a space) to that syntax.

[references]

For NOTATION type values, see the section on Notation Information Items. For ENTITY or ENTITIES type values, see the section on Unparsed Entity Information Items. For IDREF or IDREFS type values, applications must track attributes by using the [attribute type] IDs reported as keys to application-specific representations of elements, and they must be ready to handle forward references. (ENTITIES and IDREFS values must be tokenized by the application.)

[owner element]

Attributes are associated with the element signified by the startElement() call providing the Attributes object.

Note that DOM extends this information item to expose entities (expanded or not) within attribute values. That is not widely believed to be a useful feature. Since SAX doesn't extend the Infoset in that way, you can't implement that part of DOM using pure SAX.

Previous | Home | Next
B.4. Element Information Items | Book Index | B.6. Processing Instruction Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Processing Instruction Information Items (SAX2)

 SAX2Search this book

B.6. Processing Instruction Information Items

Processing instructions (PIs) are used within XML documents to capture information that doesn't necessarily fit into the nested structure found elsewhere. Such data doesn't need to relate to processing tasks, although that's one historical use for such constructs.

Property | Callbacks | Explanation
[target] | ContentHandler.processingInstruction(), target parameter |
[content] | ContentHandler.processingInstruction(), target parameter |
[base URI] | computed using xml:base | In the absence of xml:base attributes, this property is normally the value that Locator.getSystemId() exposes during the processingInstruction() callback.
[notation] |  | See the section on Notation Information Items. Tracking notations is the responsibility of applications.
[parent] |  | When startElement() is invoked with no matching endElement(), the parent is the current element. Between calls to LexicalHandler.startDTD() and LexicalHandler.endDTD(), the parent is the Document Type Declaration. Otherwise, the document itself is the parent.

Some applications use a convention that PI target names are matched against notation declarations, and the notations' public (or system) IDs are used to deduce the meaning behind a given PI. For example, such an ID might indicate a particular tool to use on receipt of a document (preferably redirecting through a table to facilitate useful security constraints). This is purely a convention, but it's recognized by the XML specification. It is not an XML error if such notations are undeclared. Moreover, PIs can precede notation declarations in the DTD.

If the SAX2 implementation doesn't support the LexicalHandler, then there is no way to determing whether processing instructions are part of the DTD or a part of another section of the document prologue.

Previous | Home | Next
B.5. Attribute Information Items | Book Index | B.7. Unexpanded Entity Reference Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Unexpanded Entity Reference Information Items (SAX2)

 SAX2Search this book

B.7. Unexpanded Entity Reference Information Items

For any nonvalidating XML parser that doesn't read all external entities -- possibly because it was configured not to do so or because it didn't choose to implement that feature -- the XML specification says you need to be able to indicate when an entity that would normally be parsed wasn't actually processed. These unexpanded entities are not the same as "unparsed" entities, although neither kind of entity gets parsed.

The XML Infoset describes some information that can be made available in one of those cases: when the entity was an external general entity. For external parameter entities, the Infoset is silent beyond defining a document information item property to expose whether all declarations have been processed; no declarations are exposed.

Property | Callbacks | Explanation
[name] | Content.skippedEntity(), name parameter | SAX2 makes this callback for all entities that have been skipped, including parameter and internal entities.
[system identifier] | DeclHandler.externalEntityDecl(), systemId parameter | If [all declarations processed] is false, this information may be unavailable. Otherwise, the application must have recorded this information for later use. Note that SAX parsers absolutize this property against the appropriate base URI before reporting it. However, some parsers have a bug here, and don't absolutize this URI.
[public identifier] | DeclHandler.externalEntityDecl(), publicId parameter | If [all declarations processed] is false, this information may be unavailable. Otherwise, the application must have recorded this information for later use.
[declaration base URI] | Locator.getSystemId() | If [all declarations processed] is false, this information may be unavailable. Otherwise, the application must have recorded this information for later use, when this entity was reported through a DeclHandler.externalEntityDecl() callback. (xml:base does not apply.)
[parent] |  | Applications must keep track of this information item if it is needed.

SAX2 effectively defines new types of information items for internal and external entities. (So does DOM Level 1.) The XML Infoset doesn't expose such entities except for this one case (for external entities), but applications may use those extension information items for other purposes if appropriate.

Previous | Home | Next
B.6. Processing Instruction Information Items | Book Index | B.8. Character Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Character Information Items (SAX2)

 SAX2Search this book

B.8. Character Information Items

Along with element and attribute information items, characters are one of the core types of information used by XML applications. SAX2 reports characters in groups, rather than one at a time.

Property | Callbacks | Explanation
[character code] | ContentHandler.characters(), ContentHandler.ignorableWhitespace() | These calls provide one or more characters in the UTF-16 encoding. Normally, each Java char is a single [character code], but surrogate pairs are used to encode characters from the "Astral Planes," which don't fit into 16 bits. (No whitespace characters need surrogate pairs.)
[element content whitespace] |  | When known, this Boolean property is encoded by using the ignorableWhitespace() callback instead of characters(). Most SAX parsers report this property even when they aren't validating, though that's not required. (If any external parameter entities are skipped, it is not possible to reliably provide this information.)
[parent] |  | Applications must keep track of this information item if it is needed.

SAX2 permits reporting of a character property that the XML Infoset doesn't address: whether the characters are in a CDATA section. (DOM requires this information.) Such section boundaries are reported using methods in the LexicalHandler class.

Previous | Home | Next
B.7. Unexpanded Entity Reference Information Items | Book Index | B.9. Comment Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Comment Information Items (SAX2)

 SAX2Search this book

B.9. Comment Information Items

Comments are intended for human consumption; processing instructions are reserved for application data. The main curiosity here is that the Infoset doesn't believe in comments within DTDs, perhaps on the grounds that they'd need to be associated with the declarations they describe. (Some DTD documentation tools rely on magic comment syntax, much like javadoc.)

Property | Callbacks | Explanation
[content] | LexicalHandler.comment() | The characters identified in this callback are the contents of the comment.
[parent] |  | When startElement() is invoked with no matching endElement(), the parent is the current element. The Infoset ignores comments reported between calls to LexicalHandler.startDTD() and LexicalHandler.endDTD(). Otherwise, the document itself is the parent.

Some legacy applications use comments to represent the sort of information that processing instructions were designed to hold; an example is wrapping of CSS rendering hints in HTML comments.

Previous | Home | Next
B.8. Character Information Items | Book Index | B.10. Document Type Declaration Information Item
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Document Type Declaration Information Item (SAX2)

 SAX2Search this book

B.10. Document Type Declaration Information Item

This is a curious item in the Infoset, because it doesn't expose all the DTD information. In particular, it doesn't include any declarations (including the expected root element name) or comments in DTDs.

This information item starts with a LexicalHandler.startDTD() call and ends with a LexicalHandler.endDTD() call.

Property | Callbacks | Explanation
[system identifier] | LexicalHandler.startDTD(), systemId parameter | If the DTD includes an external subset, this is its system identifier. Note that this URI is not absolutized.
[public identifier] | LexicalHandler.startDTD(), publicId parameter | External subsets are not required to have public identifiers. When provided, this value is normalized.
[children] |  | See the section on Processing Instruction Information Items. Comments within DTDs are not part of the Infoset, and the few declarations that are included (notations and unparsed entities) are separated from the DTD.
[parent] |  | This is the Document Information Item.

SAX2 exposes more information than the Infoset describes, though somewhat less than XML allows. Comments may be reported using the LexicalHandler. Element and attribute declarations, as well as external and internal entity declarations, may be reported using the DeclHandler.

Previous | Home | Next
B.9. Comment Information Items | Book Index | B.11. Unparsed Entity Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Unparsed Entity Information Items (SAX2)

 SAX2Search this book

B.11. Unparsed Entity Information Items

When unparsed entities are used, these information items are normally saved by applications during DTD processing (keyed by entity name) and then accessed on demand. Unparsed entities are used only with attribute values of type ENTITY or ENTITIES.
Property | Callbacks | Explanation
[name] | DTDHandler.unparsedEntityDecl(), name parameter |
[system identifier] | DTDHandler.unparsedEntityDecl(), systemId parameter | This ID should be absolutized by the parser. However, some parsers have a bug here and don't absolutize this URI.
[public identifier] | DTDHandler.unparsedEntityDecl(), publicId parameter | Unparsed entities are not required to have public identifiers. When provided, this value is normalized.
[declaration base URI] | Locator.getSystemId() | If a SAX parser provides a Locator, it may be used to determine the current base URI during parser callbacks. (xml:base does not apply.)
[notation name] | DTDHandler.unparsedEntityDecl(), notationName parameter |
[notation] |  | See the section on Notation Information Items. Locating notations is the responsibility of applications. It's best not to try accessing this property until all declarations have been processed.

Previous | Home | Next
B.10. Document Type Declaration Information Item | Book Index | B.12. Notation Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Notation Information Items (SAX2)

 SAX2Search this book

B.12. Notation Information Items

When notations are used, these information items are normally saved by applications during DTD processing (keyed by notation name) and then accessed on demand. Notations are used with NOTATION attributes (at most one per element) or with unparsed entities, and perhaps with processing instruction target names.

Property | Callbacks | Explanation
[name] | DTDHandler.notationDecl(), name parameter |
[system identifier] | DTDHandler.notationDecl(), systemId parameter | Notations are not required to have system identifiers if they have a public identifier. This ID should be absolutized by the parser. However, some parsers have a bug here and don't absolutize this URI -- although, because of an issue with early versions of the SAX1 and SAX2 specifications, some parsers might absolutize such URIs.
[public identifier] | DTDHandler.notationDecl(), publicId parameter | Notations are not required to have public identifiers if they have a system identifier. When provided, this value is normalized.
[declaration base URI] | Locator.getSystemId() | If a SAX event producer provides a Locator, it can be used to determine the current base URI during parser callbacks. (xml:base does not apply.)

Previous | Home | Next
B.11. Unparsed Entity Information Items | Book Index | B.13. Namespace Information Items
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespace Information Items (SAX2)

 SAX2Search this book

B.13. Namespace Information Items

These information items expose namespace identifiers and the prefixes currently used to associate element or attribute names with those identifiers. With SAX2, applications that track these prefixes need to use a stack to handle the lexical scoping rules: in the context of one element and its children, a prefix may indicate a different namespace than in parent elements because of a locally scoped redefinition. You can use the NamespaceSupport helper class to manage this stack or write something of your own.

These information items start with a ContentHandler.startPrefixMapping() call and end with a ContentHandler.endPrefixMapping() call. These are the only two start/end calls that SAX2 doesn't require to be cleanly nested. Alternatively, if the namespaces feature flag is false, this information can be reconstructed from the xmlns and xmlns:* element attributes.

Property | Callbacks | Explanation
[prefix] | ContentHandler.startPrefixMapping(), prefix parameter |
[namespace name] | ContentHandler.startPrefixMapping(), uri parameter | Since these values aren't dereferenced, they are exactly as provided in the XML source text. Don't assume dereferencing such URIs lets you do anything useful.

If the namespaces feature is set to false (its default is true) this information is not made available except implicitly through the element [attributes] property, which will implicitly include all [namespace attributes]. (It is illegal to set namespaces to false without setting namespace-prefixes to true.)

Previous | Home | Next
B.12. Notation Information Items | Book Index | Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Colophon (SAX2)

 SAX2Search this book
Previous

Colophon

Our look is the result of reader comments, our own experimentation, and feedback from distribution channels. Distinctive covers complement our distinctive approach to technical topics, breathing personality and life into potentially dry subjects.

The animal on the cover of SAX2 is a pampas cat. Not much is known about this cat, as extensive studies on it have never been done. For instance, the pampas cat is believed to be mainly nocturnal and to live primarily on the ground. However, some pampas cats that reside in zoos have been observed as being somewhat active in daylight, as well as comfortable spending time in trees.

The pampas cat is native to South America. Its natural habitat allows for quite a range, as some species are found in grasslands, some in mountain regions, and some in swampy areas. It is not a large cat, weighing only between 7 and 8 pounds when fully grown. Its features are marked by a wide face and pointed ears. The color and markings on its fur are determined by the area in which it lives. For example, in the Andes Mountains, the cat is found with gray fur and red stripes; Brazilian cats are reddish-brown with black stripes; and cats living in Argentina are a light brown shade and have faint markings. The stripes always appear on the cat's legs and torso, and the cat has very long hair, which can grow up to three inches long. When the cat is frightened, these hairs stand on end, which makes the cat seem larger, as well as more menacing. This no doubt serves as a deterrent to predators.

The pampas cat population was dwindling for a time, as the cat was hunted for its skin. However, in 1980 laws were passed against this, so that particular threat to the species seems to have passed. Now the main danger is the growing human population, which is infringing on the pampas cat's home in the plains and forests.

Mary Brady was the production editor and proofreader and Melanie Wang was the copyeditor for SAX2 . Colleen Gorman, Matt Hutchinson, and Claire Cloutier provided quality control. Derek Di Matteo and Philip Dangler provided production support. Joe Wizda wrote the index.

Ellie Volckhausen designed the cover of this book, based on a series design by Edie Freedman. The cover image is a 19th-century engraving from Mammalia. Emma Colby produced the cover layout with QuarkXPress 4.1, using Adobe's ITC Garamond font.

Melanie Wang designed the interior layout based on a series design by Nancy Priest. The print version of this book was created by translating the DocBook XML markup of its source files into a set of gtroff macros, using a filter developed at O'Reilly & Associates by Norman Walsh. Steve Talbott designed and wrote the underlying macro set on the basis of the GNU troff -gs macros; Lenny Muellner adapted them to XML and implemented the book design. The GNU groff text formatter Version 1.11.1 was used to generate PostScript output. The text and heading fonts are ITC Garamond Light and Garamond Book. The illustrations that appear in the book were produced by Robert Romano and Jessamyn Read, using Macromedia FreeHand 9 and Adobe Photoshop 6. This colophon was written by Mary Brady.

Previous | Home
Index | Book Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX2: Index

SAX2Search this book

Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Symbols & Numbers

Symbols

= = (equal to), testing string equality: 3.3.2. XMLReader Feature Flags
!= (not equal to), testing string equality: 3.3.2. XMLReader Feature Flags
% (percent sign) in parameter entities: 3.3.2. XMLReader Feature Flags
4.3.1. The DeclHandler Interface
<!--...--> (comments), using comment( ) to report characters inside: 4.2. The LexicalHandler Interface

Numbers

80/20 rule for application requirements: 1.4.2. SAX2

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

addAttribute( ) function: 5.1.1. The AttributesImpl Class
AElfred Java parser: 1.4.1. SAX1
1.6.1. Ælfred2
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags
always validating parsers: 2.4.1. SAX2 Feature Flags
Apache
Cocoon v2 project: 4.5. XML Pipelines
Software License: 1.6.2. Crimson
XML project: 1.6.3. Xerces
APIs (Application Programming Interfaces): 1. The Simple API for XML
consumers and producers: 2.1. Producers and Consumers
high-level, flexibility of: 1.2.2. Data Structure Flexibility
JAXP (Java API for XML Processing): 1.4.5. Sun's Java API for XML Processing (JAXP)
types of XML: 1.1. Types of XML APIs
Application Programming Interface (see API)
Astral Planes: 4.1.3. Internationalization Concerns
Attribute Information Items: B.5. Attribute Information Items
Attribute interface: A.1.2. The Attributes Interface
attributeDecl( ) callback function: 4.3.1. The DeclHandler Interface
AttributeList interface: A.1.1. The AttributeList Interface
AttributeListImpl interface: A.2.1. The AttributeListImpl Interface
attributes: 2.3.3. The Attributes Interface
indexes: 2.3.3.2. Attribute lookup by index
namespaces and: 2.6. Namespaces and SAX2
2.6.2. Element and Attribute Naming with Namespaces
naming: 2.3.3.1. Attribute lookup by name
2.6.1. What Namespaces Do to XML
2.6.2.2. Attribute naming
Attributes atts element: 2.3.4. Essential ContentHandler Callbacks
Attributes method, using DeclHandler interface: 4.3.1. The DeclHandler Interface
Attributes.getLocalName( ) function: B.5. Attribute Information Items
Attributes.getType( ) function: 2.3.3.3. Other attributes issues
Attributes.getURI( ) function: B.5. Attribute Information Items
AttributesImpl class: 2.3.4. Essential ContentHandler Callbacks
3.5.2.2. Turning objects into SAX events
5.1.1. The AttributesImpl Class
A.2.2. The AttributesImpl Class

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

base URIs: B.2.2. Base URIs, xml:base, and Locator Data
BEEP: 6.2.2. Roles for Java in XML Messaging
BLOB (Binary Large Object), storing text as: 3.1.2.2. Providing entity text
Blueberry: 1.8. What XML Are We Talking About?
4.1.3. Internationalization Concerns
boolean feature flags: 3.3. Configuring XMLReader Behavior
5.1.3. The NamespaceSupport Class
boolean values: 3.3.2. XMLReader Feature Flags
Bosak, Jon: 1.4.1. SAX1
Bray, Tim: 1.4.1. SAX1
buf[] character array in LexicalHandler class: 4.2. The LexicalHandler Interface
bytes, in XML text: 1.2.3. Memory Consumption with SAX and DOM

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

C++ programming language: 1.4.4. Is SAX2 a "Standard"?
callbacks (event): 4. Consuming SAX2 Events
CallFilter class: 6.2.3. XML Messaging over HTTP with SAX2
CallWriter class: 6.2.3. XML Messaging over HTTP with SAX2
Canonicalization (XML): B.3. Document Information Item
CDATA attribute: 4.3.1. The DeclHandler Interface
CDF (Channel Definition Format): 6.1. Rich Site Summary: RSS
char buf[] character array: 2.3.4. Essential ContentHandler Callbacks
Character Information Items: B.8. Character Information Items
characters( ) function: 2.3.2. Example: Elements and Text
ignorableWhitespace( ) parameters and: 4.1.1. Other ContentHandler Methods
startCDATA( )/endCDATA() functions and: 4.2. The LexicalHandler Interface
Chemical Markup Language (CML): 1.4.1. SAX1
Clark, James: 1.4.1. SAX1
class paths, installing SAX 2.0: 1.7. Installing a SAX2 Parser
classes: 2.2.1. How Do the Parts Fit Together?
exception: 2.5.1. SAX2 Exception Classes
CML (Chemical Markup Language): 1.4.1. SAX1
Cocoon pipeline framework: 4.5. XML Pipelines
command lines, installing SAX 2.0: 1.7. Installing a SAX2 Parser
comment( ) function: 4.2. The LexicalHandler Interface
Comment Information Items: B.9. Comment Information Items
comments: 1.4.3. SAX2 Extensions
using comment( ) to report characters inside: 4.2. The LexicalHandler Interface
compound objects: 3.5.4. The XMLFilter Interface
consumers: 2.1. Producers and Consumers
SAX-to-DOM: 4.4.1. SAX-to-DOM Consumers
XMLFilterImpl class and: 4.5.1. The XMLFilterImpl Class
ContentHandler class: 2.2.1. How Do the Parts Fit Together?
2.2.3.2. Concerns when writing XML text
2.6.4. ContentHandler and Prefix Mappings
4.1. More About ContentHandler
A.1.3. The ContentHandler Interface
callbacks: 2.3.4. Essential ContentHandler Callbacks
4.1.1. Other ContentHandler Methods
DOM documents, building: 4.4.1. SAX-to-DOM Consumers
events: 2.3. Basic ContentHandler Events
example of elements and text: 2.3.2. Example: Elements and Text
internationalization and: 4.1.3. Internationalization Concerns
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT
ContentHandler.characters( ) function: B.8. Character Information Items
ContentHandler.endElement( ) function: 5.1.3. The NamespaceSupport Class
ContentHandler.ignorableWhitespace( ) function: B.8. Character Information Items
ContentHandler.processingInstruction( ): B.6. Processing Instruction Information Items
ContentHandler.skippedEntity( ) function: B.3. Document Information Item
ContentHandler.startDocument( ) function: 5.1.3. The NamespaceSupport Class
ContentHandler.startElement( ) function: B.4. Element Information Items
B.4. Element Information Items
AttributesImpl class and: 5.1.1. The AttributesImpl Class
parameters for element names: 2.6.2.1. Element naming
ContentHandler.startPrefixMapping( ) function: 5.1.3. The NamespaceSupport Class
Content.skippedEntity( ) function: B.7. Unexpanded Entity Reference Information Items
coupling (loose): 6.2.1. XML/Internet Versus Older Technologies
Crimson parser: 1.4.5. Sun's Java API for XML Processing (JAXP)
1.6.2. Crimson
4.4.1. SAX-to-DOM Consumers
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags
CSV (Comma Separated Values) files, turning into SAX events: 3.5.2.1. Turning CSV files into SAX events
custom data structures, turning SAX event into: 4.4.4. Turning SAX Events into Custom Data Structures

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

data modeling: 3.5.2.3. Data modeling concerns
data parameter for processingInstruction( ) function: 4.1.1. Other ContentHandler Methods
data structures: 1.1. Types of XML APIs
custom, turning events into: 4.4.4. Turning SAX Events into Custom Data Structures
flexibility: 1.2.2. Data Structure Flexibility
SAX events, turning into: 4.4. Turning SAX Events into Data Structures
DeclHandler interface: 4.3.1. The DeclHandler Interface
A.3.1. The DeclHandler class
DOM documents, building: 4.4.1. SAX-to-DOM Consumers
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT
DeclHandler.attributeDecl( ) function: 2.3.3.3. Other attributes issues
DeclHandler.externalEntityDexl( ) function: B.7. Unexpanded Entity Reference Information Items
DefaultHandler class: 2.2.1. How Do the Parts Fit Together?
2.3.1. The DefaultHandler Class
3.1.1. The XMLReader Interface
A.2.3. The DefaultHandler Class
deserializing: 4.4. Turning SAX Events into Data Structures
design tools, affecting runtime: 1.2.2. Data Structure Flexibility
diagnostics: 2.5.3. Errors and Diagnostics
Document Information Items: B.3. Document Information Item
Document Object Model (see DOM)
Document Type Declarations (see DTDs)
DocumentHandler interface: 5.2. SAX1 Support
A.1.4. The DocumentHandler Interface
DOM (Document Object Model): 1.1. Types of XML APIs
building partial: 4.4.3. Building a Partial DOM
consumer classes: 4.4.1. SAX-to-DOM Consumers
event production and: 3.5.1. DOM-to-SAX Event Production (and DOM4J, JDOM)
memory consumption with SAX: 1.2.3. Memory Consumption with SAX and DOM
DOM trees: 4.4.1. SAX-to-DOM Consumers
constructing with SAX 1.0: 1.4.2. SAX2
pruning noise data from: 4.4.2. Pruning Noise Data from a DOM Tree
SAX events, turning into: 3.5.1.1. Turning DOM trees into SAX events
DOM4J: 4.4.1. SAX-to-DOM Consumers
trees, turning into SAX events: 3.5.1.2. Turning DOM4J trees into SAX events
DOS
filenames, turning into URIs: 3.1.3. Filenames Versus URIs
XML output, writing: 2.2.3.2. Concerns when writing XML text
DTD-based validation: 2.4. Producer-Side Validation
DTDHandler interface: 2.2.2. What Are the SAX2 Event Handlers?
3.1.1. The XMLReader Interface
4.3.2. The DTDHandler Interface
A.1.5. The DTDHandler Interface
DOM documents, building: 4.4.1. SAX-to-DOM Consumers
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT
DTDHandler.notationDecl( ) function: 4.1.1. Other ContentHandler Methods
B.12. Notation Information Items
DTDHandler.unparsedEntityDexl( ) function: B.11. Unparsed Entity Information Items
DTDs (Document Type Declarations): 1.8. What XML Are We Talking About?
4.3. Exposing DTD Information
EntityReslover class, using: 3.4. The EntityResolver Interface
Information Items: B.10. Document Type Declaration Information Item
producer-side validation and: 2.4. Producer-Side Validation
subdocuments, including: 6.3. Including Subdocuments

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

EBCDIC (Extended Binary Coded Decimal Interchange Code) encodings: 3.1.2.2. Providing entity text
elementDecl( ) function: 4.3.1. The DeclHandler Interface
elements: 2.3.2. Example: Elements and Text
Information Items: B.4. Element Information Items
naming: 2.6.1. What Namespaces Do to XML
with namespaces: 2.6.2. Element and Attribute Naming with Namespaces
endCDATA( ) function: 4.2. The LexicalHandler Interface
endDocument( ) function: 3.5.2.1. Turning CSV files into SAX events
4.1.1. Other ContentHandler Methods
endDTD( ) function: 4.2. The LexicalHandler Interface
endElement( ) function: 2.3.2. Example: Elements and Text
2.3.4. Essential ContentHandler Callbacks
2.6.4. ContentHandler and Prefix Mappings
endEntity( ) function: 4.2. The LexicalHandler Interface
endPrefixMapping( ) function: 2.6.3. Namespace Feature Flags
2.6.4. ContentHandler and Prefix Mappings
3.3.2. XMLReader Feature Flags
ENTITIES attribute: 4.3.1. The DeclHandler Interface
unparsedEntityDecl( ) and: 4.3.2. The DTDHandler Interface
ENTITY attribute: 4.3.1. The DeclHandler Interface
unparsedEntityDecl( ) and: 4.3.2. The DTDHandler Interface
EntityResolver interface: 3.1.1. The XMLReader Interface
3.3.2. XMLReader Feature Flags
3.4. The EntityResolver Interface
A.1.6. The EntityResolver Interface
InputSource objects, creating with: 3.1.2.2. Providing entity text
EntityResolver objects: 6.3. Including Subdocuments
enumerated values in attributeDecl( ): 4.3.1. The DeclHandler Interface
error handling: 1.2.1. Stream-Based Processing
2.4. Producer-Side Validation
diagnostics and: 2.5.3. Errors and Diagnostics
producer-side validation and: 2.4.2. Handling Validity Errors
error( ) method: 2.5.2. ErrorHandler Interface
ErrorHandler class: 2.2.1. How Do the Parts Fit Together?
2.5.2. ErrorHandler Interface
2.5.2. ErrorHandler Interface
2.5.3. Errors and Diagnostics
3.1.1. The XMLReader Interface
A.1.7. The ErrorHandler Interface
Locator class and: 4.1.2. The Locator Interface
producer-side validation, handling errors: 2.4.2. Handling Validity Errors
ErrorHandler object: 6.3. Including Subdocuments
ErrorHandler.error( ) function: 2.6.3. Namespace Feature Flags
event pipelines: 2.2.3.1. Event pipelines
event producers: 3.1. Pull Mode Event Production with XMLReader
B.1. Event Producer Issues
defining custom: 1.2.1. Stream-Based Processing
DOM-to-SAX: 3.5.1. DOM-to-SAX Event Production (and DOM4J, JDOM)
push modes: 3.5.2. Push Mode Event Production
event streams, producing: 3.5.3. Producing Well-Formed Event Streams
EventConsumer interface: 4.5.4. The gnu.xml.pipeline Framework
events: 4. Consuming SAX2 Events
consumer issues: B.2. Event Consumer Issues
CSV files, turning into: 3.5.2.1. Turning CSV files into SAX events
custom data structures, turning into: 4.4.4. Turning SAX Events into Custom Data Structures
data structures, turning into: 4.4. Turning SAX Events into Data Structures
DOM trees, turning into: 3.5.1.1. Turning DOM trees into SAX events
objects, turning into: 3.5.2.2. Turning objects into SAX events
pipelines: 4.5. XML Pipelines
XMLFilterImpl class and: 4.5.1. The XMLFilterImpl Class
exception handling: 2.5. Exception Handling
classes: 2.5.1. SAX2 Exception Classes
Extended Binary Coded Decimal Interchange Code (EBCDIC) encodings: 3.1.2.2. Providing entity text
Extensible Markup Language (see XML)
extensions directory, adding to when installing SAX 2.0: 1.7. Installing a SAX2 Parser
external subsets: 4.2. The LexicalHandler Interface
externalEntityDecl( ) callback function: 4.3.1. The DeclHandler Interface

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

fatal errors, handling: 1.2.1. Stream-Based Processing
fatalError( ) method: 2.5.2. ErrorHandler Interface
feature flags: 2.4.1. SAX2 Feature Flags
namespaces: 2.6.3. Namespace Feature Flags
XMLReader class: 3.3.2. XMLReader Feature Flags
file: (scheme): 2.4.1. SAX2 Feature Flags
filenames: 3.1.3. Filenames Versus URIs
filters (pipelines): 4.5. XML Pipelines
#FIXED attribute: 4.3.1. The DeclHandler Interface
flags: 2.4.1. SAX2 Feature Flags
namespaces: 2.6.3. Namespace Feature Flags
XMLReader class: 3.3.2. XMLReader Feature Flags
flat file text formats: 3.5.2.1. Turning CSV files into SAX events
Formal Public Identifiers (FPIs): 3.4. The EntityResolver Interface
SGML: 4.3.1. The DeclHandler Interface
forward-only event streams: 1.3. Why Not to Choose SAX?
FPIs (Formal Public Identifiers): 3.4. The EntityResolver Interface
ftp: (scheme): 2.4.1. SAX2 Feature Flags

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

GCC Java (GCJ): 1.6.1. Ælfred2
GCJ (GNU General Public License): 1.6.1. Ælfred2
get*( ) method: 3.3. Configuring XMLReader Behavior
GET request: 6.2.3. XML Messaging over HTTP with SAX2
getBaseURI( ) function: 5.1.1. The AttributesImpl Class
getCause( ) method: 2.5.1. SAX2 Exception Classes
getColumnNumber( ) function: 4.1.2. The Locator Interface
getDTDHandler( ) function: 3.1.1. The XMLReader Interface
getEntityResolver( ) function: 3.1.1. The XMLReader Interface
getErrorHandler( ) function: 3.1.1. The XMLReader Interface
getException( ) function: 2.5.1. SAX2 Exception Classes
getFeature( ) function: 3.1.1. The XMLReader Interface
namespace feature flags: 2.6.3. Namespace Feature Flags
getIndex( ) function: 2.3.3.1. Attribute lookup by name
getLength( ) function: 2.3.3.2. Attribute lookup by index
getLineNumber( ) function: 4.1.2. The Locator Interface
getLocalName( ) function, naming attributes: 2.6.2.2. Attribute naming
getPrefix( ) function: 5.1.3. The NamespaceSupport Class
getProperty( ) function: 3.1.1. The XMLReader Interface
getPublicId( ) function: 4.1.2. The Locator Interface
getQName( ) function, naming attributes: 2.6.2.2. Attribute naming
getSystemId( ) function: 4.1.2. The Locator Interface
getType( ) function: 2.3.3.1. Attribute lookup by name
getURI( ) function, naming attributes: 2.6.2.2. Attribute naming
getValue( ) function: 2.3.3.1. Attribute lookup by name
GNU Classpath Extensions project: 1.6.1. Ælfred2
GNU General Public License (GCJ): 1.6.1. Ælfred2
GNU pipeline framework: 4.5. XML Pipelines
GNUJAXP: 4.4.1. SAX-to-DOM Consumers
gnujaxp.jar file: 1.6.1. Ælfred2
XMLWriter and: 2.2.3. XMLWriter: an Event Consumer
gnu.xml.pipeline framework: 4.5.4. The gnu.xml.pipeline Framework
gnu.xml.pipeline.CallFilter class: 6.2.3. XML Messaging over HTTP with SAX2
gun.xml.dom.Consumer class: 4.4.1. SAX-to-DOM Consumers

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

HandlerBase interface: A.1.8. The HandlerBase Interface
handlers: 2.1. Producers and Consumers
2.2.2. What Are the SAX2 Event Handlers?
ContentHandler callbacks: 2.3.4. Essential ContentHandler Callbacks
errors: 2.4. Producer-Side Validation
diagnostics and: 2.5.3. Errors and Diagnostics
validity (producer-side): 2.4.2. Handling Validity Errors
exception: 2.5. Exception Handling
helper classes: 5.1. Helper Classes
high-level APIs: 1.1. Types of XML APIs
flexibility of: 1.2.2. Data Structure Flexibility
high surrogate (Java char values): 4.1.3. Internationalization Concerns
HotJava web browser: 1.6.2. Crimson
HTML (HyperText Markup Language): 3.5. Other Kinds of SAX2 Event Producers
HTML Tidy tool: 3.5. Other Kinds of SAX2 Event Producers
HTTP protocol: 2.4.1. SAX2 Feature Flags
6.2.1. XML/Internet Versus Older Technologies
messaging and: 6.2.3. XML Messaging over HTTP with SAX2
HyperText Markup Language (HTML): 3.5. Other Kinds of SAX2 Event Producers

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

IANA Internet encoding names: 3.1.2.2. Providing entity text
ID attribute: 4.3.1. The DeclHandler Interface
IDREF attribute: 4.3.1. The DeclHandler Interface
IDREFS attribute: 4.3.1. The DeclHandler Interface
IETF (Internet Engineering Task Force): 1.4.1. SAX1
ignorableWhitespace( ) callback function: 4.1.1. Other ContentHandler Methods
4.4.2. Pruning Noise Data from a DOM Tree
#IMPLIED attribute: 4.3.1. The DeclHandler Interface
5.1.1. The AttributesImpl Class
indexes, looking up attributes witg: 2.3.3.2. Attribute lookup by index
Infoset (XML): 1.2.1. Stream-Based Processing
1.8. What XML Are We Talking About?
B. SAX2 and the XML Infoset
input normalization: B.3. Document Information Item
InputSource class: 3.1.2. The InputSource Class
A.1.9. The InputSource Class
entity text, providing: 3.1.2.2. Providing entity text
EntityResolver interface and: 3.4. The EntityResolver Interface
methods for: 3.1.2.1. Always provide absolute URIs
Inputsource.getEncoding( ) function: B.3. Document Information Item
InputStreamReader class, providing entity text: 3.1.2.2. Providing entity text
interface facility, working with multiple products: 1.1. Types of XML APIs
interfaces: 2.2.1. How Do the Parts Fit Together?
ErrorHandler: 2.5.2. ErrorHandler Interface
internal subsets: 4.2. The LexicalHandler Interface
internalEntityDecl( ) function: 4.3.1. The DeclHandler Interface
International Standards Organization (ISO): 1.4.4. Is SAX2 a "Standard"?
internationalization: 4.1.3. Internationalization Concerns
Internet: 6.2.1. XML/Internet Versus Older Technologies
Internet Engineering Task Force (IETF): 1.4.1. SAX1
Internet xml-dev mailing list, developing SAX 1.0: 1.4.1. SAX1
ISO (International Standards Organization): 1.4.4. Is SAX2 a "Standard"?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

J2EE (Java2 Enterprise Edition): 1.4.5. Sun's Java API for XML Processing (JAXP)
Java2 Enterprise Edition (J2EE): 1.4.5. Sun's Java API for XML Processing (JAXP)
Java in XML messaging: 6.2.2. Roles for Java in XML Messaging
Java parsers: 1.4.1. SAX1
Java Project X: 1.6.2. Crimson
java.io.CharArrayReader method for InputSource class: 3.1.2.2. Providing entity text
java.io.InputStream in method for InputStream class: 3.1.2.2. Providing entity text
java.io.IOException class: 3.4. The EntityResolver Interface
java.io.Reader class: 2.2.1. How Do the Parts Fit Together?
3.1. Pull Mode Event Production with XMLReader
InputSource class and: 3.1.2.2. Providing entity text
installing SAX 2.0: 1.7. Installing a SAX2 Parser
java.lang.Character class, using surrogate pairs: 4.1.3. Internationalization Concerns
java.lang.String class, using surrogate pairs: 4.1.3. Internationalization Concerns
JavaScript language: 1.4.4. Is SAX2 a "Standard"?
javax.xml.parsers.SAXParserFactory, using JAXP: 3.2.3. Using JAXP
javax.xml.transform.sax package: 4.5.3. The javax.xml.transform.sax Package
JAXP (Java API for XML Processing): 1.4.5. Sun's Java API for XML Processing (JAXP)
3.2.3. Using JAXP
jaxp.jar file: 1.6.2. Crimson
JDOM: 4.4.1. SAX-to-DOM Consumers
trees, turning into SAX events: 3.5.1.3. Turning JDOM trees into SAX events
.jpeg files, declaring with NDATA annotations: 4.3.2. The DTDHandler Interface
Jumbo XML browser: 1.4.1. SAX1

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

LANs (local area networks): 6.2.1. XML/Internet Versus Older Technologies
Lark Java parser: 1.4.1. SAX1
late binding of handlers: 3.1.1. The XMLReader Interface
LexicalHandler class: 3.3.2. XMLReader Feature Flags
4.2. The LexicalHandler Interface
A.3.2. The LexicalHandler Interface
DOM documents, building: 4.4.1. SAX-to-DOM Consumers
DTD data and: 4.3. Exposing DTD Information
push mode with XSLT and: 4.5.3.1. SAX in Push-Mode with XSLT
LexicalHandler.comment( ) function: B.9. Comment Information Items
LexicalHandler.endDTD( ) function: B.3. Document Information Item
LexicalHandler.startDTD( ) function: B.10. Document Type Declaration Information Item
LexicalHandler.startEntity( ) function: 4.3.1. The DeclHandler Interface
local area networks (LANs): 6.2.1. XML/Internet Versus Older Technologies
localName parameter: B.4. Element Information Items
Locator class: 4.1.1. Other ContentHandler Methods
4.1.2. The Locator Interface
A.1.10. The Locator Interface
locator data: B.2.2. Base URIs, xml:base, and Locator Data
Locator.getSystemId( ) function: B.3. Document Information Item
B.7. Unexpanded Entity Reference Information Items
B.12. Notation Information Items
LocatorImpl class: 5.1.2. The LocatorImpl Class
A.2.4. The LocatorImpl Class
loose coupling: 6.2.1. XML/Internet Versus Older Technologies
low surrogate (Java char values): 4.1.3. Internationalization Concerns

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

Macintosh
filenames, turning into URIs: 3.1.3. Filenames Versus URIs
XML output, writing: 2.2.3.2. Concerns when writing XML text
marshalling: 3.5.2.2. Turning objects into SAX events
MathML: 4.1.3. Internationalization Concerns
Megginson, David: 1.4.1. SAX1
memory
consuming, with SAX and DOM: 1.2.3. Memory Consumption with SAX and DOM
SAX parsers, using: 1.2.1. Stream-Based Processing
messaging: 6.2. XML and Messaging
HTTP with SAX 2.0 and: 6.2.3. XML Messaging over HTTP with SAX2
Java, roles for: 6.2.2. Roles for Java in XML Messaging
methods
attribute names, accessing: 2.6.2.2. Attribute naming
InputSource class: 3.1.2.1. Always provide absolute URIs
Microsoft, shipping CDFs: 6.1. Rich Site Summary: RSS
mixed mode: 2.6.3. Namespace Feature Flags
MSXML Java parser: 1.4.1. SAX1
multithreaded applications, using SAX with: 1.2.4. Other Reasons to Prefer SAX
My Netscape, using RSS: 6.1. Rich Site Summary: RSS

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

namespace flag: 2.6.3. Namespace Feature Flags
namespace-prefixes feature: 3.3.2. XMLReader Feature Flags
namespaces: 1.8. What XML Are We Talking About?
2.4.1. SAX2 Feature Flags
2.6. Namespaces and SAX2
feature flags: 2.6.3. Namespace Feature Flags
Information Items: B.13. Namespace Information Items
NamespaceSupport class and: 5.1.3. The NamespaceSupport Class
naming attributes and elements with: 2.6.2. Element and Attribute Naming with Namespaces
specification of: 1.4.2. SAX2
NamespaceSupport class: 3.5.2.2. Turning objects into SAX events
5.1.3. The NamespaceSupport Class
A.2.5. The NamespaceSupport Class
namespaceURI parameter: B.4. Element Information Items
naming
attributes: 2.3.3.1. Attribute lookup by name
2.6.2.2. Attribute naming
elements: 2.6.1. What Namespaces Do to XML
with namespaces: 2.6.2. Element and Attribute Naming with Namespaces
NDATA attribute: 4.3.2. The DTDHandler Interface
Netscape, shipping RSS: 6.1. Rich Site Summary: RSS
NMTOKEN attribute: 4.3.1. The DeclHandler Interface
NMTOKENS attribute: 4.3.1. The DeclHandler Interface
noise data, pruning from DOM trees: 4.4.2. Pruning Noise Data from a DOM Tree
nonvalidating parsers: 2.4.1. SAX2 Feature Flags
NOTATION attribute: 4.3.2. The DTDHandler Interface
Notation Information Items: B.12. Notation Information Items
notationDecl( ) callback function: 4.3.2. The DTDHandler Interface
NSFilter class: 6.2.3. XML Messaging over HTTP with SAX2
null, setting handlers: 3.1.1. The XMLReader Interface
NullPointerException class: 3.1.1. The XMLReader Interface

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

OASIS group: 1.4.1. SAX1
OASIS SGML/Open Catalog (SOCAT): 3.4. The EntityResolver Interface
object tree parsers API: 1.1. Types of XML APIs
object values: 3.3.2. XMLReader Feature Flags
objects, turning into SAX events: 3.5.2.2. Turning objects into SAX events
Open Directory Project: 6.1. Rich Site Summary: RSS
optionally validating parsers: 2.4.1. SAX2 Feature Flags
org.apache.crimson.tree.Xml-DocumentBuilder class: 4.4.1. SAX-to-DOM Consumers
org.dom4j.io.SAXContentHandler class: 4.4.1. SAX-to-DOM Consumers
org.dom4j.io.XMLWriter: 2.2.3. XMLWriter: an Event Consumer
org.jdom.input.SAXHandler: 4.4.1. SAX-to-DOM Consumers
org.xml.sax package: 1.4.1. SAX1
1.5. Packages in the SAX2 API
A.1. The org.xml.sax Package
org.xml.sax.AttributeList class: 5.2. SAX1 Support
org.xml.sax.ContentHandler: 2.2.2. What Are the SAX2 Event Handlers?
org.xml.sax.DocumentHandler class: 5.2. SAX1 Support
org.xml.sax.driver class: 3.2.1. The XMLReaderFactory Class
org.xml.sax.DTDHandler: 2.2.2. What Are the SAX2 Event Handlers?
org.xml.sax.ErrorHandler: 2.2.2. What Are the SAX2 Event Handlers?
org.xml.sax.ext package: 1.5. Packages in the SAX2 API
A.3. The org.xml.sax.ext Package
org.xml.sax.ext.DeclHandler: 2.2.2. What Are the SAX2 Event Handlers?
org.xml.sax.ext.LexicalHandler: 2.2.2. What Are the SAX2 Event Handlers?
org.xml.sax.HandlerBase class: 5.2. SAX1 Support
org.xml.sax.helpers package: 1.5. Packages in the SAX2 API
A.2. The org.xml.sax.helpers Package
org.xml.sax.helpers.AttributeListImpl class: 5.2. SAX1 Support
org.xml.sax.helpers.ParserAdapter class: 5.2. SAX1 Support
org.xml.sax.helpers.ParserFactory class: 5.2. SAX1 Support
org.xml.sax.helpers.XMLReaderAdapter class: 5.2. SAX1 Support
org.xml.sax.Parser class: 3.1. Pull Mode Event Production with XMLReader
5.2. SAX1 Support
org.xml.sax.SAXException class: 2.5.1. SAX2 Exception Classes
3.4. The EntityResolver Interface
org.xml.sax.SAXNotRecognizedException class: 2.5.1. SAX2 Exception Classes
org.xml.sax.SAXNotSupportedException class: 2.5.1. SAX2 Exception Classes
org.xml.sax.SAXParseException class: 2.5.1. SAX2 Exception Classes
org.xml.sax.XMLReader class: 3.1. Pull Mode Event Production with XMLReader
output encoding: B.3. Document Information Item
output, when writing XML text: 2.2.3.2. Concerns when writing XML text

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

P2P (peer-to-peer): 6.2.1. XML/Internet Versus Older Technologies
parse( ) function: 2.5.1. SAX2 Exception Classes
3.1. Pull Mode Event Production with XMLReader
3.1.1. The XMLReader Interface
EntityResolver class, passing InputSource objects to: 3.1.2.2. Providing entity text
InputSource class and: 3.1.2. The InputSource Class
parser configuration: 2.4. Producer-Side Validation
Parser interface: A.1.11. The Parser Interface
parser-level APIs: 1.1. Types of XML APIs
ParserAdapter class: 5.2. SAX1 Support
A.2.6. The ParserAdapter Class
ParserFactory class: 5.2. SAX1 Support
A.2.7. The ParserFactory Class
parsers: 1. The Simple API for XML
advantages of SAX: 1.2.1. Stream-Based Processing
distributions for SAX 2.0: 1.6. Some Popular SAX2 Parser Distributions
feature flags and: 2.4.1. SAX2 Feature Flags
SAX 2.0: 1.4.2. SAX2
installing: 1.7. Installing a SAX2 Parser
Pascal language: 1.4.4. Is SAX2 a "Standard"?
passive APIs: 1.1. Types of XML APIs
peer-to-peer (P2P): 6.2.1. XML/Internet Versus Older Technologies
percent sign (%) in parameter entities: 3.3.2. XMLReader Feature Flags
4.3.1. The DeclHandler Interface
Perl language: 1.4.4. Is SAX2 a "Standard"?
PHP, syntax of: 4.1.1. Other ContentHandler Methods
pipeline stage: 4.5. XML Pipelines
pipelines: 4.5. XML Pipelines
events: 2.2.3.1. Event pipelines
using SAX parsers: 1.2.1. Stream-Based Processing
.png files, declaring with NDATA annotations: 4.3.2. The DTDHandler Interface
popContext( ) function: 5.1.3. The NamespaceSupport Class
POST (HTTP) request: 6.2.3. XML Messaging over HTTP with SAX2
Post-Schema-Validation Infoset (PSVI): 2.4. Producer-Side Validation
B. SAX2 and the XML Infoset
prefix mapping: 2.6.4. ContentHandler and Prefix Mappings
procedural logic: 2.4. Producer-Side Validation
Processing Instruction Information Items: B.6. Processing Instruction Information Items
processingInstruction( ) function: 4.1.1. Other ContentHandler Methods
processName( ) function: 5.1.3. The NamespaceSupport Class
producer-side validation: 2.4. Producer-Side Validation
handling errors for: 2.4.2. Handling Validity Errors
producer.parse ( ): 2.2.1. How Do the Parts Fit Together?
producers: 2.1. Producers and Consumers
producing events: 3. Producing SAX2 Events
property objects: 3.3. Configuring XMLReader Behavior
PSVI (Post-Schema-Validation Infoset): 2.4. Producer-Side Validation
B. SAX2 and the XML Infoset
public IDs for entities: 4.3.1. The DeclHandler Interface
publicId parameter: B.10. Document Type Declaration Information Item
pull mode event producer
with XMLReader: 3.1. Pull Mode Event Production with XMLReader
with XSLT: 4.5.3.2. SAX in Pull-Mode with XSLT
pull-to-push adapter: 3.1. Pull Mode Event Production with XMLReader
push mode event producers: 3.1. Pull Mode Event Production with XMLReader
3.5.2. Push Mode Event Production
with XSLT: 4.5.3.1. SAX in Push-Mode with XSLT
pushContext( ) function: 5.1.3. The NamespaceSupport Class
Python language: 1.4.4. Is SAX2 a "Standard"?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

qualified names, naming attributes and elements: 2.6.2. Element and Attribute Naming with Namespaces

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

random access, providing for XML data: 1.3. Why Not to Choose SAX?
RDF (Resource Description Framework) Site Summary: 6.1. Rich Site Summary: RSS
Remote Procedure Call (RPC): 6.2.1. XML/Internet Versus Older Technologies
#REQUIRED attribute: 4.3.1. The DeclHandler Interface
reset( ) function: 5.1.3. The NamespaceSupport Class
Resource Description Framework (RDF) Site Summary: 6.1. Rich Site Summary: RSS
Rich Site Summary (see RSS)
RPC (Remote Procedure Call): 6.2.1. XML/Internet Versus Older Technologies
RSS (Rich Site Summary): 6.1. Rich Site Summary: RSS
applications, building with: 6.1.3. Building Applications with RSS
data model for: 6.1.1. Data Model for RSS Classic
parsing events, consuming and producing: 6.1.2. Consuming and Producing RSS Parsing Events
RssConsumer class: 6.1.2. Consuming and Producing RSS Parsing Events
RssHandler interface: 6.1.2. Consuming and Producing RSS Parsing Events
rule-based logic: 2.4. Producer-Side Validation

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

SAX 2.0: 1.4.2. SAX2
extensions: 1.4.3. SAX2 Extensions
installing: 1.7. Installing a SAX2 Parser
introducing: 2. Introducing SAX2
parser distributions for: 1.6. Some Popular SAX2 Parser Distributions
SAX parsers: 3.1. Pull Mode Event Production with XMLReader
SAX (Simple API for XML)
APIs and: 1. The Simple API for XML
history of: 1.4. A Short History of SAX
SAXException class: 2.4.2. Handling Validity Errors
2.5.1. SAX2 Exception Classes
3.1.1. The XMLReader Interface
A.1.12. SAXException
error and diagnostics when using: 2.5.3. Errors and Diagnostics
SAXNotRecognizedException: A.1.13. SAXNotRecognizedException
SAXNotSupportedException: A.1.14. SAXNotSupportedException
SAXON Java XSLT engine: 4.5.3. The javax.xml.transform.sax Package
SAXParseException: A.1.15. SAXParseException
SAXParseException class: 2.5.3. Errors and Diagnostics
Locator class and: 4.1.2. The Locator Interface
SAXResult class: 4.5.3.1. SAX in Push-Mode with XSLT
SAXTransformerFactory class: 4.5.3. The javax.xml.transform.sax Package
4.5.3.2. SAX in Pull-Mode with XSLT
schemes: 2.4.1. SAX2 Feature Flags
EntityResolver interface and: 3.4. The EntityResolver Interface
serialization: 3.5.2. Push Mode Event Production
set*( ) method: 3.3. Configuring XMLReader Behavior
setDocumentLocator( ) callback method: 4.1.1. Other ContentHandler Methods
setEncoding( ) function: 3.1.2.2. Providing entity text
setFeature( ) function: 2.4.1. SAX2 Feature Flags
3.1.1. The XMLReader Interface
namespace feature flags and: 2.6.3. Namespace Feature Flags
setLocale( ) function: 5.2. SAX1 Support
setProperty( ) function: 3.1.1. The XMLReader Interface
3.2.1. The XMLReaderFactory Class
setSystemId( ) function: 3.1.2.2. Providing entity text
SGML Formal Public Identifiers: 4.3.1. The DeclHandler Interface
skippedEntity( ) function: 4.1.1. Other ContentHandler Methods
SOAP: 6.2.2. Roles for Java in XML Messaging
SOCAT (OASIS SGML/Open Catalog): 3.4. The EntityResolver Interface
sockets network API: 1.4.4. Is SAX2 a "Standard"?
stages (pipeline): 4.5. XML Pipelines
startCDATA( ) function: 4.2. The LexicalHandler Interface
startDocument( ) function: 4.1.1. Other ContentHandler Methods
startDTD( ) function: 4.2. The LexicalHandler Interface
startElement( ) function: 2.3.2. Example: Elements and Text
2.3.2. Example: Elements and Text
2.3.3.2. Attribute lookup by index
2.3.4. Essential ContentHandler Callbacks
2.6.4. ContentHandler and Prefix Mappings
attributeDecl( ) and: 4.3.1. The DeclHandler Interface
NamespaceSupport class and: 5.1.3. The NamespaceSupport Class
using for elements and naming: 2.6.1. What Namespaces Do to XML
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags
startEntity( ) function: 4.2. The LexicalHandler Interface
startPrefixMapping( ) function: 2.6.3. Namespace Feature Flags
2.6.4. ContentHandler and Prefix Mappings
2.6.4. ContentHandler and Prefix Mappings
3.3.2. XMLReader Feature Flags
stream-based processing: 1.2.1. Stream-Based Processing
stream validator in Ælfred parsers: 1.6.1. Ælfred2
streaming parsers API: 1.1. Types of XML APIs
String class, parsing data with no URI: 3.1.2. The InputSource Class
String local element: 2.3.4. Essential ContentHandler Callbacks
String qName element: 2.3.4. Essential ContentHandler Callbacks
String uri element: 2.3.4. Essential ContentHandler Callbacks
StringBuffers: 2.3.4. Essential ContentHandler Callbacks
String.equals( ) function: 3.3.2. XMLReader Feature Flags
String.intern( ) function: 3.3.2. XMLReader Feature Flags
StringReader class: 4.1.2. The Locator Interface
subdocuments, including: 6.3. Including Subdocuments
Sun, using JAXP: 1.4.5. Sun's Java API for XML Processing (JAXP)
surrogate pairs (Java char values): 4.1.3. Internationalization Concerns
system ID for notations: 4.3.2. The DTDHandler Interface
systemId parameter: 3.1.2.1. Always provide absolute URIs
B.10. Document Type Declaration Information Item
EntityResolver interface and: 3.4. The EntityResolver Interface
System.setProperty( ) function: 3.2.1. The XMLReaderFactory Class

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

target parameter for processingInstruction( ) function: 4.1.1. Other ContentHandler Methods
text: 2.3.2. Example: Elements and Text
entity text, providing with InputSource: 3.1.2.2. Providing entity text
input normalization: B.3. Document Information Item
writing XML output: 2.2.3.2. Concerns when writing XML text
TransformerHandler class: 4.5.3.1. SAX in Push-Mode with XSLT
Transformer.transform( ) function: 4.5.3. The javax.xml.transform.sax Package
4.5.3.2. SAX in Pull-Mode with XSLT
TRAX API: 1.4.5. Sun's Java API for XML Processing (JAXP)

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

Unexpanded Entity Reference Information Items: B.7. Unexpanded Entity Reference Information Items
Unicode for internationalization: 4.1.3. Internationalization Concerns
Unicode Normalization Form C: B.3. Document Information Item
internationalizing character encodings: 4.1.3. Internationalization Concerns
universal names for attributes and elements: 2.6.2. Element and Attribute Naming with Namespaces
Universal Resource Identifier (see URI)
Universal Resource Locator (URL): 2.4.1. SAX2 Feature Flags
Universal Resource Name (see URM)
UNIX, writing XML output: 2.2.3.2. Concerns when writing XML text
unknown validation behavior: 2.4.1. SAX2 Feature Flags
unmarshaling: 3.5.2.2. Turning objects into SAX events
4.4. Turning SAX Events into Data Structures
unparsed entity information items: B.11. Unparsed Entity Information Items
unparsedEntityDecl( ) callback function: 4.3.2. The DTDHandler Interface
URI (Universal Resource Identifier): 2.4.1. SAX2 Feature Flags
base: B.2.2. Base URIs, xml:base, and Locator Data
defining new handlers and features: 3.3. Configuring XMLReader Behavior
filenames vs.: 3.1.3. Filenames Versus URIs
InputSource class and: 3.1.2.1. Always provide absolute URIs
namespaces and: 2.6.1. What Namespaces Do to XML
URL (Universal Resource Locator): 2.4.1. SAX2 Feature Flags
URNs (Universal Resource Names): 2.4.1. SAX2 Feature Flags
FPIs (Formal Public Identifiers) and: 3.4. The EntityResolver Interface
UTF-8/16 character encoding: 2.2.3.2. Concerns when writing XML text
3.1.2.2. Providing entity text
B.3. Document Information Item

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

validation (producer-side): 2.4. Producer-Side Validation
handling errors for: 2.4.2. Handling Validity Errors

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

warning( ) method: 2.5.2. ErrorHandler Interface
W3C Character Model, internationalizing character encodings: 4.1.3. Internationalization Concerns
web site for SAX: 1.4. A Short History of SAX
whitespace: 4.1.1. Other ContentHandler Methods
wrapped exceptions: 2.5.1. SAX2 Exception Classes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

Xerces parser: 1.6.3. Xerces
XMLReader feature flags and: 3.3.2. XMLReader Feature Flags
3.3.2. XMLReader Feature Flags
XHTML: 3.5. Other Kinds of SAX2 Event Producers
XInclude: 1.8. What XML Are We Talking About?
6.3. Including Subdocuments
XML 1.0
Blueberry (see Blueberry)
XML 1.0 mode: 2.6.3. Namespace Feature Flags
XML Canonicalization: B.3. Document Information Item
XML (Extensible Markup Language)
APIs for: 1. The Simple API for XML
Infoset: 1.2.1. Stream-Based Processing
1.8. What XML Are We Talking About?
B. SAX2 and the XML Infoset
Internet versus older technologies: 6.2.1. XML/Internet Versus Older Technologies
Java, roles for in messaging: 6.2.2. Roles for Java in XML Messaging
JAXP, using for: 1.4.5. Sun's Java API for XML Processing (JAXP)
messaging and: 6.2. XML and Messaging
Namespaces (see Namespaces)
piplines: 4.5. XML Pipelines
related standards of: 1.8. What XML Are We Talking About?
SAX parsers and: 1.2.1. Stream-Based Processing
validity and: 2.4. Producer-Side Validation
XML Infoset: 1.8. What XML Are We Talking About?
XML Namespaces: 1.8. What XML Are We Talking About?
specification of: 1.4.2. SAX2
XML plus namespace mode: 2.6.3. Namespace Feature Flags
XML-RPC: 6.2.2. Roles for Java in XML Messaging
XML Schema Datatypes (XSD): 2.6.4. ContentHandler and Prefix Mappings
xml:base attribute: 1.8. What XML Are We Talking About?
5.1.1. The AttributesImpl Class
5.1.1. The AttributesImpl Class
B.2.2. Base URIs, xml:base, and Locator Data
XMLFilter interface: 3.5.4. The XMLFilter Interface
A.1.16. The XMLFilter Interface
pull modes with XSLT and: 4.5.3.2. SAX in Pull-Mode with XSLT
XMLFilterImpl class: 3.5.4. The XMLFilter Interface
4.5.1. The XMLFilterImpl Class
6.1.2. Consuming and Producing RSS Parsing Events
A.2.8. The XMLFilterImpl Class
examples of: 4.5.2. XMLFilter Examples
XML4J (XML for Java) parser: 1.6.3. Xerces
xml.jar, installing SAX 2.0: 1.7. Installing a SAX2 Parser
xml:lang attribute: 5.1.1. The AttributesImpl Class
xmlns attributes, using XMLReader feature flags: 3.3.2. XMLReader Feature Flags
XMLReader class: 2.2.1. How Do the Parts Fit Together?
A.1.17. The XMLReader Interface
configuring behavior: 3.3. Configuring XMLReader Behavior
feature flags: 3.3.2. XMLReader Feature Flags
functional groups: 3.1.1. The XMLReader Interface
JAXP, using: 3.2.3. Using JAXP
obtaining: 3.2. Bootstrapping an XMLReader
properties: 3.3.1. XMLReader Properties
pull mode event production and: 3.1. Pull Mode Event Production with XMLReader
XMLReaderAdapter class: A.2.9. The XMLReaderAdapter Class
XMLReaderFactory class: 3.2.1. The XMLReaderFactory Class
5.2. SAX1 Support
A.2.10. The XMLReaderFactory Class
XMLReaderFactory.createXMLReader( ): 2.2.1. How Do the Parts Fit Together?
XMLReader.getFeature( ) function: B.3. Document Information Item
XMLReader.parse( ) function: 3.1. Pull Mode Event Production with XMLReader
6.2.3. XML Messaging over HTTP with SAX2
6.2.3. XML Messaging over HTTP with SAX2
B.1. Event Producer Issues
B.3. Document Information Item
InputSource class and: 3.1.2. The InputSource Class
XMLReader.setDTDHandler( ) function, binding DTDHandler to parsers: 4.3.2. The DTDHandler Interface
XMLReader.setEntityResolver( ) method: 3.4. The EntityResolver Interface
xml:space attribute: 5.1.1. The AttributesImpl Class
XMLWriter: 2.2.3. XMLWriter: an Event Consumer
event pipelines and: 2.2.3.1. Event pipelines
4.5. XML Pipelines
XP Java parser: 1.4.1. SAX1
XPath: 6.3. Including Subdocuments
XPath data model, reading comments: 4.2. The LexicalHandler Interface
XPointer: 6.3. Including Subdocuments
XSD (XML Schema Datatypes): 2.6.4. ContentHandler and Prefix Mappings
XSLT: 4.5.3. The javax.xml.transform.sax Package
push mode with XSLT: 4.5.3.1. SAX in Push-Mode with XSLT

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


XML Schema

by Eric van der Vlist
ISBN 0-596-00252-1
First Edition, published June 2002.
(See the catalog page for this book.)

Table of Contents

Copyright Page
Preface
Chapter 1: Schema Uses and Development
Chapter 2: Our First Schema
Chapter 3: Giving Some Depth to Our First Schema
Chapter 4: Using Predefined Simple Datatypes
Chapter 5: Creating Simple Datatypes
Chapter 6: Using Regular Expressions to Specify Simple Datatypes
Chapter 7: Creating Complex Datatypes
Chapter 8: Creating Building Blocks
Chapter 9: Defining Uniqueness, Keys, and Key References
Chapter 10: Controlling Namespaces
Chapter 11: Referencing Schemas and Schema Datatypes in XML Documents
Chapter 12: Creating More Building Blocks Using Object-Oriented Features
Chapter 13: Creating Extensible Schemas
Chapter 14: Documenting Schemas
Chapter 15: Elements Reference Guide
Chapter 16: Datatype Reference Guide
Appendix A: XML Schema Languages
Appendix B: Work in Progress
Glossary
Index
Colophon
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Copyright (XML Schema)

 XML SchemaSearch this book

Copyright © 2002 O'Reilly & Associates, Inc. All rights reserved.

Printed in the United States of America.

Published by O'Reilly & Associates, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safari.oreilly.com). For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are registered trademarks of O'Reilly & Associates, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps. The association between the image of a Reeves's pheasant and XML Schema is a trademark of O'Reilly & Associates, Inc. Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc., in the United States and other countries. O'Reilly & Associates, Inc. is independent of Sun Microsystems.

While every precaution has been taken in the preparation of this book, the publisher and the author assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Previous | Home | Next
Table of Contents | Book Index | 0. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Preface (XML Schema)

 XML SchemaSearch this book

Preface

As developers create new XML vocabularies, they often need to describe those vocabularies to share, define, and apply them. This book will guide you through W3C XML Schema, a set of Recommendations from the World Wide Web Consortium (W3C). These specifications define a language that you can use to express formal descriptions of XML documents using a generally object-oriented approach. Schemas can be used for documentation, validation, or processing automation. W3C XML Schema is a key component of Web Services specifications such as SOAP and WSDL, and is widely used to describe XML vocabularies precisely.

With this power comes complexity. The Recommendations are long, complex, and generally difficult to read. The Primer helps, of course, but there are many details and style approaches to consider in building schemas. This book attempts to provide an objective, and sometimes critical, view of the tools W3C XML Schema provides, helping you to discover the possibilities of schemas while avoiding potential minefields.

0.1. Who Should Read This Book?

Read this book if you want to:

  • Create W3C XML Schema schemas using a text editor, XML editor, or a W3C XML Schema IDE or editor.

  • Understand and modify existing W3C XML Schema schemas.

You should already have a basic understanding of XML document structures and how to work with them.

Previous | Home | Next
Copyright Page | Book Index | 0.2. Who Should Not Read This Book?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Who Should Not Read This Book? (XML Schema)

 XML SchemaSearch this book

0.2. Who Should Not Read This Book?

If you are just using an XML application using a W3C XML Schema schema, you probably do not need to deal with the subtleties of the Recommendation.

Previous | Home | Next
0. Preface | Book Index | 0.3. About the Examples
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


About the Examples (XML Schema)

 XML SchemaSearch this book

0.3. About the Examples

All the examples in this book have been tested with the XSV and Xerces-J implementations of W3C XML Schema running Linux (the Debian "sid" distribution). I have chosen these tools for their high level of conformance to the Recommendation (the best ones according to the tests I have performed); the vast majority runs without error on these implementations--however, the Recommendation is sometimes fuzzy and difficult to understand, and there are some examples that give different results with different implementations. These conform to my own understanding of the Recommendation as discussed on the xmlschema-dev mailing list (the archives are available at http://lists.w3.org/Archives/Public/xmlschema-dev).

Previous | Home | Next
0.2. Who Should Not Read This Book? | Book Index | 0.4. Organization of This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Organization of This Book (XML Schema)

 XML SchemaSearch this book

0.4. Organization of This Book

Chapter 1, "Schema Uses and Development"
This chapter examines why we would want to bring a new XML Schema language onto the XML scene and what basic benefits W3C XML Schema offers.

Chapter 2, "Our First Schema"
This chapter presents a first complete schema, introducing the basic features of the language in a very "flat" style.

Chapter 3, "Giving Some Depth to Our First Schema"
With W3C XML Schema, style matters. This chapter gives a second example of a complete schema, describing the same class of documents, and written in a completely different style called "Russian doll design."

Chapter 4, "Using Predefined Simple Datatypes"
W3C XML Schema also provides datatyping. In this chapter, we explore how these types can be bound to the content of our document.

Chapter 5, "Creating Simple Datatypes"
This chapter guides you through the process of defining your own simple types.

Chapter 6, "Using Regular Expressions to Specify Simple Datatypes"
This chapter explores how to constrain new datatypes using regular expressions.

Chapter 7, "Creating Complex Datatypes"
Now that we know all about simple types, this chapter explores the different complex types that can be used to define structures within an XML document.

Chapter 8, "Creating Building Blocks"
This chapter shows how to organize schema tools into reusable building blocks.

Chapter 9, "Defining Uniqueness, Keys, and Key References"
In addition to content (simple types) and structure (complex types), W3C XML Schema can constrain the identifiers and references within a document. We explore this feature in this chapter.

Chapter 10, "Controlling Namespaces"
Support for XML namespaces is one of the top requirements of W3C XML Schema. This chapter explains how this requirement has been implemented and its implications.

Chapter 11, "Referencing Schemas and Schema Datatypes in XML Documents"
This chapter shows how schema information may be embedded in the XML instance documents.

Chapter 12, "Creating More Building Blocks Using Object-Oriented Features"
This chapter explains how more building blocks may be defined, by playing with namespaces and justifying the object-oriented qualification given to W3C XML Schema.

Chapter 13, "Creating Extensible Schemas"
This chapter gives some hints to write extensible and open schemas.

Chapter 14, "Documenting Schemas"
This chapter shows how schemas can be documented and made more readable, either by humans or programs.

Chapter 15, "Elements Reference Guide"
This is a quick reference guide to the elements used by W3C XML Schema.

Chapter 16, "Datatype Reference Guide"
This is a quick reference guide to the W3C XML Schema predefined types.

Appendix A, "XML Schema Languages"
W3C XML Schema is not the only language of its kind. Here we provide a short history of this not-so-new family and see some of its competitors.

Appendix B, "Work in Progress"
If you want to look ahead at what's to come from the W3C, you may be interested in this list of promising developments yet to be done in relation with W3C XML Schema.

Glossary
This provides short definitions for the main concepts and acronyms manipulated in the book.

Previous | Home | Next
0.3. About the Examples | Book Index | 0.5. Conventions Used in This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Conventions Used in This Book (XML Schema)

 XML SchemaSearch this book

0.5. Conventions Used in This Book

Constant Width
Used for attributes, datatypes, types, elements, code examples, and fragments.

Constant Width Bold
Used to highlight a section of code being discussed in the text.

Constant Width Italic
Used for replaceable elements in code examples.

TIP: This icon designates a tip or note, which is an important aside to the nearby text.

WARNING: This icon designates a warning relating to the nearby text.

Previous | Home | Next
0.4. Organization of This Book | Book Index | 0.6. How to Contact Us
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How to Contact Us (XML Schema)

 XML SchemaSearch this book

0.6. How to Contact Us

Please address comments and questions concerning this book to the publisher:

O'Reilly & Associates, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
(800) 998-9938 (in the United States or Canada)
(707) 829-0515 (international or local)
(707) 829-0104 (fax)

We have a web page for this book, where we list errata, examples, or any additional information. You can access this page at:

http://www.oreilly.com/catalog/xmlschema

To comment or ask technical questions about this book, send email to:

bookquestions@oreilly.com

For more information about our books, conferences, Resource Centers, and the O'Reilly Network, see our web site at:

http://www.oreilly.com

Previous | Home | Next
0.5. Conventions Used in This Book | Book Index | 0.7. Acknowledgments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Acknowledgments (XML Schema)

 XML SchemaSearch this book

0.7. Acknowledgments

I would like to thank the contributors of xmlhack for their encouragements, and more specifically Simon St.Laurent, whose role has been aggravated by the fact that he has also been my editor for this book and has shown a remarkable level of helpfulness and patience. I'd also like to thank Edd Dumbill, who helped me set up Debian on the laptop on which this book was written.

I have been lucky enough to work with Jeni Tennison as a technical reviewer. Jeni's deep and thorough knowledge has been invaluable to my confidence in the deciphering of the Recommendation. Her friendly, yet accurate, reviews were my safety net while I was writing this book.

I am also very grateful to all the people who have answered my many nasty questions on the xmlschema-dev mailing list, especially Henry S. Thompson, Noah Mendelsohn, Ashok Malhotra, Priscilla Walmsley, and Jeni Tennison (yes, Jeni is helping people on this list too!).

Finally, I would like to thank my wife and children for their patience during the whole year I have spent writing this book. Hopefully, now that this work is over, they can retrieve their husband and father!

Previous | Home | Next
0.6. How to Contact Us | Book Index | 1. Schema Uses and Development
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schema Uses and Development (XML Schema)

 XML SchemaSearch this book

Chapter 1. Schema Uses and Development

Contents:

What Schemas Do for XML
W3C XML Schema

XML, the Extensible Markup Language, lets developers create their own formats for storing and sharing information. Using that freedom, developers have created documents representing an incredible range of information, and XML can ease many different information-sharing problems. A key part of this process is formal declaration and documentation of those formats, providing a foundation on which software developers can build software.

1.1. What Schemas Do for XML

An XML schema language is a formalization of the constraints, expressed as rules or a model of structure, that apply to a class of XML documents. In many ways, schemas serve as design tools, establishing a framework on which implementations can be built. Since formalization is a necessary ground for software designers, formalizing the constraints and structures of XML instance documents can lead to very diverse applications. Although new applications for schemas are being invented every day, most of them can be classified as validation, documentation, query, binding, or editing.

1.1.1. Validation

Validation is the most common use for schemas in the XML world. There are many reasons and opportunities to validate an XML document: when we receive one, before importing data into a legacy system, when we have produced or hand-edited one, to test the output of an application, etc. In all these cases, a schema helps to accomplish a substantial part of the job. Different kinds of schemas perform different kinds of validation, and some especially complex rules may be better expressed in procedural code rather than in a descriptive schema, but validation is generally the initial purpose of a schema, and often the primary purpose as well.

Validation can be considered a "firewall" against the diversity of XML. We need such firewalls principally in two situations: to serve as actual firewalls when we receive documents from the external world (as is commonly the case with Web Services and other XML communications), and to provide check points when we design processes as pipelines of transformations. By validating documents against schemas, you can ensure that the documents' contents conform to your expected set of rules, simplifying the code needed to process them.

Validation of documents can substantially reduce the risk of processing XML documents received from sources beyond your control. It doesn't remove either the need to follow the administration rules of your chosen communication protocol or the need to write robust applications, but it's a useful additional layer of tests that fits between the communications interface and your internal code.

Validation can take place at several levels. Structural validation makes certain that XML element and attribute structures meet specified requirements, but doesn't clarify much about the textual content of those structures. Data validation looks more closely at the contents of those structures, ensuring that they conform to rules about what type of information should be present. Other kinds of validation, often called business rules, may check relationships between information and a higher level of sanity-checking, but this is usually the domain of procedural code, not schema-based validation.

XML is a good foundation for pipelines of transformations using widely available tools. Since each of these transformations introduces a risk of error, and each error is easier to fix when detected near its source, it is good practice to introduce check points in the pipeline where the documents are validated. Some applications will find that validating after each step is an overhead cost they can't bear, while others will find that it is crucial to detect the errors just as they happen, before they can cause any harm and when they are still easy to diagnose. Different situations may have different validation requirements, and it may make sense to validate more heavily during pipeline design than during production deployment.

1.1.2. Documentation

XML schemas are frequently used to document XML vocabularies, even when validation isn't a requirement. Schemas provide a formal description of the vocabulary with a precision and conciseness that can be difficult to achieve in prose. It is very unusual to publish the specification of a new XML vocabulary without attaching some form of XML schema.

The machine-readability of schemas gives them several advantages as documentation. Human-readable documentation can be generated from the schema's formal description. Schema IDEs, for instance, provide graphical views that help to understand the structure of the documents. Developers can also create XSLT transformations that generate a description of the structure. (This technique was used to generate the structure of Chapters 15 and 16 from the W3C XML Schema for W3C XML Schema published on the W3C web site.)

We will see, in Chapter 14, "Documenting Schemas", that W3C XML Schema has introduced additional facilities to annotate schemas with both structured or unstructured information, making it easier to use schemas explicitly as a documentation framework.

1.1.3. Querying Support

The first versions of XPath and XSLT were defined to work without any explicit understanding of the structure of the documents being manipulated. This has worked well, but has imposed performance and functionality limits. Knowledge of the document's structure could improve the efficiency of optimizers, and some functions, such as sorts and equality testing, may be improved by a datatype system. The second version of XPath and XSLT and the first version of XQuery (a new specification defining an XML query language that is still a work in progress) will rely on the availability of a W3C XML Schema for those features.

1.1.4. Data Binding

Although it isn't especially difficult to write applications that process XML documents using the SAX, DOM, and similar APIs, it is a low-level task, both repetitive and error-prone. The cost of building and maintaining these programs grows rapidly as the number of elements and attributes in a vocabulary grows. The idea of automating these through "binding" the information available in XML documents directly into the structures of applications (generally as objects or RDBMS tables) is probably as old as markup.

Ronald Bourret, who maintains of list of XML Data Binding Resources at http://www.rpbourret.com/xml/XMLDataBinding.htm, makes a distinction between design time and runtime binding tools. While runtime binding tools do their best to perform a binding based on the structure of the documents and applications discovered by introspection, design time binding tools rely on a model formalized in a schema of some kind. He describes this category as "usually more flexible in the mappings they can support."

Many different languages, either specific or general-purpose XML schema languages, define these bindings. W3C XML Schema has a lot of traction in this area; many data-binding tools were started to support W3C XML Schema for even its early releases, well before the specification was finalized.

1.1.5. Guided Editing

XML editors (and SGML editors before them) have long used schemas to present users with appropriate choices over the course of document creation and editing. While DTDs provided structural information, recent XML schema languages add more sophisticated structural information and datatype information.

The W3C is creating a standard API that can be used by guided editing applications to ask a schema processor which action can be performed at a certain location in a document--for instance: "Can I insert this new element here?", "Can I update this text node to this value?", etc. The Document Object Model (DOM) Level 3 Abstract Schemas and Load and Save Specification (which is still a work in progress) defines "Abstract Schemas" generic enough to cover both DTDs and W3C XML Schema (and potentially other schema languages as well). When finalized and widely adopted, this API should allow you to plug the schema processor of your choice into any editing application.

Another approach to editing applications builds editors from the information provided in schemas. Combined with information about presentation and controls, these tools let users edit XML documents in applications custom-built for a particular schema. For example, the W3C XForms specification (which is still a work in progress) proposes to separate the logic and layout of the form from the structure of the data to edit, and relies on a W3C XML Schema to define this structure.

Previous | Home | Next
0.7. Acknowledgments | Book Index | 1.2. W3C XML Schema
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


W3C XML Schema (XML Schema)

 XML SchemaSearch this book

1.2. W3C XML Schema

XML 1.0 included a set of tools for defining XML document structures, called Document Type Definitions (DTDs). DTDs provide a set of tools for defining which element and attribute structures are permitted in a document, as well as mechanisms for providing default values for attributes, defining reusable content (entities), and some kinds of metadata information (notations). While DTDs are widely supported and used, many XML developers quickly outgrew the capabilities DTDs provide. An alternative schema proposal, XML-Data, was even submitted to the W3C before XML 1.0 was a Recommendation.

The World Wide Web Consortium (W3C), keeper of the XML specification, sought to build a new language for describing XML documents. It needed to provide more precision in describing document structures and their contents, to support XML namespaces, and to use an XML vocabulary to describe XML vocabularies. The W3C's XML Schema Working Group spent two years developing two normative Recommendations, XML Schema Part 1: Structures, and XML Schema Part 2: Datatypes, along with a nonnormative Recommendation, XML Schema Part 0: Primer.

W3C XML Schema is designed to support all of these applications. An initial set of requirements, formally described in the XML Schema Requirements Note (http://www.w3.org/TR/NOTE-xml-schema-req), listed a wide variety of usage scenarios for schemas as well as for the design principles that guided its creation.

In the rest of this book, we explore the details of W3C XML Schema and its many capabilities, focusing on how to apply it to specific XML document situations.

Previous | Home | Next
1. Schema Uses and Development | Book Index | 2. Our First Schema
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Our First Schema (XML Schema)

 XML SchemaSearch this book

Chapter 2. Our First Schema

Contents:

The Instance Document
Our First Schema
First Findings

Starting with a simple example (a limited number of elements and attributes and containing no namespaces), we will see how a first schema can be simply derived from the document structure, using a catalog of the elements in a document as we write a DTD for this document.

2.1. The Instance Document

The instance document, which we use in the first part of this book, is a simple library file describing a book, its author, and its characters:

<?xml version="1.0"?>
was used to <library>
  <book id="b0836217462" available="true">
    <isbn>
      0836217462
    </isbn>
    <title lang="en">
      Being a Dog Is a Full-Time Job
    </title>
    <author id="CMS">
      <name>
        Charles M Schulz
      </name>
      <born>
        1922-11-26
      </born>
      <dead>
        2000-02-12
      </dead>
    </author>
    <character id="PP">
      <name>
        Peppermint Patty
      </name>
      <born>
        1966-08-22
      </born>
      <qualification>
        bold, brash and tomboyish
      </qualification>
    </character>
    <character id="Snoopy">
      <name>
        Snoopy
      </name>
      <born>
        1950-10-04
      </born>
      <qualification>
        extroverted beagle
      </qualification>
    </character>
    <character id="Schroeder">
      <name>
        Schroeder
      </name>
      <born>
        1951-05-30
      </born>
      <qualification>
        brought classical music to the Peanuts strip
      </qualification>
    </character>
    <character id="Lucy">
      <name>
        Lucy
      </name>
      <born>
        1952-03-03
      </born>
      <qualification>
        bossy, crabby and selfish
      </qualification>
    </character>
  </book>
</library>
Previous | Home | Next
1.2. W3C XML Schema | Book Index | 2.2. Our First Schema
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Our First Schema (XML Schema)

 XML SchemaSearch this book

2.2. Our First Schema

We will see, in the course of this book, that there are many different styles for writing a schema, and there are even more approaches to deriving a schema from an instance document. For our first schema, we will adopt a style that is familiar to those of you who have already worked with DTDs. We'll start by creating a classified list of the elements and attributes found in the schema.

The elements existing in our instance document are author, book, born, character, dead, isbn, library, name, qualification, and title, and the attributes are available, id, and lang.

We will build our first schema by defining each element in turn under our schema document element (named, unsurprisingly, schema), which belongs to the W3C XML Schema namespace (http://www.w3.org/2001/XMLSchema) and is usually prefixed as "xs."

Before we start, we need to classify the elements and, for this exercise, give some key definitions for understanding how W3C XML Schema does this classification. (We will see these definitions in more detail in the chapters about simple and complex types.)

The content model characterizes the types of children elements and text nodes that can be included in an element (without paying any attention to the attributes).

The content model is said to be "empty" when no children elements nor text nodes are expected, "simple" when only text nodes are accepted, "complex" when only subelements are expected, and "mixed" when both text nodes and sub-elements can be present. Note that to determine the content model, we pay attention only to the element and text nodes and ignore any attribute, comment, or processing instruction that could be included. For instance, an element with some attributes, a comment, and a couple of processing instructions would have an "empty" content model if it has no text or element children.

Elements such as name, born, and title have simple content models:

.../...

  <title lang="en">
    Being a Dog Is a Full-Time Job
  </title>
.../...

  <name>
    Charles M Schulz
  </name>

  <born>
    1922-11-26
  </born>
.../...

Elements such as library or character have complex content models:

<library>
  <book id="b0836217462" available="true">
    .../...
  </book>
</library>

<character id="Lucy">
  <name>
    Lucy
  </name>
  <born>
    1952-03-03
  </born>
  <qualification>
    bossy, crabby and selfish
  </qualification>
</character>

Within elements that have a simple content model, we can distinguish those which have attributes and those which cannot have any attributes. Later chapters discuss how W3C XML Schema can also represent empty and mixed content models.

W3C XML Schema considers the elements that have a simple content model and no attributes "simple types," while all the other elements (such as simple content with attributes and other content models) are "complex types." In other words, when an element can only have text nodes and doesn't accept any child elements or attributes, it is considered a simple type; in all the other cases, it is a complex type.

Attributes always have a simple type since they have no children and contain only a text value.

In our example, elements such as author or title have a complex type:

  <author id="CMS">
    <name>
      Charles M Schulz
    </name>
    <born>
      1922-11-26
    </born>
    <dead>
      2000-02-12
    </dead>
  </author>
.../...

  <title lang="en">
    Being a Dog Is a Full-Time Job
  </title>

While elements such as born or qualification (and, of course, all the attributes) have a simple type:

  <born>
    1922-11-26
  </born>
.../...

  <qualification>
    brought classical music to the Peanuts strip
  </qualification>
.../...

  <book available="true"/>

Now that we have criteria to classify our components, we can define each of them. Let's start with the simplest one by taking a type element, such as the name element that can be found in author or character:

<name>
  Charles M Schulz
</name>

To define such an element, we use an xs:element(global definition), included directly under the xs:schema document element:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="name" type="xs:string"/>
  .../...
</xs:schema>

The value used to reference the datatype (xs:string) is prefixed by xs, the prefix associated with W3C XML Schema. This means that xs:string is a predefined W3C XML Schema datatype.

The same can be done for all the other simple types as well as for the attributes:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="name" type="xs:string"/>
  <xs:element name="qualification" type="xs:string"/>
  <xs:element name="born" type="xs:date"/>
  <xs:element name="dead" type="xs:date"/>
  <xs:element name="isbn" type="xs:string"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
  .../...
</xs:schema>

While we said that this design style would be familiar to DTD users, we must note that it is flatter than a DTD since the declaration of the attributes is done outside of the declaration of the elements. This results in a schema in which elements and attributes get fairly equal treatment. We will see, though, that when a schema describes an XML vocabulary that uses a namespace, this simple flat style is impossible to use most of time.

NOTE: The assimilation of simple type elements and attributes is a simplification compared to the XPath, DOM, and Infoset data models. These consider a simple type element to be an item having a single child item of type "character," and an attribute to be an item having a normalized value. The benefit of this simplification is we can use simple datatypes to define simple type elements and attributes indifferently and write in a consistent fashion:

  <xs:element name="isbn" type="xs:string"/>
                or
  <xs:attribute name="isbn" type="xs:string"/>

The order of the definitions in a schema isn't significant; we can now take the next step in terms of type complexity and define the title element that appears in the instance document as:

<title lang="en">
  Being a Dog Is a Full-Time Job
</title>

Since this element has an attribute, it has a complex type. Since it has only a text node, it is considered to have a simple content. We will, therefore, write its definition as:

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute ref="lang"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

The XML syntax makes it verbose, but this can almost be read as plain English as "the element named title has a complex type which is a simple content obtained by extending the predefined datatype xs:string by adding the attribute defined in this schema and having the name lang."

The remaining elements (library, book, author, and character) are all complex types with complex content. They are defined by defining the sequence of elements and attributes that will compose them.

The library element, the most straightforward of them, is defined as:

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="book" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

This definition can be read as "the element named library is a complex type composed of a sequence of 1 to many occurrences (note the maxOccurs attribute) of elements defined as having a name book."

The element author, which has an attribute and for which we may consider the date of death as optional, could be:

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

This means the element named author is a complex type composed of a sequence of three elements (name, born, and dead), and id. The dead element is optional- it may occur zero times.

The minOccurs and maxOccurs attributes, which we have seen in a couple of previous elements, allow us to define the minimum and maximum number of occurrences. Their default value is 1, which means that when they are both missing, the element must appear exactly one time in the sequence. The special value "unbounded" may be used for maxOccurs when the maximum number of occurrences is unlimited.

The attributes need to be defined after the sequence. The remaining elements (book and character) can be defined in the same way, which leads us to the following full schema:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="name" type="xs:string"/>
  <xs:element name="qualification" type="xs:string"/>
  <xs:element name="born" type="xs:date"/>
  <xs:element name="dead" type="xs:date"/>
  <xs:element name="isbn" type="xs:string"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:string">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
      <xs:attribute ref="available"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="qualification"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

Previous | Home | Next
2. Our First Schema | Book Index | 2.3. First Findings
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


First Findings (XML Schema)

 XML SchemaSearch this book

2.3. First Findings

Even in this very simple schema, we have learned a lot about what W3C XML Schema has to offer.

2.3.1. W3C XML Schema Is Modular

In this example, we defined simple components (elements and attributes in this case, but we will see in the next chapters how to define other kinds of components) that we used to build more complex components. This is one of the key principles that have guided the editors of W3C XML Schema. These editors have borrowed many concepts of object-oriented design to develop complex components.

If we draw a parallel between datatypes and classes, the elements and attributes can be compared to objects. Each of the component definitions that we included in our first schema is similar to an object. Referencing one of these components to build a new element is similar to creating a new object by cloning the already defined component.

In the next chapters, we will see how we can also create the components "in place" (where they are needed) as well as create datatypes from which we can derive elements and attributes the same way we can instantiate a class to create an object.

2.3.2. W3C XML Schema Is Both About Structure and Datatyping

Note also that W3C XML Schema is pursuing two different levels of validation in this first example: we have defined both rules about the structure of the document and rules above the content of leaf nodes of the document. The W3C Recommendation makes a clear distinction between these two levels by publishing the recommendation in two parts (Part 1: Structures and Part 2: Datatypes), which are relatively independent.

There is also a big difference between simple types, which are about datatyping and constraining the content of leaf nodes in the tree structure of an XML document, and complex types, which are about defining the structure of a document.

2.3.3. Flat Design, Global Components

Finally, note the flatness of this schema: each component (element or attribute) is defined directly under the xs:schema document element.

Components defined directly under the xs:schema document element are called "global" components. These have a couple of notable properties: they can be referenced anywhere in the schema as well as in the other schema that may include or import this schema (we will see in the next chapters how to import or include schemas), and all the global elements can be used as document root elements.

Previous | Home | Next
2.2. Our First Schema | Book Index | 3. Giving Some Depth to Our First Schema
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Giving Some Depth to Our First Schema (XML Schema)

 XML SchemaSearch this book

Chapter 3. Giving Some Depth to Our First Schema

Contents:

Working From the Structure of the Instance Document
New Lessons

Our first schema was very flat, and all its components were defined at the top level. Our second attempt will give it more depth and show how local components may be defined.

3.1. Working From the Structure of the Instance Document

For this second schema, we follow a style opposite from the one we used in Chapter 2, "Our First Schema", and we define all the elements and attributes locally where they appear in the document.

Following the document structure, we will start by defining our document element library. This element was defined in the earlier schema as:

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="book" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

In our new schema, we will keep the same construct and the same structure, but we will replace the reference to the book element with the actual definition of this element:

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="book" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="isbn"/>
            <xs:element ref="title"/>
            <xs:element ref="author" minOccurs="0"
              maxOccurs="unbounded"/>
            <xs:element ref="character" minOccurs="0"
              maxOccurs="unbounded"/>
          </xs:sequence>
          <xs:attribute ref="id"/>
          <xs:attribute ref="available"/>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Because the definition of the book element is contained inside the definition of the library element, other definitions of book elements could be done at other locations in the schema without any risk of confusion--except maybe by human readers.

If all the elements and attributes still referenced in this schema are defined as global, this piece of schema is valid and accurately describes our schema. The only differences between the first schema and this intermediary step are that the definition of the book element cannot be reused elsewhere, and the book element can no longer be a document element any longer.

We can also reiterate the same operation and perform the definitions of all the elements and all the attributes locally:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="isbn" type="xs:integer"/>
              <xs:element name="title">
                <xs:complexType>
                 <xs:simpleContent>
                 <xs:extension base="xs:string">
                 <xs:attribute name="lang" type="xs:language"/>
                 </xs:extension>
                 </xs:simpleContent>
                </xs:complexType>
              </xs:element>
              <xs:element name="author" minOccurs="0"
                maxOccurs="unbounded">
                <xs:complexType>
                 <xs:sequence>
                 <xs:element name="name" type="xs:string"/>
                 <xs:element name="born" type="xs:date"/>
                 <xs:element name="dead" type="xs:date"/>
                 </xs:sequence>
                 <xs:attribute name="id" type="xs:ID"/>
                </xs:complexType>
              </xs:element>
              <xs:element name="character" minOccurs="0"
                maxOccurs="unbounded">
                <xs:complexType>
                 <xs:sequence>
                 <xs:element name="name" type="xs:string"/>
                 <xs:element name="born" type="xs:date"/>
                 <xs:element name="qualification" type="xs:string"/>
                 </xs:sequence>
                 <xs:attribute name="id" type="xs:ID"/>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID"/>
            <xs:attribute name="available" type="xs:boolean"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Apart from an obvious difference in style, this new schema is validating the same instance document as in Chapter 2, "Our First Schema". It is not, strictly speaking, equivalent to the first one: it is less reusable (the document element is the only one that could be reused in another schema) and more strict, since it validates only the documents that have a library document element. Chapter 2, "Our First Schema"'s schema must validate documents having any of the elements as a document element.

TIP: The price we pay to constrain the value of the document root element with W3C XML Schema is a loss of reusability. This has been widely criticized without affecting the decision of its editors. We will see, fortunately, that there are some workarounds to limit this loss for applications that need to constrain the value of the document element.

Previous | Home | Next
2.3. First Findings | Book Index | 3.2. New Lessons
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


New Lessons (XML Schema)

 XML SchemaSearch this book

3.2. New Lessons

Although this schema describes the same document as the one in Chapter 2, "Our First Schema", it illustrates very different aspects of W3C XML Schema.

3.2.1. Depth Versus Modularity?

Even though we will present features to balance this fact in the next chapters--xs:complexType and xs:group--we have sacrificed the modularity of our first schema to gain the depth and structure of the second one. This is a general tendency in W3C XML Schema.

In practice, you will probably want to keep a balance between these two opposite styles and allow a certain level of depth under several global elements.

There are two cases, however, in which these two styles are not equivalent. The first is when elements with the same name need to be defined with different contents at different locations. In this case, local element definitions should be used (at least at all the location except one) since the elements are identified by their names.

In our example, the element name appears both within author and character with the same datatype. We may want to define the element name with different content models in author and character, as in this instance document:

<?xml version="1.0"?>
<library>
  <book id="b0836217462" available="true">
    <isbn>
      0836217462
    </isbn>
    <title lang="en">
      Being a Dog Is a Full-Time Job
    </title>
    <author id="CMS">
      <name>
        <first>
          Charles
        </first>
        <middle>
          M.
        </middle>
        <last>
          Schulz
        </last>
      </name>
      <born>
        1922-11-26
      </born>
      <dead>
        2000-02-12
      </dead>
    </author>
    <character id="Snoopy">
      <name>
        Snoopy
      </name>
      <born>
        1950-10-04
      </born>
      <qualification>
        extroverted beagle
      </qualification>
    </character>
  </book>
</library>

Since we can define only one global element named name, we need to define at least one of the name elements locally under its parent.

The W3C Schema for XML Schema gives several examples of elements having different types depending on their location. We will see this used in the next section in our Russian doll schema: global definitions of elements have a different type in the schema for schema than local definitions or references, even though they use the same element name (xs:element).

TIP: Whether defining elements with the same name and different datatypes is good practice or not is subject to discussion. It may be confusing for human authors and more difficult to document, but W3C XML Schema gives, through local definitions, a way to avoid any confusion for the applications that will process these documents. In our example, for instance, we have two occurrences of a name element under author and under character. It is perfectly possible to define different constraints and even contents on those two elements. Although this could be presented as overloaded element names ("character/name" versus "author/name"), I find this practice unreliable, since we often don't have a clear and simple way to identify those two contexts.

Another example is recursive schema, in which an element can be included within an element of the same type directly or indirectly in a child element. In this case, a flat design employing references must be used since the depth of these recursive structures is unlimited.

W3C XML Schema offers several examples of such elements with local definitions of elements that can be recursively nested, as is the case in our second schema. A flat design must be used since these elements need to be referenced if we don't want to limit the maximum depth of the structure, and the schema for schema uses a reference mechanism. (The actual mechanism used in this case involves an element group, a feature we have not seen yet but is equivalent to an actual reference to an element.)

3.2.2. Russian Doll and Object-Oriented Design

The style of defining elements and attributes locally is often called the Russian doll design, since the definition of each element is embedded in the definition of its parent, in the same way Russian dolls are embedded into each other.

If we look at the Russian dolls with our object-oriented lenses, we may say that the objects are now created locally where they are needed as opposed to being created globally and cloned when we need them (which was the case as in our first schema).

At this point, we still need to learn how we can create types that are the equivalent of classes of objects and containers, and that will let us manipulate sets of objects.

3.2.3. Where Have the Element Types Gone?

Those of you who are familiar with XML (or SGML) and its DTD are used to identifying the elements though the term "element type." The XML 1.0 Recommendation states that "each element has a type, identified by name." This is further disambiguated by the namespaces specification, which explain that "an XML namespace is a collection of names, identified by a URI reference [RFC2396], which are used in XML documents as element types and attribute names."

A surprising feature of our Russian doll schema is that this fundamental notion of element type has completely disappeared, and there is no way to tell which element type name is. Two different elements have been defined as having a name equal to name. These have an independent definition, which is identical in our example, but could be different--such as if we had decomposed the first, middle, and last names for authors, but not for characters. The notion of element type name doesn't mean anything if we do not specify in which context it is used.

This loss has such little importance that few people have even noticed it. There are some situations where we need to identify elements, though--for instance to document XML vocabularies. A convenient way to write a reference manual for a XML vocabulary is to write an index of the element names with their definition. This becomes much more complex when there is no clear match between element types and their definitions and content models.

TIP: RDF is another application that relies on element types. RDF uses element types to identify elements as objects in its triples. The element "name" of the namespace http://dyomedea.com/ns is identified as http://dyomedea.com/ns#name. Cutting the link between element types and their schema definition makes it difficult, if not impossible, to answer basic questions, such as what's the content model of http://dyomedea.com/ns#name, and where can I find its definition.

I was confronted with this issue when writing the reference guide of this book since the W3C XML Schema for W3C XML Schema uses many local element definitions. I came to the conclusion that the fact that the same element type (such as xs:restriction, which we will see later on) can have different content models with a different semantic, depending on its location in a schema, adds a significant amount of difficulty in understanding the language and reading a schema.

Previous | Home | Next
3. Giving Some Depth to Our First Schema | Book Index | 4. Using Predefined Simple Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Using Predefined Simple Datatypes (XML Schema)

 XML SchemaSearch this book

Chapter 4. Using Predefined Simple Datatypes

Contents:

Lexical and Value Spaces
Whitespace Processing
String Datatypes
Numeric Datatypes
Date and Time Datatypes
List Types
What About anySimpleType?
Back to Our Library

W3C XML Schema provides an extensive set of predefined datatypes. W3C XML Schema derives many of these predefined datatypes from a smaller set of "primitive" datatypes that have a specific meaning and semantic and cannot be derived from other types. We will see how we can use these types to define our own datatypes by derivation to meet more specific needs in the next chapter.

Figure 4-1 provides a map of predefined datatypes and the relationships between them.

Figure 4-1. W3C XML Schema type hierarchy

4.1. Lexical and Value Spaces

W3C XML Schema introduced a decoupling between the data, as it can be read from the instance documents (the "lexical space"), and the value, as interpreted according to the datatype (the "value space").

Before we can enter into the definition of these two spaces, we must examine the processing model and the transformations endured by a value written in a XML document before it is validated. Element and attribute content proceeds through the following steps during processing:

Serialization space
The series of bytes that is actually stored in a document (either as the value of an attribute or as a text node) may be seen as belonging to a first space, which we may call the "serialization space."

Parsed space
The XML 1.0 Recommendation makes it clear that the serialization space is not directly meaningful to applications, and a first transformation is performed on the value by conforming XML parsers before the value reaches an application: characters are converted into Unicode, and ends of lines (for text nodes and attributes) and whitespaces (only for attributes) are normalized. The result of this transformation is what reaches the applications--including schema processors--and belongs to what we may call the "parsed space."

Lexical space
Before doing any validation, W3C XML Schema performs a second round of whitespace processing on this value reported by the XML parser. This depends on the value's datatype and may either ignore, normalize, or collapse the whitespaces. The value after this whitespace processing belongs to the "lexical space" defined in the W3C XML Schema Recommendation.

Value space
W3C XML Schema considers an item from the lexical space to be a representation of an abstract value whose meaning or semantic is defined by its datatype and can be a piece of text, and also a number, a date, or qualified name. The ensemble of abstract values is defined as the "value space."

Each datatype has its own lexical and value spaces and its own rules to associate a lexical representation with a value; for many datatypes, a single value can have multiple lexical representations (for instance, the <xs:float> value "3.14116" can also be written equivalently as "03.14116," "3.141160," or ".314116E1"). This distinction is important since the basic operations performed on the values (such as equality testing or sorting) are done on the value space. "3.14116" is considered to be equal to "03.14116" when the type is xs:float and is different when the type is xs:string. The same applies to sort orders: some datatypes have a full order relation (every pair of values can be compared), other have no order relation at all, and the remaining types have a partial order relation (values cannot always be compared).

TIP: Although future versions of APIs might send these values to the applications, the transformations between parsed, lexical, and value spaces are currently done for the sake of the validation only, and don't impact the values sent by a validating parser.

Previous | Home | Next
3.2. New Lessons | Book Index | 4.2. Whitespace Processing
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Whitespace Processing (XML Schema)

 XML SchemaSearch this book

4.2. Whitespace Processing

The handling of special characters (tab, linefeeds, carriage returns and spaces, which are often used only to "pretty print" XML documents) has always been very controversial. W3C XML Schema has imposed a two-step generic algorithm, which is applied to most of the predefined datatypes (actually, on all of them except two, xs:string and xs:normalizedString).

Whitespace replacement
This is the first step of whitespace processing applied to the parsed value. During whitespace replacement, all occurrences of any whitespace--#x9 (tab), #xA (linefeed), and #xD (carriage return)--are replaced with a space (#x20). The number of characters is not changed by this step, which is applied to all the predefined datatypes (except for xs:string, since no whitespace replacement is performed on the parsed value for this).

Whitespace collapse
The second step removes the leading and trailing spaces, and replaces all contiguous occurrences of spaces by a single space character. This is applied on all the predefined datatypes (except for xs:string, since no whitespace replacement is performed on the parsed value for this, and for xs:normalizedString, in which whitespaces are only normalized).

TIP: This notion of "normalized string" does not match the XPath function normalize-space( ), which corresponds with what W3C XML Schema calls whitespace collapsing. It is also different from the DOM normalize() method, which is a merge of adjacent text objects.

Previous | Home | Next
4. Using Predefined Simple Datatypes | Book Index | 4.3. String Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


String Datatypes (XML Schema)

 XML SchemaSearch this book

4.3. String Datatypes

This section discusses datatypes derived from the xs:string primitive datatype as well as other datatypes that have a similar behavior (namely, xs:hexBinary, xs:base64Binary, xs:anyURI, xs:QName, and xs:NOTATION). These types are not expected to carry any quantifiable value (W3C XML Schema doesn't even expect to be able to sort them) and their value space is identical to their lexical space except when explicitly described otherwise. One should note that even though they are grouped in this section because they have a similar behavior, these primitive datatypes are considered quite different by the Recommendation.

The datatypes covered in this section are shown in Figure 4-2.

Figure 4-2. Strings and similar datatypes

The two exceptions in whitespace processing (xs:string and xs:normalizedString) are string datatypes. One of the main differences between these types is the applied whitespace processing. To stress this difference, we will classify these types by their whitespace processing.

4.3.1. No Whitespace Replacement

xs:string
This string datatype is the only predefined datatype for which no whitespace replacement is performed. As we will see in the next chapter, the whitespace replacement performed on user-defined datatypes derived from this type can be defined without restriction. On the other hand, a user datatype cannot be defined as having no whitespace replacement if it is derived from any predefined datatype other than xs:string.

As expected, a string is a set of characters matching the definition given by XML 1.0, namely, "legal characters are tab, carriage return, line feed, and the legal characters of Unicode and ISO/IEC 10646."

The value of the following element:

<title lang="en">
   Being a Dog Is
   a Full-Time Job
</title>

is the full string:

Being a Dog Is
a Full-Time Job

with all its tabs, and CR/LF if the title element is a type xs:string.

4.3.2. Normalized Strings

xs:normalizedString
The normalized string is the only predefined datatype in which whitespace replacement is performed without collapsing.

The lexical space of xs:normalizedString is the same as the lexical space of xs:string from which it is derived--except that since any occurrence of #x9 (tab), #xA (linefeed), and #xD (carriage return) are replaced by a #x20 (space), these three characters cannot be found in its lexical and value spaces.

The value of the same element:

<title lang="en">
   Being a Dog Is
   a Full-Time Job
</title>

is now the string:

    Being a Dog Is     a Full-Time Job

in which all the whitespaces have been replaced by spaces if the title element is a type xs:normalizedString.

TIP: There is no additional constraint on normalized strings. Any value that is a valid xs:string is also a valid xs:normalizedString. The difference is the whitespace processing that is applied when the lexical value is calculated.

4.3.3. Collapsed Strings

Whitespace collapsing is performed after whitespace replacement by trimming the leading and trailing spaces and replacing all the contiguous occurrences of spaces with a single space. All the predefined datatypes (except, as we have seen, xs:string and xs:normalizedString) are whitespace collapsed.

We will classify tokens, binary formats, URIs, qualified names, notations, and all their derived types under this category. Although these datatypes share a number of properties, we must stress again that this categorization is done for the purpose of explanation and does not directly appear in the Recommendation.

4.3.3.1. Tokenss

xs:token
xs:token is xs:normalizedString on which the whitespaces have been collapsed. Since whitespaces are accepted in the lexical space of xs:token, this type is better described as a " tokenized" string than as a "token"!

The same element:

<title lang="en">
   Being a Dog Is
   a Full-Time Job
</title>

is still a valid xs:token, and its value is now the string:

Being a Dog Is a Full-Time Job

in which all the whitespaces have been replaced by spaces, any trailing spaces are removed, and contiguous sequences of spaces are replaced by single spaces.

TIP: As is the case with xs:normalizedString, there is no constraint on xs:token, and any value that is a valid xs:string is also a valid xs:token. The difference is the whitespace processing that is applied when the lexical value is calculated. This is not true of derived datatypes that have additional constraints on their lexical and value space. The restriction on the lexical spaces of xs:normalizedString is, therefore, a restriction by projection of their parsed space (different values of their parsed space are transformed into a single value of their lexical space), and not a restriction by invalidating values of their lexical space, as is the case for all the other predefined datatypes.

The predefined datatypes derived from xs:token are xs:language, xs:NMTOKEN, and xs:Name.

xs:language
This was created to accept all the language codes standardized by RFC 1766. Some valid values for this datatype are en, en-US, fr, or fr-FR.

xs:NMTOKEN
This corresponds to the XML 1.0 "Nmtoken" (Name token) production, which is a single token (a set of characters without spaces) composed of characters allowed in XML name. Some valid values for this datatype are "Snoopy", "CMS", "1950-10-04", or "0836217462". Invalid values include "brought classical music to the Peanuts strip" (spaces are forbidden) or "bold,brash" (commas are forbidden).

xs:Name
This is similar to xs:NMTOKEN with the additional restriction that the values must start with a letter or the characters ":" or "-". This datatype conforms to the XML 1.0 definition of a "Name." Some valid values for this datatype are Snoopy, CMS, or -1950-10-04-10:00. Invalid values include 0836217462 (xs:Name cannot start with a number) or bold,brash (commas are forbidden). This datatype should not be used for names that may be "qualified" by a namespace prefix, since we will see another datatype (xs:QName) that has a specific semantic for these values.The datatype xs:NCName is derived from xs:Name.

xs:NCName
This is the "noncolonized name" defined by Namespaces in XML1.0, i.e., a xs:Name without any colons (":"). As such, this datatype is probably the predefined datatype that is closest to the notion of a "name" in most of the programming languages, even though some characters such as "-" or "." may still be a problem in many cases. Some valid values for this datatype are Snoopy, CMS, -1950-10-04-10-00, or 1950-10-04. Invalid values include -1950-10-04:10-00 or bold:brash (colons are forbidden). xs:ID, xs:IDREF, and xs:ENTITY are derived from xs:NCName.

xs:ID
This is derived from xs:NCName. There is one constraint added to its value space is that there must not be any duplicate values in a document. In other words, the values of attributes or simple type elements having this datatype can be used as unique identifiers, and this datatype emulates the XML 1.0 ID attribute type. We will see this feature in more detail in Chapter 9, "Defining Uniqueness, Keys, and Key References".

xs:IDREF
This is derived from xs:NCName. The constraint added to its value space is it must match an ID defined in the same document. I will explain this feature in more detail in Chapter 9, "Defining Uniqueness, Keys, and Key References".

xs:ENTITY
Also provided for compatibility with XML 1.0 DTDs, this is derived from xs:NCName and must match an unparsed entity defined in a DTD.

TIP: XML 1.0 gives the following definition of unparsed entities: "an unparsed entity is a resource whose contents may or may not be text, and if text, may be other than XML. Each unparsed entity has an associated notation, identified by name. Beyond a requirement that an XML processor make the identifiers for the entity and notation available to the application, XML places no constraints on the contents of unparsed entities." In practice, this mechanism has seldom been used, as the general usage is to define links to the resources that could be defined as unparsed entities.

4.3.3.2. Qualified names

xs:QName
Following Namespaces in XML 1.0, xs:QName supports the use of namespace-prefixed names. A namespace prefix xs:QName treats a shortcut to identify a URI. Each xs:QName effectively contains a set of tuples {namespace name, local part}, in which the namespace name is the URI associated to the prefix through a namespace declaration. Even though the lexical space of xs:QName is very close to the lexical space of xs:Name (the only constraint on the lexical space is that there is a maximum of one colon allowed in an xs:QName, which cannot be the first character), the value spaces of these datatypes are completely different (a scalar for xs:Name and a tuple for xs:QName) and xs:QName is defined as a primitive datatype. The constraint added by this datatype over an xs:Name is the prefix must be defined as a namespace prefix in the scope of the element in which this datatype is used.

W3C XML Schema itself has already given us some examples of QNames. When we write <xs:attribute name="lang" type="xs:language"/>, the type attribute is an xs:QName and its value is the tuple:

{"http://www.w3.org/2001/XMLSchema", "language"}

because the URI:

"http://www.w3.org/2001/XMLSchema"

was assigned to the prefix "xs:". If there is no namespace declaration for this prefix, the type attribute is considered invalid.

The prefix of an xs:QName is optional. We are also able to write:

<xs:element ref="book" maxOccurs="unbounded"/>

in which the ref attribute is also a xs:QName and its value the tuple:

{NULL, "book"}

because we haven't defined any default namespace. xs:QName does support default namespaces; if a default namespace is defined in the scope of this element, the value of its URI is used for this tuple.

4.3.3.3. URIs

xs:anyURI
This is another string datatype in which lexical and value spaces are different. This datatype tries to compensate for the differences of format between XML and URIs as specified in the RFCs 2396 and 2732. These RFCs are not very friendly toward non-ASCII characters and require many character escapings that are not necessary in XML. The W3C XML Schema Recommendation doesn't describe the transformation to perform, noting only that it is similar to what is described for XLink link locators.

As an example of this transformation, the href attribute of an XHTML link written as:

<a href="http://dmoz.org/World/Français/">
  Word/Français
</a>

would be converted to the value:

http://dmoz.org/World/Fran%e7ais/

in the value space.

The xs:anyURI datatype doesn't pay any attention to xml:base attributes that may have been defined in the document.

4.3.3.4. Notations

xs:NOTATION
This is probably the most obscure of these string datatypes. This datatype was created to implement the XML 1.0 notations. It cannot be used directly in a schema; it must be used through user-defined derived datatypes. We will see more of it in the next chapter.

4.3.3.5. Binary string-encoded datatypes

XML 1.0 is unable to hold binary content, which must be string-encoded before it can be included in a XML document. W3C XML Schema has defined two primary datatypes to support two encodings that are commonly used (BinHex and base64). These encodings may be used to include any binary content, including text formats whose content may be incompatible with the XML markup. Other binary text encodings may also be used (such as uuXXcode, Quote Printable, BinHex, aencode, or base85, to name a few), but their value would not be recognized by W3C XML Schema.

xs:hexBinary
This defines a simple way to code binary content as a character string by translating the value of each binary octet into two hexadecimal digits. This encoding is different from the encoding method called BinHex (introduced by Apple, described by RFC 1741, and includes a mechanism to compress repetitive characters).

A UTF-8 XML header such as:

<?xml version="1.0" encoding="UTF-8"?>

that is encoded as xs:hexBinary would be:

3f3c6d78206c657673726f693d6e3122302e20226e656f636964676e223d54552d4622383e3f
xs:base64Binary
This matches the encoding known as "base64" and is described in RFC 2045. It maps groups of 6 bits into an array of 64 printable characters.

The same header encoded as xs:base64Binary would be:

PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCg==

The W3C XML Schema Recommendation missed the fact that RFC 2045 requests a line break every 76 characters. This should be clarified in an errata. The consequence of these line breaks being thought of as optional by W3C XML Schema, is that the lexical and value spaces of xs:base64Binary cannot be considered identical.

Previous | Home | Next
4.2. Whitespace Processing | Book Index | 4.4. Numeric Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Numeric Datatypes (XML Schema)

 XML SchemaSearch this book

4.4. Numeric Datatypes

The numeric datatypes are built on top of four primitive datatypes: xs:decimal for all the decimal types (including the integer datatypes, considered decimals without a fractional part), xs:double and xs:float for single and double precision floats, and xs:boolean for Booleans. Whitespaces are collapsed for all these datatypes.

The datatypes covered in this section are shown in Figure 4-3.

Figure 4-3. Numeric datatypes

4.4.1. Decimal Types

All decimal types are derived from the xs:decimal primary type and constitute a set of predefined types that address the most common usages.

xs:decimal
This datatype represents the decimal numbers. The number of digits can be arbitrarily long (the datatype doesn't impose any restriction), but obviously, since a XML document has an arbitrary but finite length, the number of digits of the lexical representation of a xs:decimal value needs to be finite. Although the number of digits is not limited, we will see in the next chapter how the author of a schema can derive user-defined datatypes with a limited number of digits if needed.

Leading and trailing zeros are not significant and may be trimmed. The decimal separator is always a dot ("."); a leading sign ("+" or "-") may be specified and any characters other than the 10 digits (including whitespaces) are forbidden. Scientific notation ("E+2") is also forbidden and has been reserved to the float datatypes only.

Valid values for xs:decimal include:

123.456

+1234.456

-1234.456

-.456

-456

The following values are invalid:

1 234.456 (spaces are forbidden)

1234.456E+2 (scientific notation ("E+2") is forbidden)

+ 1234.456 (spaces are forbidden)

+1,234.456 (delimiters between thousands are forbidden)

xs:integer is the only datatype directly derived from xs:decimal.

xs:integer
This integer datatype is a subset of xs:decimal, representing numbers which don't have any fractional digits in its lexical or value spaces. The characters that are accepted are reduced to 10 digits and an optional leading sign. Like its base datatype, xs:integer doesn't impose any limitation on the number of digits, and leading zeros are not significant.

Valid values for xs:integer include:

123456

+00000012

-1

-456

The following values are invalid:

1 234 (spaces are forbidden)

1. (the decimal separator is forbidden)

+1,234 (delimiters between thousands are forbidden).

xs:integer has given birth to three derived datatypes: xs:nonPositiveInteger and xs:nonNegativeInteger (which have still an unlimited length) and xs:long (to fit in a 64-bit word).

xs:nonPositiveInteger and xs:negativeInteger
The W3C XML Schema Working Group thought that it would be more clear that the value "0" was included if they used litotes as names, and used xs:nonPositiveInteger if the integers are negative or null. xs:negativeInteger is derived from xs:nonPositiveInteger to represent the integers that are strictly negative. These two datatypes allow integers of arbitrary length.

xs:nonNegativeInteger and xs:positiveInteger
Similarly, xs:nonNegativeInteger is the integers that are positive or equal to zero and xs:positiveInteger is derived from this type. The "unsigned" family branch (xs:unsignedLong, xs:unsignedInt, xs:unsignedShort, and xs:unsignedByte) is also derived from xs:nonNegativeInteger.

xs:long, xs:int, xs:short, and xs:byte.
The datatypes we have seen up to now have an unconstrained length. This approach isn't very microprocessor-friendly. This subfamily represents signed integers that can fit into 8, 16, 32, and 64-bit words. xs:long is defined as all of the integers between -9223372036854775808 and 9223372036854775807, i.e., the values that can be stored in a 64-bit word. The same process is applied again to derive xs:int with a range between -2147483648 and 2147483647 (32 bits), to derive xs:short with a range between -32768 and 32767 (16 bits), and to derive xs:byte with a range between -128 and 127 (8 bits).

xs:unsignedLong, xs:unsignedInt, xs:unsignedShort, and xs:unsignedByte.
The last of the predefined integer datatypes is the subfamily of unsigned (i.e., positive) integers that can fit into 8, 16, 32, and 64-bit words. xs:unsignedLong is defined as the integers in a range between 0 and 18446744073709551615, i.e., the values that can be stored in a 64-bit word. The same process is applied again to derive xs:unsignedInt with a range between 0 and 4294967295 (32 bits), to derive xs:unsignedShort with a range between 0 and 65535 (16 bits), and to derive xs:unsignedByte with a range between 0 and 255 (8 bits).

4.4.2. Float Datatypes

xs:float and xs:double
xs:float and xs:double are both primitive datatypes and represent IEEE simple (32 bits) and double (64 bits) precision floating-point types. These store the values in the form of mantissa and an exponent of a power of 2 (m x 2^e), allowing a large scale of numbers in a storage that has a fixed length. Fortunately, the lexical space doesn't require that we use powers of 2 (in fact, it doesn't accept powers of 2), but instead lets us use a traditional scientific notation with integer powers of 10. Since the value spaces (powers of 2) don't exactly match the values from the lexical space (powers of 10), the recommendation specifies that the closest value is taken. The consequence of this approximate matching is that float datatypes are the domain of approximation; most of the float values can't be considered exact, and are approximate.

These datatypes accept several "special" values: positive zero (0), negative zero (-0) (which is greater than positive 0 but less than any negative value), infinity (INF) (which is greater than any value), negative infinity (-INF) (which is less than any float, and "not a number" (NaN).

Valid values for xs:float and xs:double include:

123.456

+1234.456

-1.2344e56

-.45E-6

INF

-INF

NaN

The following values are invalid:

1234.4E 56 (spaces are forbidden)

1E+2.5 (the power of 10 must be an integer)

+INF (positive infinity doesn't expect a sign)

NAN (capitalization matters in special values)

4.4.3. xs:boolean

xs:boolean
This is a primitive datatype that can take the values true and false (or 1 and 0).

Previous | Home | Next
4.3. String Datatypes | Book Index | 4.5. Date and Time Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Date and Time Datatypes (XML Schema)

 XML SchemaSearch this book

4.5. Date and Time Datatypes

The datatypes covered in this section are shown in Figure 4-4.

Figure 4-4. Date and time datatypes

4.5.1. The Realm of ISO 8601

The W3C Recommendation, "XML Schema Part 2: Datatypes," provides new confirmation of how difficult it is to fix time.

The support for date and time datatypes relies entirely on a subset of the ISO 8601 standard, which is the only format supported by W3C XML Schema. The purpose of ISO 8601 is to eliminate the risk of confusion between the various date and time formats used in different countries. In other words, W3C XML Schema does not support these local date and time formats, and imposes the usage of ISO 8601 for any datatype that has the semantic of a date or time. While this is a good thing for interchange formats, this is more questionable when XML is used to define user interfaces, since we will see that ISO 8601 is not very user friendly. The variations using the names of the months or different orders between year, month, and day are not the only victims of this decision: ISO 8601 imposes the usage of the Gregorian (Christian) calendar to the exclusion of calendars used by other cultures or religions.

ISO 8601 describes several formats to define date, times, periods, and recurring dates, with different levels of precision and indetermination. After many discussions, W3C XML Schema selected a subset of these formats and created a primitive datatype for each format that is supported.

The indeterminacy allowed in some of these formats adds a lot of difficulty, especially when comparisons or arithmetic are involved. For instance, it is possible to define a point in time without specifying the time zone, which is then considered undetermined. This undetermined time zone is identical all over the document (and between the schema and the instance documents) and it's not an issue to compare two datetimes without a time zone. The problem arises when you need to compare two points in time, one with a time zone and the other without. The result of this comparison will be undetermined if these values are too close, since one of them may be between -13 hours and +12 hours of Coordinated Universal Time (UTC). Thus, the support of these datetime datatypes introduces a notion of "partial order relation."

Another caveat with ISO 8601 is that time zones are only supported through the time difference from UTC, which ignores the notion of summer time. For instance, if an application working in British Summer Time (BST) wants to specify the time zone--and we have seen that this is necessary to be able to compare datetimes--the application needs to know if a date is in summer (the time zone will be one hour after UTC) or in winter (the time zone would then be UTC). ISO 8601 ignores the "named time zones" using the summer saving times (such as PST, BST, or WET) that we use in our day-to-day life; ignoring the time zones can be seen as a somewhat dangerous shortcut to specify that a datetime is on your "local time," whatever it is.

4.5.2. Datatypes

Point in time: xs:dateTime
The xs:dateTime datatype defines a "specific instant of time." This is a subset of what ISO 8601 calls a "moment of time." Its lexical value follows the format "CCYY-MM-DDThh:mm:ss," in which all the fields must be present and may optionally be preceded by a sign and leading figures, if needed, and followed by fractional digits for the seconds and a time zone. The time zone may be specified using the letter "Z," which identifies UTC, or by the difference of time with UTC.

TIP: The value space of xs:dateTime is considered to be the moment of time itself. The time zone that defines the value (when there is one) is considered meaningless, which is a problem for some applications that complain that even though 2002-01-18T12:00:00+00:00 and 2002-01-18T11:00:00-01:00 refer to the same "moment of time," they carry different time zone information, which should make its way into the value space.

Valid values for xs:dateTime include:

2001-10-26T21:32:52

2001-10-26T21:32:52+02:00

2001-10-26T19:32:52Z

2001-10-26T19:32:52+00:00

-2001-10-26T21:32:52

2001-10-26T21:32:52.12679

The following values are invalid:

2001-10-26 (all the parts must be specified)

2001-10-26T21:32 (all the parts must be specified)

2001-10-26T25:32:52+02:00 (the hours part (25) is out of range)

01-10-26T21:32 (all the parts must be specified)

In the valid examples given above, three of them have identical value spaces:

2001-10-26T21:32:52+02:00

2001-10-26T19:32:52Z

2001-10-26T19:32:52+00:00

The first one (2001-10-26T21:32:52), which doesn't include a time zone specification, is considered to have an indeterminate value between 2001-10-26T21:32:52-14:00 and 2001-10-26T21:32:52+14:00. With the usage of summer saving time, this range is subject to national regulations and may change. The range was between -13:00 and +12:00 when the Recommendation was published, but the Working Group has kept a margin to accommodate possible changes in the regulations.

Despite the indeterminacy of the time zone when none is specified, the W3C XML Schema Recommendation considers that the values of datetimes without time zones implicitly refer to the same undetermined time zone and can be compared between them. While this is fine for "local" applications that operate in a single time zone, this is a source of potential confusion and errors for world-wide applications or even for applications that calculate a duration between moments belonging to different time saving seasons within a single time zone.

Periods of time: xs:date, xs:gYearMonth and xs:gYear.
The lexical space of xs:date datatype is identical to the date part of xs:dateTime. Like xs:dateTime, it includes a time zone that should always be specified to be able to compare two dates without ambiguity. As defined per W3C XML Schema, a date is a period one day in its time zone, "independent of how many hours this day has." The consequence of this definition is that two dates defined in a different time zone cannot be equal except if they designate the same interval (2001-10-26+12:00 and 2001-10-25-12:00, for instance). Another consequence is that, like with xs:dateTime, the order relation between a date with a time zone and a date without a time zone is partial.

Valid values for xs:date include:

2001-10-26

2001-10-26+02:00

2001-10-26Z

2001-10-26+00:00

-2001-10-26

-20000-04-01

The following values are invalid:

2001-10 (all the parts must be specified)

2001-10-32 (the days part (32) is out of range)

2001-13-26+02:00 (the month part (13) is out of range)

01-10-26 (the century part is missing)

xs:date represents a day identified by a Gregorian calendar date (and could have been called "gYearMonthDay"). xs:gYearMonth ("g" for Gregorian) is a Gregorian calendar month and xs:gYear is a Gregorian calendar year. These three datatypes are fixed periods of time and optional time zones may be specified for each of them. The only differences between them really are their length (1 day, 1 month, and 1 year) and their format (i.e., their lexical spaces).

The format of xs:gYearMonth is the format of xs:date without the day part. Valid values for xs:gYearMonth include:

2001-10

2001-10+02:00

2001-10Z

2001-10+00:00

-2001-10

-20000-04

The following values are invalid:

2001 (the month part is missing)

2001-13 (the month part is out of range)

2001-13-26+02:00 (the month part is out of range)

01-10 (the century part is missing)

The format of xs:gYear is the format of xs:gYearMonth without the month part. Valid values for xs:gYear include:

2001

2001+02:00

2001Z

2001+00:00

-2001

-20000

The following values are invalid:

01 (the century part is missing)

2001-13 (the month part is out of range)

This support of time periods is very restrictive: these periods can only match the Gregorian calendar day, month, or year, and cannot have an arbitrary length or start time.

Recurring point in time: xs:time
The lexical space of xs:time is identical to the time part of xs:dateTime. The semantic of xs:time represents a point in time that recurs every day; the meaning of 01:20:15 is "the point in time recurring each day at 01:20:15 am." Like xs:date and xs:dateTime, xs:time accepts an optional time zone definition. The same issue arises when comparing times with and without time zones.

NOTE: Despite the fact that: 01:20:15 is commonly used to represent a duration of 1 hour, 20 minutes, and 15 seconds, a different format has been chosen to represent a duration.

Valid values for xs:time include:

21:32:52

21:32:52+02:00

19:32:52Z

19:32:52+00:00

21:32:52.12679

The following values are invalid:

21:32 (all the parts must be specified)

25:25:10 (the hour part is out of range)

-10:00:00 (the hour part is out of range)

1:20:10 (all the digits must be supplied)

This support of a recurring point in time is also very limited: the recursion period must be a Gregorian calendar day and cannot be arbitrary.

Recurring period of time: xs:gDay, xs:gMonth, and xs:gMonthDay.
We have already seen points in times and periods, as well as recurring points in time. This wouldn't be complete without a description of recurring periods. W3C XML Schema supports three predefined recurring periods corresponding to Gregorian calendar months (recurring every year) and days (recurring each month or year). The support of recurring periods is restricted both in terms of recursion (the recursion period can only be a Gregorian calendar year or month) and period (the start time can only be a Gregorian calendar day or month, and the duration can only be a Gregorian calendar month or year).

xs:gDay is a period of a Gregorian calendar day recurring each Gregorian calendar month. The lexical representation of xs:gDay is ---DD with an optional time zone specification. Valid values for xs:gDay include:

---01

---01Z

---01+02:00

---01-04:00

---15

---31

The following values are invalid:

--30- (the format must be "---DD")

---35 (the day is out of range)

---5 (all the digits must be supplied)

15 (missing the leading "---")

The rules of arithmetic between dates and durations apply in this case, and days are "pinned" in the range for each month. In our example, --31, the selected dates will be January 31st, February 28th (or 29th), March 31st, April 30th, etc.

xs:gMonthDay is a period of a Gregorian calendar day recurring each Gregorian calendar year. The lexical representation of xs:gMonthDay is --MM-DD with an optional time zone specification. Valid values for xs:gMonthDay include:

--05-01

--11-01Z

--11-01+02:00

--11-01-04:00

--11-15

--02-29

The following values are invalid:

-01-30- (the format must be --MM-DD)

--01-35 (the day part is out of range)

--1-5 (one part is missing)

01-15 (the leading -- is missing)

xs:gMonth is a period of a Gregorian calendar month recurring each Gregorian calendar year. The lexical representation of xs:gMonth defined in the Recommendation is --MM-- with an optional time zone specification. The W3C XML Schema Working Group has acknowledged that this was an error and that the format --MM defined by ISO 8061 should be used instead. It has not been decided yet if the format described in the Recommendation will be forbidden or only deprecated, but it is advised to use the format --MM (assuming that the tools you are using already support it). Valid values for xs:gMonth include:

--05

--11Z

--11+02:00

--11-04:00

--02

The following values are invalid:

-01- (the format must be --MM)

--13 (the month is out of range)

--1 (both digits must be provided)

01 (the leading -- is missing)

xs:duration
Naive programmers who think that the concept of duration is simple should read the Recommendation, which states: xs:duration is defined as a six-dimensional space!" Mathematicians would object that this is not absolutely true since most of the axes of these dimension are parallel, but the fact is that when these programmers say that a development will last one month and 3 days, they define a duration that is comprised of between 31 and 34 days. The attempt of W3C XML Schema to deal with these issues on top of ISO 8601 has introduced a degree of indeterminacy in the comparisons between durations.

The lexical space of xs:duration is the format defined by ISO 8601 under the form PnYnMnDTnHnMnS, in which the capital letters are delimiters that can be omitted when the corresponding member is not used. An important difference with the format used for xs:dateTime is none of these members are mandatory and none of them are restricted to a range. This gives flexibility to choose the units that will be used and to combine several of them--for instance, P1Y2MT123S (1 year, 2 months, and 123 seconds). This flexibility has a price; such a duration is not completely defined: a year may have 365 or 366 days, and a period of two months lasts between 59 and 62 days. Durations cannot always be compared and the order between durations is partial. We will see, in the next chapter, that user-defined datatypes can be derived from xs:duration, which can restrict the components used to express durations and insure that these indeterminations do not happen.

Since the value of a duration is fixed as soon as you give it a starting point, the schema Working Group has identified four datetimes:

1696-09-01T00:00:00Z

1697-02-01T00:00:00Z

1903-03-01T00:00:00Z

1903-07-01T00:00:00Z

These cause the greatest deviations when durations mixing day, month, and other components are added. The Working Group has determined that the comparison of durations is undefined if--and only if--the result of the comparison is different when each of these dates is used as a starting point.

Valid values for xs:duration include:

PT1004199059S

PT130S

PT2M10S

P1DT2S

-P1Y

P1Y2M3DT5H20M30.123S

The following values are invalid:

1Y (the leading P is missing)

P1S (the T separator is missing)

P-1Y (all parts must be positive)

P1M2Y (the parts order is significant and Y must precede M)

P1Y-1M (all parts must be positive)

Previous | Home | Next
4.4. Numeric Datatypes | Book Index | 4.6. List Types
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


List Types (XML Schema)

 XML SchemaSearch this book

4.6. List Types

These datatypes are lists of whitespace-separated items. The type of these items (called the item type) is defined during the derivation process (which we will see in the next chapter) and list datatypes can be derived from any simple type. Three predefined datatypes are lists (xs:NMTOKENS, xs:IDREFS, and xs:ENTITIES). For all the list datatypes, the items must be separated by one or more whitespaces.

xs:NMTOKENS
This is a whitespace-separated list of xs:NMTOKEN. Each item of the list must be in the lexical space of xs:NMTOKEN.

xs:IDREFS
This is a whitespace-separated list of xs:IDREF. Each item of the list must be in the lexical space of xs:IDREF and must reference an existing xs:ID in the same document.

xs:ENTITIES
This is a whitespace-separated list of xs:ENTITY. Each item of the list must be in the lexical space of xs:ENTITY and must match an unparsed entity defined in a DTD.

Previous | Home | Next
4.5. Date and Time Datatypes | Book Index | 4.7. What About anySimpleType?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What About anySimpleType? (XML Schema)

 XML SchemaSearch this book

4.7. What About anySimpleType?

We have now covered all the predefined datatypes except one, which is an atypical type: anySimpleType. This datatype is a kind of wildcard, which means, as expected, that any value is accepted and doesn't add any constraint on the lexical space.

anySimpleType has two other characteristics that make it unique among simple types: users' simple types cannot be derived from it and its properties, and its canonical form is not defined in the Recommendation! These characteristics make it a type that should be avoided, except when the rules of a derivation (which we will see in the next chapter) require its usage.

Previous | Home | Next
4.6. List Types | Book Index | 4.8. Back to Our Library
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Back to Our Library (XML Schema)

 XML SchemaSearch this book

4.8. Back to Our Library

If we look back with a critical eye at our library, we see we used the following simple datatypes:

<xs:element name="name" type="xs:string"/>

<xs:element name="qualification" type="xs:string"/>

<xs:element name="born" type="xs:date"/>

<xs:element name="dead" type="xs:date"/>

<xs:element name="isbn" type="xs:string"/>

<xs:attribute name="id" type="xs:ID"/>

<xs:attribute name="available" type="xs:boolean"/>

<xs:attribute name="lang" type="xs:language"/>

We are lucky that the elements born and dead are ISO 8601 dates. The ISBN number is composed of numeric digits and a final character which can be either a digit or the letter "x"-and is therefore represented as a string. We also did a good job with the datatypes for the id, available and lang attributes, but the choice of xs:string for the elements name and qualification is more controversial. They appear in the instance document as:

<name>
  Charles M Schulz
</name>
                      .../...

<qualification>
  bold, brash and tomboyish
</qualification>

This formatting suggests that whitespaces are probably not significant and should be collapsed. This can be done by choosing the datatype xs:token instead of xs:string; the same applies to the title element, which is a simple content derived from xs:string that would be better derived from xs:token. This change will not have any impact on the validation with our schema, but the document is more precisely described and future derivations would be more easily built on xs:token than on xs:string. The other datatype that could have been chosen better is isbn, which can be represented as xs:NMTOKEN. The new schema would then be:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="name" type="xs:token"/>
  <xs:element name="qualification" type="xs:token"/>
  <xs:element name="born" type="xs:date"/>
  <xs:element name="dead" type="xs:date"/>
  <xs:element name="isbn" type="xs:NMTOKEN"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:token">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
      <xs:attribute ref="available"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="qualification"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

Previous | Home | Next
4.7. What About anySimpleType? | Book Index | 5. Creating Simple Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Creating Simple Datatypes (XML Schema)

 XML SchemaSearch this book

Chapter 5. Creating Simple Datatypes

Contents:

Derivation By Restriction
Derivation By List
Derivation By Union
Some Oddities of Simple Types
Back to Our Library

So far, we have used only predefined datatypes. In this chapter, we will see how to create new simple types, taking advantage of the different derivation mechanisms and facets of derivation by restriction.

W3C XML Schema has defined three independent and complementary mechanisms for defining our own custom datatypes, using existing datatypes as starting points. These new user datatypes that are built upon existing predefined datatypes or on other user datatypes are called "derivation."

The three derivation methods are derivation by restriction (where constraints are added on a datatype without changing its original semantic or meaning), derivation by list (where new datatypes are defined as being lists of values belonging to a datatype and take the semantic of list datatypes), and derivation by union (where new datatypes are defined as allowing values from a set of other datatypes and lose most of their semantic).

As with the xs:complexType, definitions (which we saw in our Russian doll design) and xs:simpleType(global definition) can be either named or anonymous. Despite this similarity, simple and complex types are very different. A simple type is a restriction on the value of an element or an attribute (i.e., a constraint on the content of a set of documents) while a complex type is a definition of a content model (i.e., a constraint on the markup). This is why the derivation methods for simple and complex types are very different, even though W3C XML Schema used the same element name (xs:restriction) for both. This is a common source of confusion.

TIP: These derivation methods are flexible and powerful. However, that W3C XML Schema needs many different primary datatypes can be seen as proof that they are not sufficient to create a new primary datatype. The reason being that the derivation methods are only acting on the value space or on the lexical space (as defined in Chapter 4, "Using Predefined Simple Datatypes"), but they cannot modify the relations between these two spaces, nor create new value or lexical spaces. This subject has been debated by the W3C XML Schema Working Group, which has not found an agreement for ways to define an abstract datatype system that would allow definition of several lexical representations. The most obvious consequence of this decision is that, despite the protestation from the W3C I18N Working Group, W3C XML Schema doesn't allow the definition of localized decimal or date datatypes.

5.1. Derivation By Restriction

Restriction is probably the most commonly used and natural derivation method.Datatypes are created by restriction by adding new constraints to the possible values. W3C XML Schema itself has been using derivation by restriction to define most of derived predefined datatypes, such as xs:positiveInteger, which is a derivation by restriction of xs:integer. The restrictions can be defined along different aspects or axes that W3C XML Schema calls "facets."

A derivation by restriction is done using a xs:restriction element and each facet is defined using a specific element embedded in the xs:restriction element. The datatype on which the restriction is applied is called the base datatype, which can be referenced though a <base> attribute or defined in the xs:restriction element:

<xs:simpleType name="myInteger">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="-2"/>
    <xs:maxExclusive value="5"/>
  </xs:restriction>
</xs:simpleType>

It can also be defined in two steps using an embedded xs:simpleType(global definition) anonymous definition:

<xs:simpleType name="myInteger">
  <xs:restriction>
    <xs:simpleType>
      <xs:restriction base="xs:integer">
        <xs:maxExclusive value="5"/>
      </xs:restriction>
    </xs:simpleType>
    <xs:minInclusive value="-2"/>
  </xs:restriction>
</xs:simpleType>

The xs:minInclusive and xs:maxExclusive elements are two facets that can be applied to an integer datatype. As can be guessed from their names, they specify the minimum inclusive (i.e., that can be reached) and maximum exclusive (i.e., that is not allowed) values. We will introduce the list of facets in the next section. Depending on the facet, each acts directly either on the value space or on the lexical space of the datatype, and the same facet may have different effects depending on the datatype on which it is applied.

Whatever facet is being applied on a datatype, the semantic of its primitive type is unchanged, the list of facets that can be applied cannot be extended, and one must be careful to choose, when possible, a datatype whose primitive type matches the purpose of the node in which it will be used. For instance, while it is possible to constrain a string datatype to match non-ISO 8601 dates using patterns, this solution should be used only when absolutely required since this datatype would still be considered a string and lack facets, such as xs:minInclusive or xs:maxExclusive that are defined on date datatypes but that have no meaning (for W3C XML Schema) on a string.

TIP: The impact of the "right" choice of the base datatype with a semantic as close as possible to its actual usage in the instance documents will become more critical when W3C XML Schema aware applications become available. Such applications will have a different behavior depending on the datatype information found in the PSVI. A "wrong" choice will have side effects. For instance, the first drafts of XPath 2.0 propose to interpret values according to predefined datatypes and the results of equality tests on values or the sort orders would depend on the datatypes.

5.1.1. Facets

Before we start looking at the list of facets, we'll discuss the way they work. They may be classified into three categories: xs:whiteSpace defines the whitespace processing that happens between the parser and lexical spaces--but can be used only on xs:string and xs:normalizedString. xs:pattern works on the lexical space; all the other facets constrain the value space. The availability of the facets and their effect depend on the datatype on which they are applied. We will see them in the context of groups of datatypes sharing the same set of facets.

5.1.1.1. Whitespace collapsed strings

These datatypes share the fact that they are character strings (even though technically W3C XML Schema doesn't consider all of them as derived from the xs:string datatypes) and that whitespaces are collapsed before validation, as defined in the Recommendation, "all occurrences of #x9 (tab), #xA (line feed), and #xD (carriage return) are replaced with #x20 (space) and then, contiguous sequences of #x20s are collapsed to a single #x20, and initial and/or final #x20s are deleted."

Those datatypes are: xs:ENTITY, xs:ID, xs:IDREF, xs:language, xs:Name, xs:NCName, xs:NMTOKEN, xs:token, xs:anyURI, xs:base64Binary, xs:hexBinary, xs:NOTATION, and xs:QName. Their facets are explained in the next section:

5.1.1.1.1. xs:enumeration

xs:enumeration allows definition of a list of possible values. Here's an example:

<xs:simpleType name="schemaRecommendations">
  <xs:restriction base="xs:anyURI">
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-0/"/>
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-1/"/>
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-2/"/>
  </xs:restriction>
</xs:simpleType>

This facet is constraining the value space. For most of the string (and assimilated) datatypes, lexical and values are identical and this doesn't make any difference; however, it does make a difference for xs:anyURI, xs:base64Binary, and xs:QName. For instance, "http://dmoz.org/World/Français/" and "http://dmoz.org/World/Fran%e7ais/" would be considered equal for xs:anyURI, the line breaks would be ignored for xs:base64Binary, and the match would be done on the tuples {namespace URI, local name} for xs:QName, ignoring the prefix used in the schema and instance documents.

One should also note that xs:anyURI datatypes are not "absolutized" by W3C XML Schema and do not support xml:base. This means that if the "schemaRecommendations" defined in the previous example is assigned to a XLink href attribute, it must fail to validate the following instance element:

<a xml:base="http://www.w3.org/TR/" href="xmlschema-1/">
  XML Schema Part 2: Datatypes
</a>

We cannot leave this section without discussing xs:NOTATION. This datatype is the only case of a predefined datatype that cannot be used directly in a schema and must be used through derived types specifying a set of xs:enumeration facets. Even though notations are very seldom used in real-life applications, this book wouldn't be complete without at least an example of notations. If we take the usual example of a picture using a notation in an attribute to qualify the content of a binary field as follows:

<?xml version="1.0"?>
<picture type="png">

  iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAABmJLR0QA/wD/AP+gvaeTAAAA
  CXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH0QofESYx2JhwGwAAAFZJREFUeNqlj8ENwDAI
  A6HqGDCWp2QQ2AP2oI9IbaQm/dRPn9EJ7m7a56DPPDgiIoKIzGyBM9Pdx+4ueXabWVUBEJHR
  nLNJVbfuqspMAEOxwO9r/vX3BTEnKRXtqqslAAAAAElFTkSuQmCC
</picture>

The schema might be written as (note how the notations need to be declared in the schema to be used in an xs:enumeration facet):

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:notation name="jpeg" public="image/jpeg"
    system="file:///usr/bin/xv"/>
  <xs:notation name="gif" public="image/gif"
    system="file:///usr/bin/xv"/>
  <xs:notation name="png" public="image/png"
    system="file:///usr/bin/xv"/>
  <xs:notation name="svg" public="image/svg"
    system="file:///usr/bin/xsmiles"/>
  <xs:notation name="pdf" public="application/pdf"
    system="file:///usr/bin/acroread"/>
  <xs:simpleType name="graphicalFormat">
    <xs:restriction base="xs:NOTATION">
      <xs:enumeration value="jpeg"/>
      <xs:enumeration value="gif"/>
      <xs:enumeration value="png"/>
      <xs:enumeration value="svg"/>
      <xs:enumeration value="pdf"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="picture">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:base64Binary">
          <xs:attribute name="type" type="graphicalFormat"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
</xs:schema>

5.1.1.1.2. xs:length

xs:length defines a fixed length measured in number of characters (general case) or bytes (xs:hexBinary and xs:base64Binary):

<xs:simpleType name="standardNotations">
  <xs:restriction base="xs:NOTATION">
    <xs:length value="8"/>
  </xs:restriction>
</xs:simpleType>

This facet also constrains the value space. For xs:anyURI, this may be difficult to predict since the length is checked after the character normalization. For xs:QName, this is even worse since the W3C XML Schema recommendation has not given any definition of the length of an xs:QName tuple. Fortunately, in practice, constraining the length of these datatypes doesn't seem to be very useful, and it's a good idea to avoid using these constrains on these datatypes. The same restriction applies to the next two facets.

5.1.1.1.3. xs:maxLength

xs:maxLength defines a maximum length measured in number of characters (general case) or bytes (xs:hexBinary and xs:base64Binary):

<xs:simpleType name="binaryImage">
  <xs:restriction base="xs:hexBinary">
    <xs:maxLength value="1024"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.1.4. xs:minLength

xs:minLength defines a minimum length measured in number of characters (general case) or bytes (hexBinary and base64Binary):

<xs:simpleType name="longName">
  <xs:restriction base="xs:NCName">
    <xs:minLength value="6"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.1.5. xs:pattern

xs:pattern defines a pattern that must be matched by the string (we will explore patterns in more detail in the next chapter) :

<xs:simpleType name="httpURI">
  <xs:restriction base="xs:anyURI">
    <xs:pattern value="http://.*"/>
  </xs:restriction>
</xs:simpleType>

Several pattern facets can be defined in a single derivation step. They are then merged together through a logical "or" (a value will match the restricted datatype if it matches one of the patterns).

TIP: Because of the impossibility of defining a single order that would be useful for all the regional alphabets, W3C XML Schema has decided to handle the string datatypes as being unordered. The consequence is there are no facets to define minimal or maximal values for string datatypes.

5.1.1.2. Other strings

The whitespaces of these other strings are not collapsed before validation, and a new facet (xs:whiteSpace) is available, in addition to the facets just described, to specify the treatment to apply on whitespaces for the user-defined datatypes derived from them.

Those datatypes are: xs:normalizedString and xs:string.

5.1.1.2.1. xs:whiteSpace

xs:whiteSpace defines the way to handle whitespaces--i.e., #x20 (space), #x9 (tab), #xA (linefeed), and #xD (carriage return)--for this datatype:

<xs:simpleType name="CapitalizedNameWS">
  <xs:restriction base="xs:string">
    <xs:whiteSpace value="collapse"/>
    <xs:pattern value="([A-Z]([a-z]*) ?)+"/>
  </xs:restriction>
</xs:simpleType>

The values of an xs:whiteSpace facet are "preserve" (whitespaces are kept unchanged), "replace" (all the instances of any whitespace are replaced with a space), and "collapse" (leading and trailing whitespaces are removed and all the other sequences of contiguous whitespaces are replaced by a single space). This facet is atypical since it specifies a treatment to be done on a value before applying any validation test on this value. In the earlier example, setting whitespace to "collapse" allows testing of a single space character in the pattern (" ?"). This ensures the whitespaces are collapsed before the pattern is tested and will match any number of whitespaces.

The whitespace behavior cannot be relaxed during a restriction: if a datatype has a whitespace set as "preserve," its derived datatypes can have any whitespace behavior; if its whitespace is set as "replace," its derived datatypes can only have whitespace equal to "replace" or "collapse"; if its whitespace is "collapse," all its derived datatypes must have the same behavior. This means xs:string is the only datatype that can be used to derive datatypes without any whitespace processing and xs:string and xs:normalizedString are the only datatypes that can be used to derive datatypes normalizing the whitespaces.

In practice, this facet isn't really useful for user-defined datatypes since the whitespace processing largely dictates the choice of the predefined datatype to use. When we need a datatype that does no whitespace processing, we must use xs:string and not xs:whiteSpace. When we need a datatype that normalizes the whitespaces, instead of using xs:string and applying a xs:whiteSpace facet, we can use xs:normalizedString directly, which has the same effect. When we need a datatype that collapses the whitespaces, we can use xs:token if it's a string--since, again, xs:token is not a token in the usual meaning of the word but rather a "tokenized string"--as well as any nonstring datatype. The whitespace processing will already be set to "collapse" without any need to use xs:whiteSpace. The previous example given is then equivalent to:

<xs:simpleType name="CapitalizedNameWS">
  <xs:restriction base="xs:token">
    <xs:pattern value="([A-Z]([a-z]*) ?)+"/>
  </xs:restriction>
</xs:simpleType>
TIP: Technically speaking, the W3C Working Group hasn't "fixed" the xs:whiteSpace facet for xs:token and its derived datatypes. However, xs:whiteSpace has been set to "collapse" for xs:token; since the facet can't be relaxed in further restriction, this value cannot be changed in any datatype derived from these datatypes.

5.1.1.3. Float datatypes

The facets of: xs:double and xs:float are described in the next sections.

5.1.1.3.1. xs:enumeration

xs:enumeration allows definition of a list of possible values and operates on the value space--for example:

<xs:simpleType name="enumeration">
  <xs:restriction base="xs:float">
    <xs:enumeration value="-INF"/>
    <xs:enumeration value="1.618033989"/>
    <xs:enumeration value="3e3"/>
  </xs:restriction>
</xs:simpleType>

This simple type will match literals such as:

<enumeration>
  1.618033989
</enumeration>

<enumeration>
  3e3
</enumeration>

<enumeration>
  003000.0000
</enumeration>

This example shows (as we've briefly seen with xs:anyURI, xs:QName, and xs:base64Binary) two different lexical representations ("3e3" and "003000.0000") for the same value. It also shows, as expected, that all the lexical representations have the same value, so one of the enumerated values will be accepted.

5.1.1.3.2. xs:maxExclusive

xs:maxExclusive defines a maximum value that cannot be reached:

<xs:simpleType name="maxExclusive">
  <xs:restriction base="xs:float">
    <xs:maxExclusive value="10"/>
  </xs:restriction>
</xs:simpleType>

This datatype validates "9.999999999999999," but not "10."

The xs:maxExclusive facet is especially useful for datatypes such as xs:float, xs:double, xs:decimal, or even for datetime types that can cope with infinitesimal values and in which it is not possible to determine the greatest value that is smaller than a value.

5.1.1.3.3. xs:maxInclusive

xs:maxInclusive defines a maximum value that can be reached:

<xs:simpleType name="thousands">
  <xs:restriction base="xs:double">
    <xs:maxInclusive value="1e3"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.3.4. xs:minExclusive

xs:minExclusive defines a minimum value that cannot be reached:

<xs:simpleType name="strictlyPositive">
  <xs:restriction base="xs:double">
    <xs:minExclusive value="0"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.3.5. xs:minInclusive

xs:minInclusive defines a minimum value that can be reached:

<xs:simpleType name="positive">
  <xs:restriction base="xs:double">
    <xs:minInclusive value="0"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.3.6. xs:pattern

xs:pattern defines a pattern that must be matched by the lexical value of the datatype:

<xs:simpleType name="nonScientific">
  <xs:restriction base="xs:float">
    <xs:pattern value="[^eE]*"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="noLeading0">
  <xs:restriction base="xs:float">
    <xs:pattern value="[^0].*"/>
  </xs:restriction>
</xs:simpleType>

This example shows how a pattern, acting on the lexical value of the float, can disable the use of scientific notation (xxxEyyy) or leading zeros.

TIP: The xs:pattern is the only facet that directly acts on the lexical space of the datatype.

5.1.1.4. Date and time datatypes

These datatypes are partially ordered, and bounds can be defined even though some restrictions apply. These datatypes are: xs:date, xs:dateTime, xs:duration, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:gYearMonth, and xs:time and their facets are the same as those of the float datatypes, as shown in the next sections.:

5.1.1.4.1. xs:enumeration

xs:enumeration allows definition of a list of possible values as well as works on the value space--for example:

<xs:simpleType name="ModernSwissHistoricalDates">
  <xs:restriction base="xs:gYear">
    <xs:enumeration value="1864"/>
    <xs:enumeration value="1872"/>
    <xs:enumeration value="1914"/>
    <xs:enumeration value="1939"/>
    <xs:enumeration value="1971"/>
    <xs:enumeration value="1979"/>
    <xs:enumeration value="1992"/>
  </xs:restriction>
</xs:simpleType>

This simple type will match literals such as:

1939

Since no time zone is specified for the dates in the enumeration, the time zone is undetermined. These dates do not match any date with a time zone specified, such as:

1939Z

or:

1939+10:00

The same issue appears if enumerations include a time zone, such as in:

<xs:simpleType name="wakeUpTime">
  <xs:restriction base="xs:time">
    <xs:enumeration value="07:00:00-07:00"/>
    <xs:enumeration value="07:15:00-07:00"/>
    <xs:enumeration value="07:30:00-07:00"/>
    <xs:enumeration value="07:45:00-07:00"/>
    <xs:enumeration value="08:00:00-07:00"/>
  </xs:restriction>
</xs:simpleType>

This new datatype matches:

07:00:00-07:00

as well as:

11:00:00-04:00

and even:

07:15:00-07:15

but will not validate any time with a time zone.

Even though handling both times with and without time zones is problematic and questionable, it is possible to mix enumerations of values with and without time zones, such as:

<xs:simpleType name="sevenOClockPST">
  <xs:restriction base="xs:time">
    <xs:enumeration value="07:00:00-07:00"/>
    <xs:enumeration value="07:00:00"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.4.2. xs:maxExclusive

xs:maxExclusive defines a maximum value that can be reached:

<xs:simpleType name="beforeY2K">
  <xs:restriction base="xs:dateTime">
    <xs:maxExclusive value="2000-01-01T00:00:00Z"/>
  </xs:restriction>
</xs:simpleType>

This datatype validates any date strictly less than Y2K UTC, such as:

1999-12-31T23:59:59Z

or:

1999-12-31T23:59:59.999999999999Z

It will also validate the following; even if expressed using any other time zone, such as:

2000-01-01T11:59:59+12:00

It doesn't validate:

2000-01-01T00:00:00Z

The interval of indeterminacy of +/-14 hours is applied when compared to datetimes without a time zone. The greatest datetime without a time zone (without counting the fractions of seconds) is therefore:

1999-12-31T09:59:59

5.1.1.4.3. xs:maxInclusive

xs:maxInclusive defines a maximum value that can be reached:

<xs:simpleType name="AQuarterOrLess">
  <xs:restriction base="xs:duration">
    <xs:maxInclusive value="P3M"/>
  </xs:restriction>
</xs:simpleType>

This datatype validates all the durations less than or equal to 3 months. Durations such as P2M (2 months) or P3M (3 months) qualify. If both months and days are used, P2M30D (2 months and 30 days) will be valid, but P2M31D (2 months and 31 days), or even P2M30DT1S (2 months, 30 days and 1 second), will be rejected because of the indetermination of the actual duration when parts from year/month on one side and day/hours/minutes/seconds on the other side are used.

5.1.1.4.4. xs:minExclusive

xs:minExclusive defines a minimum value that can be reached:

<xs:simpleType name="afterTeaTimeInParisInSummer">
  <xs:restriction base="xs:time">
    <xs:minExclusive value="17:00:00+02:00"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.4.5. xs:minInclusive

xs:minInclusive defines a minimum value that can be reached:

<xs:simpleType name="afterOrOnThe20th">
  <xs:restriction base="xs:gDay">
    <xs:minInclusive value="---20"/>
  </xs:restriction>
</xs:simpleType>

We can also take back our example using durations and define:

<xs:simpleType name="AQuarterOrMore">
  <xs:restriction base="xs:duration">
    <xs:minInclusive value="P3M"/>
  </xs:restriction>
</xs:simpleType>

This datatype validates all durations that are more than or equal to 3 months. Durations such as P4M (4 months) or P3M (3 months) will qualify. If both months and days are used, P2M31D (2 months and 31 days) will be valid, but P2M30D (2 months and 30 days), or even P2M30DT23H59M59S (2 months, 30 days, 23 hours, 59 minutes and 59 seconds), will be rejected because of the indetermination of the actual duration.

Because of this indeterminacy, W3C XML Schema considers our third month to have 30 days when we apply xs:minInclusive, and 31 days when we apply xs:maxInclusive. In practice, it may be wise to invalidate the usage of combinations allowing such an indeterminacy. We will see in the next chapter how to do it with a pattern.

5.1.1.4.6. xs:pattern

xs:pattern defines a pattern that must be matched by the lexical value of the datatype. We will see patterns in detail in the next chapter. To get an idea of what they look like, look at the following datatype. It forbids usage of a time zone by an xs:dateTime datatype:

<xs:simpleType name="noTimeZone">
  <xs:restriction base="xs:dateTime">
    <xs:pattern value=".*T[^Z+-]*"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.5. Integer and derived datatypes

These datatypes are: xs:byte, xs:int, xs:integer, xs:long, xs:negativeInteger, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:positiveInteger, xs:short, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, and xs:unsignedShort.

They accept the same facets of float datatypes as datetime of float datatypes, which we just saw, plus an additional facet to constraint the number of digits, as shown next.

5.1.1.5.1. xs:totalDigits

xs:totalDigits defines the maximum number of decimal digits:

<xs:simpleType name="totalDigits">
  <xs:restriction base="xs:integer">
    <xs:totalDigits value="5"/>
  </xs:restriction>
</xs:simpleType>

This datatype accepts only integers with up to five decimal digits.

xs:totalDigits acts on the value space, which means that the integer "000012345," whose canonical value is "12345," matches the datatype defined previously.

5.1.1.6. Decimals

This single datatype (xs:decimal) accepts all the facets of the integers and an additional facet to define the number of fractional digits as shown next.

5.1.1.6.1. xs:fractionDigits

xs:fractionDigits specifies the maximum number of decimal digits in the fractional part (after the dot) :

<xs:simpleType name="fractionDigits">
  <xs:restriction base="xs:decimal">
    <xs:fractionDigits value="2"/>
  </xs:restriction>
</xs:simpleType>

xs:fractionDigits acts on the value space, which means that the integer "1.12000," whose canonical value is "1.12," matches the datatype defined previously.

5.1.1.7. Booleans

With only one facet allowed, as far as restriction facets are concerned, the simplest datatype is xs:boolean. The value space of this simple datatype is limited to "true" and "false," but its lexical space also includes "0" and "1." The xs:pattern facet can be used to exclude one of these formats.

5.1.1.7.1. xs:pattern

The functionality of xs:pattern is usually very rich; however, given the limited number of values of the xs:boolean, its only use here appears to be to fix a format:

<xs:simpleType name="trueOrFalse">
  <xs:restriction base="xs:boolean">
    <xs:pattern value="true"/>
    <xs:pattern value="false"/>
  </xs:restriction>
</xs:simpleType>

5.1.1.8. List datatypes

The available facets for the list datatypes ( xs:IDREFS, xs:ENTITIES, and xs:NMTOKENS) are the facets available for all the datatypes that are derived by list, as we will see in the next section.

5.1.2. Multiple Restrictions and Fixed Attribute

New restrictions can be applied to datatypes that are already derived by restriction from other types.

When the new restrictions are done on facets that have not yet been constrained, the new facets are just added to the set of facets already defined. The value and lexical spaces of the new datatype are the intersection of all the restrictions. Things become more complex when the same facets are being redefined, and restricting facets can extend the value space.

As far as multiple facet definitions are concerned, we can classify the facets into four categories, described in the next sections.

5.1.2.1. Facet that can be changed but needs to be more restrictive

This is the general case. xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:maxLength, xs:minExclusive, xs:minInclusive, xs:minLength, and xs:totalDigits are in this case.

For all these facets, it is forbidden to add a facet that expands the value space of the base datatype. The following examples demonstrate such errors:

<xs:simpleType name="minInclusive">
  <xs:restriction base="xs:float">
    <xs:minInclusive value="10"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="minInclusive2">
  <xs:restriction base="minInclusive">
    <xs:minInclusive value="0"/>
  </xs:restriction>
</xs:simpleType>

or:

<xs:simpleType name="enumeration">
  <xs:restriction base="xs:float">
    <xs:enumeration value="-INF"/>
    <xs:enumeration value="1.618033989"/>
    <xs:enumeration value="3e3"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="enumeration2">
  <xs:restriction base="enumeration">
    <xs:enumeration value="0"/>
  </xs:restriction>
</xs:simpleType>

5.1.2.2. Facet that cannot be changed

The xs:length facet is the only one in this category. The length of a derived datatype cannot be redefined if the length of its parent has been defined.

xs:length can be seen as a shortcut for assigning an equal value to xs:maxLength and xs:minLength. This behavior is coherent with what happens if these two facets are both used with the same value: further values of xs:maxLength must be inferior or equal to the length, and further values of xs:minLength must be greater than or equal to the length. Since xs:minLength must also be smaller than or equal to xs:maxLength, the only possibility is that they all need to stay equal to the length as previously defined.

5.1.2.3. Facet that performs the intersection of the lexical spaces

The xs:pattern facet is the only facet that can be applied multiple times. It always restricts the lexical space by performing a straight intersection of the lexical spaces. The following noScientificNoLeading0 datatype will try to match the patterns for both the base datatype and the new restriction:

<xs:simpleType name="nonScientific">
  <xs:restriction base="xs:float">
    <xs:pattern value="[^eE]*"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="noScientificNoLeading0">
  <xs:restriction base="nonScientific">
    <xs:pattern value="[^0].*"/>
  </xs:restriction>
</xs:simpleType>

5.1.2.4. Facet that does its job before the lexical space

xs:whiteSpace is a remarkable exception. This facet defines the whitespace processing and can actually expand the set of accepted instance documents during a "restriction," as shown in the following example:

<xs:simpleType name="greetings">
  <xs:restriction base="xs:string">
    <xs:whiteSpace value="replace"/>
    <xs:enumeration value="hi"/>
    <xs:enumeration value="hello"/>
    <xs:enumeration value="how do you do?"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="restricted-greetings">
  <xs:restriction base="greetings">
    <xs:whiteSpace value="collapse"/>
  </xs:restriction>
</xs:simpleType>

While the first datatype ("greetings") accepts:

how do you do?

but rejects a string such as:

how do     you do?

the type issued from the "restriction" accepts both.

5.1.2.5. Fixed facets

Each facet (except xs:enumeration and xs:pattern) includes a fixed attribute which, when set to true, disables the possibility of modifying the facet during further restrictions by derivation.

If we want to make sure that the minimum value of our minInclusive cannot be modified, we write:

<xs:simpleType name="minInclusive">
  <xs:restriction base="xs:float">
    <xs:minInclusive value="10" fixed="true"/>
  </xs:restriction>
</xs:simpleType>
NOTE: This is the method used by the schema for W3C XML Schema to fix the value of the facets used to derive predefined datatypes. For instance, the type xs:integer is derived from xs:decimal through:

<xs:simpleType name="integer" id="integer">
  <xs:restriction base="xs:decimal">
    <xs:fractionDigits value="0" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

<xs:enumeration> and <xs:pattern> cannot be fixed.

Previous | Home | Next
4.8. Back to Our Library | Book Index | 5.2. Derivation By List
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Derivation By List (XML Schema)

 XML SchemaSearch this book

5.2. Derivation By List

Derivation by list is the mechanism by which a list datatype can be derived from an atomic datatype. All the items in the list need to have the same datatype.

5.2.1. List Datatypes

List datatypes are special cases in which a structure is defined within the content of a single attribute or element. This practice is usually discouraged since applications do not have access to the atomic values through the current XML APIs, XPath expressions, or in the Infoset. This situation might change in the future since these datatypes should be adopted by XPath 2.0, which will likely provide some kind of mechanism to access to the items within these lists.

This feature appears to have been introduced to maintain compatibility with SGML and XML DTD IDREFS, but W3C XML Schema has been cautious and doesn't allow definition of the list separator or complex lists with complex types or heterogeneous members. Among the constructs that can be seen in some XML vocabularies and cannot be described by XML Schema (except by using regular expressions as a partial workaround) are comma-separated lists of values, and lists with heterogeneous members, such as values with units:

<commaSeparated>
  1, 2, 25
</commaSeparated>

<valueWithUnit>
  10 em
</valueWithUnit>

Whitespace-separated lists and split XML elements or attributes are preferred:

<commaSeparated>
  1 2 25
</commaSeparated>

<valueWithUnit unit="em">
  10
</valueWithUnit>

<valueWithUnit>
  10em
</valueWithUnit>

IDREFS, ENTITIES, and NMTOKENS are predefined list datatypes that are derived from atomic types using this method.

As we have seen with these three datatypes, all the list datatypes that can be defined must be whitespace-separated. No other separator is accepted.

With this restriction, defining a list is very simple, and W3C XML Schema has defined two syntaxes. Both use a xs:list element, which allows a definition by reference to existing types or embeds a type definition (these two syntaxes cannot be mixed).

The definition of a list datatype by reference to an existing type is done through a itemType attribute:

<xs:simpleType name="integerList">
  <xs:list itemType="xs:integer"/>
</xs:simpleType>

This datatype can be used to define attributes or elements that accept a whitespace-separated list of integers such as: "1 -25000 1000."

The definition of a list datatype can also be done by embedding a xs:simpleType(global definition) element:

<xs:simpleType name="myIntegerList">
  <xs:list>
    <xs:simpleType>
      <xs:restriction base="xs:integer">
        <xs:maxInclusive value="100"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:list>
</xs:simpleType>

This datatype can be used to define attributes or elements that accept a whitespace-separated list of integers smaller than or equal to 100 such as: "1 -25000 100."

List datatypes have their own value space that can be constrained using a set of specific facets that is common to all of them.

These facets are xs:length, xs:maxLength, xs:minLength, xs:enumeration and xs:whiteSpace. The unit used to measure the length of a list type is always the number of elements in the list.

To apply these facets to a user-defined list type, we need to follow two steps. We first define the list datatype, and then define a datatype to constrain the list datatype. The reason for this is each xs:simpleType(global definition) accepts only one derivation method chosen between the three existing methods.

In this process, the derivation by restriction has to be done first, since a list datatype loses the facets of its atomic type and has the only five facets just described that have a meaning that is specific to list types.

Defining Atomic Datatypes That Allow Whitespace

It is possible to define lists of atomic datatypes that allow whitespaces such as xs:string. In this case, whitespaces are always considered separators.

The impact of this statement can be seen when we apply a facet constraining the length of such a datatype:

<xs:simpleType name="myStringList">
  <xs:list itemType="xs:string"/>
</xs:simpleType>

<xs:simpleType name="myRestrictedStringList">
  <xs:restriction base="myStringList">
    <xs:maxLength value="10"/>
  </xs:restriction>
</xs:simpleType>

The datatype myRestrictedStringList is a list of a maximum of 10 items. Since these items are separated by whitespaces, myRestrictedStringList is a list of a maximum of 10 portions of strings that do not contain whitespace (i.e., 10 "words").

This datatype, therefore, validates a value such as:

<myRestrictedStringList>
  This value has less than ten words.
</myRestrictedStringList>

But not this one:

<myRestrictedStringList>
  This value has more than ten words... even if they could be
  spreading less than ten "strings."
</myRestrictedStringList>

Defining lists of lists is forbidden per the W3C XML Schema Recommendation.

Previous | Home | Next
5. Creating Simple Datatypes | Book Index | 5.3. Derivation By Union
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Derivation By Union (XML Schema)

 XML SchemaSearch this book

5.3. Derivation By Union

Derivation by union allows defining datatypes by merging the lexical spaces of several predefined or user datatypes.

As we've seen with the derivation by list, W3C XML Schema has defined two syntaxes, both using a xs:union element, allowing a definition by reference to existing types or by embedding type definition (these two syntaxes can be mixed). The definition of a union datatype by reference to existing types is done through a memberType attribute containing a whitespace-separated list of datatypes:

<xs:simpleType name="integerOrData">
  <xs:union memberTypes="xs:integer xs:date"/>
</xs:simpleType>

The definition of a union datatype can also be done by embedding one or more <xs:simpleType> elements:

<xs:simpleType name="myIntegerUnion">
  <xs:union>
    <xs:simpleType>
      <xs:restriction base="xs:integer"/>
    </xs:simpleType>
    <xs:simpleType>
      <xs:restriction base="xs:NMTOKEN">
        <xs:enumeration value="undefined"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:union>
</xs:simpleType>

Both styles can be mixed and the previous example can be written as:

<xs:simpleType name="myIntegerUnion">
  <xs:union memberTypes="xs:integer">
    <xs:simpleType>
      <xs:restriction base="xs:NMTOKEN">
        <xs:enumeration value="undefined"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:union>
</xs:simpleType>

The resulting datatype is a merge that, as a whole, has lost the semantical meaning--and facets--from the member types. In the earlier example, we couldn't constrain the myIntegerUnion type to be either less than 100 or undefined except by defining a pattern. To do so, we can create a type derived by restriction from a built-in type to be less than 100, and perform the union to allow the value to be "undefined" afterward. The only two facets that can be applied to a union datatype are xs:pattern and xs:enumeration.

Those two facets are the only facets that are common to almost all the datatypes. The only exception is xs:enumeration, which is not allowed for xs:boolean. Defining a "dummy" union over an xs:boolean could be a workaround to define an xs:enumeration facet over this type.

Previous | Home | Next
5.2. Derivation By List | Book Index | 5.4. Some Oddities of Simple Types
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Some Oddities of Simple Types (XML Schema)

 XML SchemaSearch this book

5.4. Some Oddities of Simple Types

While simple types are structurally simple, they still have some complications worth watching for.

5.4.1. Beware of the Order

The order of the different derivation methods (restriction, list, or union) is significant.

We have already seen that derivation by list and union lose the semantic meaning of the types and their facets, which are replaced by a common set of facets with their own meaning (xs:length, xs:maxLength, xs:minLength, xs:enumeration, and xs:whiteSpace for derivation by list, and xs:pattern and xs:enumeration for derivation by union).

This means that all the restrictions on the atomic or member types must be done before the derivation by list or members (as we have seen in the corresponding sections for the facets) and that a new restriction can then be performed using the common set of facets.

The order between derivation by list and derivation by union depends on the result to achieve, as a list of unions is different from a union of lists, as one might expect:

<xs:simpleType name="listOfUnions">
  <xs:list>
    <xs:simpleType>
      <xs:union memberTypes="xs:date xs:integer"/>
    </xs:simpleType>
  </xs:list>
</xs:simpleType>

<xs:simpleType name="UnionOfLists">
  <xs:union>
    <xs:simpleType>
      <xs:list itemType="xs:date"/>
    </xs:simpleType>
    <xs:simpleType>
      <xs:list itemType="xs:integer"/>
    </xs:simpleType>
  </xs:union>
</xs:simpleType>

These two datatypes match the following:

<UnionOfLists>
  2001-01-01 2001-01-02
</UnionOfLists>

<UnionOfLists>
  1 2 3
</UnionOfLists>

<ListOfUnions>
  2001-01-01 2001-01-02
</ListOfUnions>

<ListOfUnions>
  1 2 3
</ListOfUnions>

<ListOfUnions>
  2001-01-01 1 2
</ListOfUnions>

But don't match:

<UnionOfLists>
  2001-01-01 1 2
</UnionOfLists>

This requires all the items of the list to have the same member type.

The order in which a set of derivation by restriction is completed is also significant when the same facets are being redefined, since we have seen that there are some restrictions that depend on the facets being used.

5.4.2. Using or Abusing Lists to Change the Behavior of Length Constraining Facets

We have seen that a derivation by list impacts not only the value space of the item types, but also their meaning. We have also seen that their set of facets is replaced by a generic set.

In the case of length-constraining facets, the length unit is generally a number of characters (in the general case) or bytes (binary types) before a derivation by list and becomes a number of whitespace-separated values for any list datatype.

A restriction by list then allows constraint of the number of whitespace-separated "words" on any datatype. For instance, if we want to define a string datatype of 100 and 200 words, each having a length of less than 15 characters and using only basic Latin characters, we can write:

<xs:simpleType name="word">
  <xs:list>
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:maxLength value="15"/>
        <xs:pattern value="\p{IsBasicLatin}*"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:list>
</xs:simpleType>

<xs:simpleType name="story">
  <xs:restriction base="word">
    <xs:minLength value="100"/>
    <xs:maxLength value="200"/>
  </xs:restriction>
</xs:simpleType>

The first definition defines the constraint on the words and the second adds the constraint on the string itself, which is seen here as a list of words. However, one should note that in this example we have no way to define a constraint on the total number of characters of the "story." The next chapter will demonstrate that these two constraints can be defined using a set of patterns on the string itself.

Previous | Home | Next
5.3. Derivation By Union | Book Index | 5.5. Back to Our Library
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Back to Our Library (XML Schema)

 XML SchemaSearch this book

5.5. Back to Our Library

Let's see how we can improve our schema by adding constraints on our datatypes with what we have learned in this chapter:

<xs:element name="name" type="xs:token"/>

<xs:element name="qualification" type="xs:token"/>

<xs:element name="born" type="xs:date"/>

<xs:element name="dead" type="xs:date"/>

<xs:element name="isbn" type="xs:NMTOKEN"/>

<xs:attribute name="id" type="xs:ID"/>

<xs:attribute name="available" type="xs:boolean"/>

<xs:attribute name="lang" type="xs:language"/>

First, we may want to limit the size of our strings--for instance, if they must be stored into fixed-length columns in an RDBMS. Here, we will consider that the name needs to fit in a string of 32 characters, and the title and qualification need to fit in strings of 255 characters. We create two simple datatypes for this:

<xs:simpleType name="string255">
  <xs:restriction base="xs:token">
    <xs:maxLength value="255"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="string32">
  <xs:restriction base="xs:token">
    <xs:maxLength value="32"/>
  </xs:restriction>
</xs:simpleType>

Then, we may want to add some constraints on the ISBN number. The best we can do without using the patterns (we will see how to do this in the next chapter) is to limit the number of characters to 10 using xs:length. This facet is a number of characters and acts on the value space. This, therefore, does not eliminate instances such as ABCDEFGHIJ, but this is probably the best we can do for the moment:

<xs:simpleType name="isbn">
  <xs:restriction base="xs:NMTOKEN">
    <xs:length value="10"/>
  </xs:restriction>
</xs:simpleType>

We may finally want to limit the languages in which the title may be written. If our library only has titles in English and Spanish, we can add the following restriction:

<xs:simpleType name="supportedLanguages">
  <xs:restriction base="xs:language">
    <xs:enumeration value="en"/>
    <xs:enumeration value="es"/>
  </xs:restriction>
</xs:simpleType>

Our new schema is then:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="string255">
    <xs:restriction base="xs:token">
      <xs:maxLength value="255"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="string32">
    <xs:restriction base="xs:token">
      <xs:maxLength value="32"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="isbn">
    <xs:restriction base="xs:NMTOKEN">
      <xs:length value="10"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="supportedLanguages">
    <xs:restriction base="xs:language">
      <xs:enumeration value="en"/>
      <xs:enumeration value="es"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="name" type="string32"/>
  <xs:element name="qualification" type="string255"/>
  <xs:element name="born" type="xs:date"/>
  <xs:element name="dead" type="xs:date"/>
  <xs:element name="isbn" type="isbn"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="supportedLanguages"/>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="string255">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
      <xs:attribute ref="available"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="qualification"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>

</xs:schema>
Previous | Home | Next
5.4. Some Oddities of Simple Types | Book Index | 6. Using Regular Expressions to Specify Simple Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Using Regular Expressions to Specify Simple Datatypes (XML Schema)

 XML SchemaSearch this book

Chapter 6. Using Regular Expressions to Specify Simple Datatypes

Contents:

The Swiss Army Knife
The Simplest Possible Patterns
Quantifying
More Atoms
Common Patterns
Back to Our Library

Among the different facets available to restrict the lexical space of simple datatypes, the most flexible (and also the one that we will often use as a last resort when all the other facets are unable to express the restriction on a user-defined datatype) is based on regular expressions.

6.1. The Swiss Army Knife

Patterns (and regular expressions in general) are like a Swiss army knife when constraining simple datatypes. They are highly flexible, can compensate for many of the limitations of the other facets, and are often used to define user datatypes on various formats such as ISBN numbers, telephone numbers, or custom date formats. However, like a Swiss army knife, patterns have their own limitations.

Multirange datatypes (such as integers between -1 and 5 or 10 and 15) can be defined as a union of datatypes meeting the different ranges (in this case, we could perform a union between a datatype accepting integers between -1 and 5 and a second datatype accepting integers between 10 and 15); however, after the union, the resulting datatype loses its semantic of integer and cannot be constrained using integer facets any longer. Using patterns to define multirange datatypes is therefore an option: although less readable than using an union, it preserves the semantic of the base type.

Cutting a tree with a Swiss army knife is long, tiring, and dangerous. Writing regular expressions may also become long, tiring, and dangerous when the number of combinations grows. One should try to keep them as simple as possible.

A Swiss army knife cannot change lead into gold, and no facet can change the primary type of a simple datatype. A string datatype restricted to match a custom date format will still retain the properties of a string and will never acquire the facets of a datetime datatype. This means that there is no effective way to express localized date formats.

Previous | Home | Next
5.5. Back to Our Library | Book Index | 6.2. The Simplest Possible Patterns
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Simplest Possible Patterns (XML Schema)

 XML SchemaSearch this book

6.2. The Simplest Possible Patterns

In their simplest form, patterns may be used as enumerations applied to the lexical space rather than on the value space.

If, for instance, we have a byte value that can only take the values "1," "5," or "15," the classical way to define such a datatype is to use the xs:enumeration facet:

<xs:simpleType name="myByte">
  <xs:restriction base="xs:byte">
    <xs:enumeration value="1"/>
    <xs:enumeration value="5"/>
    <xs:enumeration value="15"/>
  </xs:restriction>
</xs:simpleType>

This is the "normal" way of defining this datatype if it matches the lexical space and the value space of an xs:byte. It gives the flexibility to accept the instance documents with values such as "1," "5," and "15," but also "01" or "0000005." One of the particularities of xs:pattern is it must be the only facet constraining the lexical space. If we have an application that is disturbed by leading zeros, we can use patterns instead of enumerations to define our datatype:

<xs:simpleType name="myByte">
  <xs:restriction base="xs:byte">
    <xs:pattern value="1"/>
    <xs:pattern value="5"/>
    <xs:pattern value="15"/>
  </xs:restriction>
</xs:simpleType>

This new datatype is still derived from xs:byte and has the semantic of a byte, but its lexical space is now constrained to accept only "1," "5," and "15," leaving out any variation that has the same value but a different lexical representation.

TIP: This is an important difference from Perl regular expressions, on which W3C XML Schema patterns are built. A Perl expression such as /15/ matches any string containing "15," while the W3C XML Schema pattern matches only the string equal to "15." The Perl expression equivalent to this pattern is thus /^15$/.

This example has been carefully chosen to avoid using any of the meta characters used within patterns, which are: ".", "\", "?", "*", "+", "{", "}", "(", ")", "[", and "]". We will see the meaning of these characters later in this chapter; for the moment, we just need to know that each of these characters needs to be "escaped" by a leading "\" to be used as a literal. For instance, to define a similar datatype for a decimal when lexical space is limited to "1" and "1.5," we write:

<xs:simpleType name="myDecimal">
  <xs:restriction base="xs:decimal">
    <xs:pattern value="1"/>
    <xs:pattern value="1\.5"/>
  </xs:restriction>
</xs:simpleType>

A common source of errors is that "normal" characters should not be escaped: we will see later that a leading "\" changes their meaning (for instance, "\P" matches all the Unicode punctuation characters and not the character "P").

Previous | Home | Next
6. Using Regular Expressions to Specify Simple Datatypes | Book Index | 6.3. Quantifying
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Quantifying (XML Schema)

 XML SchemaSearch this book

6.3. Quantifying

Despite an apparent similarity, the xs:pattern facet interprets its value attribute in a very different way than xs:enumeration does. xs:enumeration reads the value as a lexical representation, and converts it to the corresponding value for its base datatype, while xs:pattern reads the value as a set of conditions to apply on lexical values. When we write:

<xs:pattern value="15"/>

we specify three conditions (first character equals "1," second character equals "5," and the string must finish after this). Each of the matching conditions (such as first character equals "1" and second character equals "5") is called a piece. This is just the simplest form to specify piece.

Each piece in a pattern is composed of an atom identifying a character, or a set of characters, and an optional quantifier. Characters (except special characters that must be escaped) are the simplest form of atoms. In our example, we have omitted the quantifiers. Quantifiers may be defined using two different syntaxes: either a special character (* for 0 or more, + for one or more, and ? for 0 or 1) or a numeric range within curly braces ({n} for exactly n times, {n,m} for between n and m times, or {n,} for n or more times).

Using these quantifiers, we can merge our three patterns into one:

<xs:simpleType name="myByte">
  <xs:restriction base="xs:byte">
    <xs:pattern value="1?5?"/>
  </xs:restriction>
</xs:simpleType>

This new pattern means there must be zero or one character ("1") followed by zero or one character ("5"). This is not exactly the same meaning as our three previous patterns since the empty string "" is now accepted by the pattern. However, since the empty string doesn't belong to the lexical space of our base type (xs:byte), the new datatype has the same lexical space as the previous one.

We could also use quantifiers to limit the number of leading zeros--for instance, the following pattern limits the number of leading zeros to up to 2:

<xs:simpleType name="myByte">
  <xs:restriction base="xs:byte">
    <xs:pattern value="0{0,2}1?5?"/>
  </xs:restriction>
</xs:simpleType>
Previous | Home | Next
6.2. The Simplest Possible Patterns | Book Index | 6.4. More Atoms
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


More Atoms (XML Schema)

 XML SchemaSearch this book

6.4. More Atoms

By this point, we have seen the simplest atoms that can be used in a pattern: "1," "5," and "\." are atoms that exactly match a character. The other atoms that can be used in patterns are special characters, a wildcard that matches any character, or predefined and user-defined character classes.

6.4.1. Special Characters

Table 6-1 shows the list of atoms that match a single character, exactly like the characters we have already seen, but also correspond to characters that must be escaped or (for the first three characters on the list) that are just provided for convenience.

Table 6-1. Special characters

\n | New line (can also be written as "&#x0A;-- since we are in a XML document).
\r | Carriage return (can also be written as "&#x0D; -- ).
\t | Tabulation (can also be written as "&#x09; -- )
\\ | Character "\"
\| | Character "|"
\. | Character "."
\- | Character "-"
\^ | Character "^"
\? | Character "?"
\* | Character "*"
\+ | Character "+"
\{ | Character "{"
\} | Character "}"
\( | Character "("
\) | Character ")"
\[ | Character "["
\] | Character "]"

6.4.2. Wildcard

The character "." has a special meaning: it's a wildcard atom that matches any XML valid character except newlines and carriage returns. As with any atom, "." may be followed by an optional quantifier and ".*" is a common construct to match zero or more occurrences of any character. To illustrate the usage of ".*" (and the fact that xs:pattern is a Swiss army knife), a pattern may be used to define the integers that are multiples of 10:

<xs:simpleType name="multipleOfTen">
  <xs:restriction base="xs:integer">
    <xs:pattern value=".*0"/>
  </xs:restriction>
</xs:simpleType>

6.4.3. Character Classes

W3C XML Schema has adopted the "classical" Perl and Unicode character classes (but not the POSIX-style character classes also available in Perl).

6.4.3.1. Classical Perl character classes

W3C XML Schema supports the classical Perl character classes plus a couple of additions to match XML-specific productions. Each of these classes are designated by a single letter; the classes designated by the upper- and lowercase versions of the same letter are complementary:

\s
Spaces. Matches the XML whitespaces (space #x20, tabulation #x09, line feed #x0A, and carriage return #x0D).

\S
Characters that are not spaces.

\d
Digits ("0" to "9" but also digits in other alphabets).

\D
Characters that are not digits.

\w
Extended "word" characters (any Unicode character not defined as "punctuation", "separator," and "other"). This conforms to the Perl definition, assuming UTF8 support has been switched on.

\W
Nonword characters.

\i
XML 1.0 initial name characters (i.e., all the "letters" plus "-"). This is a W3C XML Schema extension over Perl regular expressions.

\I
Characters that may not be used as a XML initial name character.

\c
XML 1.0 name characters (initial name characters, digits, ".", ":", "-", and the characters defined by Unicode as "combining" or "extender"). This is a W3C XML Schema extension to Perl regular expressions.

\C
Characters that may not be used in a XML 1.0 name.

These character classes may be used with an optional quantifier like any other atom. The last pattern that we saw:

<xs:pattern value=".*0"/>

constrains the lexical space to be a string of characters ending with a zero. Knowing that the base type is a xs:integer, this is good enough for our purposes, but if the base type had been a xs:decimal (or xs:string), we could be more restrictive and write:

<xs:pattern value="-?\d*0"/>

This checks that the characters before the trailing zero are digits with an optional leading - (we will see later on in Section 6.5.2.2, "Fixed format" how to specify an optional leading - or +).

6.4.3.2. Unicode character classes

Patterns support character classes matching both Unicode categories and blocks. Categories and blocks are two complementary classification systems: categories classify the characters by their usage independently to their localization (letters, uppercase, digit, punctuation, etc.), while blocks classify characters by their localization independently of their usage (Latin, Arabic, Hebrew, Tibetan, and even Gothic or musical symbols).

The syntax \p{Name} is similar for blocks and categories; the prefix Is is added to the name of categories to make the distinction. The syntax \P{Name} is also available to select the characters that do not match a block or category. A list of Unicode blocks and categories is given in the specification. Table 6-2 shows the Unicode character classes and Table 6-3 shows the Unicode character blocks.

Table 6-2. Unicode character classes

Unicode Character Class | Includes
C | Other characters (non-letters, non symbols, non-numbers, non-separators)
Cc | Control characters
Cf | Format characters
Cn | Unassigned code points
Co | Private use characters
L | Letters
Ll | Lowercase letters
Lm | Modifier letters
Lo | Other letters
Lt | Titlecase letters
Lu | Uppercase letters
M | All Marks
Mc | Spacing combining marks
Me | Enclosing marks
Mn | Non-spacing marks
N | Numbers
Nd | Decimal digits
Nl | Number letters
No | Other numbers
P | Punctuation
Pc | Connector punctuation
Pd | Dashes
Pe | Closing punctuation
Pf | Final quotes (may behave like Ps or Pe)
Pi | Initial quotes (may behave like Ps or Pe)
Po | Other forms of punctuation
Ps | Opening punctuation
S | Symbols
Sc | Currency symbols
Sk | Modifier symbols
Sm | Mathematical symbols
So | Other symbols
Z | Separators
Zl | Line breaks
Zp | Paragraph breaks
Zs | Spaces

Table 6-3. Unicode character blocks

AlphabeticPresentationForms | Arabic | ArabicPresentationForms-A
ArabicPresentationForms-B | Armenian | Arrows
BasicLatin | Bengali | BlockElements
Bopomofo | BopomofoExtended | BoxDrawing
BraillePatterns | ByzantineMusicalSymbols | Cherokee
CJKCompatibility | CJKCompatibilityForms | CJKCompatibilityIdeographs
CJKCompatibilityIdeographsSupplement | CJKRadicalsSupplement | CJKSymbolsandPunctuation
CJKUnifiedIdeographs | CJKUnifiedIdeographsExtensionA | CJKUnifiedIdeographsExtensionB
CombiningDiacriticalMarks | CombiningHalfMarks | CombiningMarksforSymbols
ControlPictures | CurrencySymbols | Cyrillic
Deseret | Devanagari | Dingbats
EnclosedAlphanumerics | EnclosedCJKLettersandMonths | Ethiopic
GeneralPunctuation | GeometricShapes | Georgian
Gothic | Greek | GreekExtended
Gujarati | Gurmukhi | HalfwidthandFullwidthForms
HangulCompatibilityJamo | HangulJamo | HangulSyllables
Hebrew | HighPrivateUseSurrogates | HighSurrogates
Hiragana | IdeographicDescriptionCharacters | IPAExtensions
Kanbun | KangxiRadicals | Kannada
Katakana | Khmer | Lao
Latin-1Supplement | LatinExtended-A | LatinExtendedAdditional
LatinExtended-B | LetterlikeSymbols | LowSurrogates
Malayalam | MathematicalAlphanumericSymbols | MathematicalOperators
MiscellaneousSymbols | MiscellaneousTechnical | Mongolian
MusicalSymbols | Myanmar | NumberForms
Ogham | OldItalic | OpticalCharacterRecognition
Oriya | PrivateUse | PrivateUse
PrivateUse | Runic | Sinhala
SmallFormVariants | SpacingModifierLetters | Specials
Specials | SuperscriptsandSubscripts | Syriac
Tags | Tamil | Telugu
Thaana | Thai | Tibetan
UnifiedCanadianAboriginalSyllabics | YiRadicals | YiSyllables

We don't yet know how to specify intersections between a block and a category in a single pattern, or how to specify that a datatype must be composed of only basic Latin letters. So, to "cross" these classifications and define the intersection of the block L (all the letters) and the category BasicLatin (ASCII characters below #x7F), we can perform two successive restrictions:

<xs:simpleType name="BasicLatinLetters">
  <xs:restriction>
    <xs:simpleType>
      <xs:restriction base="xs:token">
        <xs:pattern value="\p{IsBasicLatin}*"/>
      </xs:restriction>
    </xs:simpleType>
    <xs:pattern value="\p{L}*"/>
  </xs:restriction>
</xs:simpleType>

6.4.3.3. User-defined character classes

These classes are lists of characters between square brackets that accept - signs to define ranges and a leading ^ to negate the whole list--for instance:

[azertyuiop]

to define the list of letters on the first row of a French keyboard,

[a-z]

to specify all the characters between "a" and "z",

[^a-z]

for all the characters that are not between "a" and "z," but also

[-^\\]

to define the characters "-," "^," and "\," or

[-+]

to specify a decimal sign.

These examples are enough to see that what's between these square brackets follows a specific syntax and semantic. Like the regular expression's main syntax, we have a list of atoms, but instead of matching each atom against a character of the instance string, we define a logical space. Between the atoms and the character class is the set of characters matching any of the atoms found between the brackets.

We see also two special characters that have a different meaning depending on their location! The character -, which is a range delimiter when it is between a and z, is a normal character when it is just after the opening bracket or just before the closing bracket ([+-] and [-+] are, therefore, both legal). On the contrary, ^, which is a negator when it appears at the beginning of a class, loses this special meaning to become a normal character later in the class definition.

We also notice that characters may or must be escaped: "\\" is used to match the character "\". In fact, in a class definition, all the escape sequences that we have seen as atoms can be used. Even though some of the special characters lose their special meaning inside square brackets, they can always be escaped. So, the following:

[-^\\]

can also be written as:

[\-\^\\]

or as:

[\^\\-]

since the location of the characters doesn't matter any longer when they are escaped.

Within square brackets, the character "\" also keeps its meaning of a reference to a Perl or Unicode class. The following:

[\d\p{Lu}]

is a set of decimal digits (Perl class \d) and uppercase letters (Unicode category "Lu").

Mathematicians have found that three basic operations are needed to manipulate sets and that these operations can be chosen from a larger set of operations. In our square brackets, we already saw two of these operations: union (the square bracket is an implicit union of its atoms) and complement (a leading ^ realizes the complement of the set defined in the square bracket). W3C XML Schema extended the syntax of the Perl regular expressions to introduce a third operation: the difference between sets. The syntax follows:

[set1-[set2]]

Its meaning is all the characters in set1 that do not belong to set2, where set1 and set2 can use all the syntactic tricks that we have seen up to now.

This operator can be used to perform intersections of character classes (the intersection between two sets A and B is the difference between A and the complement of B), and we can now define a class for the BasicLatin Letters as:

[\p{IsBasicLatin}-[^\p{L}]]

Or, using the \P construct, which is also a complement, we can define the class as:

[\p{IsBasicLatin}-[\P{L}]]

The corresponding datatype definition would be:

<xs:simpleType name="BasicLatinLetters">
  <xs:restriction base="xs:token">
    <xs:pattern value="[\p{IsBasicLatin}-[\P{L}]]*"/>
  </xs:restriction>
</xs:simpleType>

6.4.4. Oring and Grouping

In our first example pattern, we used three separate patterns to express three possible values. We can condense this definition using the "|" character, which is the "or" operator when used outside square brackets. The simple type definition is then:

<xs:simpleType name="myByte">
  <xs:restriction base="xs:byte">
    <xs:pattern value="1|5|15"/>
  </xs:restriction>
</xs:simpleType>

This syntax is more concise, but whether or not it's more readable is subject to discussion. Also, these "ors" would not be very interesting if it were not possible to use them in conjunction with groups. Groups are complete regular expressions, which are, themselves, considered atoms and can be used with an optional quantifier to form more complete (and complex) regular expressions. Groups are enclosed by brackets ("(" and ")"). To define a comma-separated list of "1," "5," or "15," ignoring whitespaces between values and commas, the following pattern could be used:

<xs:simpleType name="myListOfBytes">
  <xs:restriction base="xs:token">
    <xs:pattern value="(1|5|15)( *, *(1|5|15))*"/>
  </xs:restriction>
</xs:simpleType>

Note how we have relied on the whitespace processing of the base datatype (xs:token collapses the whitespaces). We have not tested leading and trailing whitespaces that are trimmed and we have only tested single occurrences of spaces with the following atom:

run back " * " run back

before and after the comma.

Previous | Home | Next
6.3. Quantifying | Book Index | 6.5. Common Patterns
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Common Patterns (XML Schema)

 XML SchemaSearch this book

6.5. Common Patterns

After this overview of the syntax used by patterns, let's see some common patterns that you may have to use (or adapt) in your schemas or just consider as examples.

6.5.1. String Datatypes

Regular expressions treat information in its textual form. This makes them an excellent mechanism for constraining strings.

6.5.1.1. Unicode blocks

Unicode is a great asset of XML; however, there are few applications able to process and display all the characters of the Unicode set correctly and still fewer users able to read them! If you need to check that your string datatypes belong to one (or more) Unicode blocks, you can derive them from basic types such as:

<xs:simpleType name="BasicLatinToken">
  <xs:restriction base="xs:token">
    <xs:pattern value="\p{IsBasicLatin}*"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="Latin-1Token">
  <xs:restriction base="xs:token">
    <xs:pattern value="[\p{IsBasicLatin}\p{IsLatin-1Supplement}]*"/>
  </xs:restriction>
</xs:simpleType>

Note that such patterns do not impose a character encoding on the document itself and that, for instance, the Latin-1Token datatype could validate instance documents using UTF-8, UTF-16, ISO-8869-1 or other encoding. (This assumes the characters used in this string belong to the two Unicode blocks BasicLatin and Latin-1Supplement.) In other words, working on the lexical space, i.e., after the transformations have been done by the parser, these patterns do not control the physical format of the instance documents.

6.5.1.2. Counting words

We have already seen a trick to count the words using a dummy derivation by list; however, this derivation counts only whitespace-separated "words," ignoring the punctuation that was treated like normal characters. We can limit the number of words using a couple of patterns. To do so, we can define an atom, which is a sequence of one or more "word" characters (\w+) followed by one or more nonword characters (\W+), and control its number of occurrences. If we are not very strict on the punctuation, we also need to allow an arbitrary number of nonword characters at the beginning of our value and to deal with the possibility of a value ending with a word (without further separation). One of the ways to avoid any ambiguity at the end of the string is to dissociate the last occurrence of a word to make the trailing separator optional:

<xs:simpleType name="story100-200words">
  <xs:restriction base="xs:token">
    <xs:pattern value="\W*(\w+\W+){99,199}\w+\W*"/>
  </xs:restriction>
</xs:simpleType>

6.5.1.3. URIs

We have seen that xs:anyURI doesn't care about "absolutizing" relative URIs and it may be wise to impose the usage of absolute URIs, which are easier to process. Furthermore, it can also be interesting for some applications to limit the accepted URI schemes. This can easily be done by a set of patterns such as:

<xs:simpleType name="httpURI">
  <xs:restriction base="xs:anyURI">
    <xs:pattern value="http://.*"/>
  </xs:restriction>
</xs:simpleType>

6.5.2. Numeric and Float Types

While numeric types aren't strictly text, patterns can still be used appropriately to constrain their lexical form.

6.5.2.1. Leading zeros

Getting rid of leading zeros is quite simple but requires some precautions if we want to keep the optional sign and the number "0" itself. This can be done using patterns such as:

<xs:simpleType name="noLeadingZeros">
  <xs:restriction base="xs:integer">
    <xs:pattern value="[+-]?([1-9][0-9]*|0)"/>
  </xs:restriction>
</xs:simpleType>

Note that in this pattern, we chose to redefine all the lexical rules that apply to an integer. This pattern would give the same lexical space applied to a xs:token datatype as on a xs:integer. We could also have relied on the knowledge of the base datatype and written:

  <xs:simpleType name="noLeadingZeros">
    <xs:restriction base="xs:integer">
      <xs:pattern value="[+-]?([^0].*|0)"/>
    </xs:restriction>
  </xs:simpleType>

Relying on the base datatype in this manner can produce simpler patterns, but can also be more difficult to interpret since we would have to combine the lexical rules of the base datatype to the rules expressed by the pattern to understand the result.

6.5.2.2. Fixed format

The maximum number of digits can be fixed using xs:totalDigits and xs:fractionDigits. However, these facets are only maximum numbers and work on the value space. If we want to fix the format of the lexical space to be, for instance, "DDDD.DD", we can write a pattern such as:

<xs:simpleType name="fixedDigits">
  <xs:restriction base="xs:decimal">
    <xs:pattern value="[+-]\.{4}\..{2}"/>
  </xs:restriction>
</xs:simpleType>

6.5.3. Datetimes

Dates and time have complex lexical representations. Patterns can give developers extra control over how they are used.

6.5.3.1. Time zones

The time zone support of W3C XML Schema is quite controversial and needs some additional constraints to avoid comparison problems. These patterns can be kept relatively simple since the syntax of the datetime is already checked by the schema validator and only simple additional checks need to be added. Applications which require that their datetimes specify a time zone may use the following template, which checks that the time part ends with a "Z" or contains a sign:

<xs:simpleType name="dateTimeWithTimezone">
  <xs:restriction base="xs:dateTime">
    <xs:pattern value=".+T.+(Z|[+-].+)"/>
  </xs:restriction>
</xs:simpleType>

Still simpler, applications that want to make sure that none of their datetimes specify a time zone may just check that the time part doesn't contain the characters "+", "-", or "Z":

<xs:simpleType name="dateTimeWithoutTimezone">
  <xs:restriction base="xs:dateTime">
    <xs:pattern value=".+T[^Z+-]+"/>
  </xs:restriction>
</xs:simpleType>

In these two datatypes, we used the separator "T". This is convenient, since no occurrences of the signs can occur after this delimiter except in the time zone definition. This delimiter would be missing if we wanted to constrain dates instead of datetimes, but, in this case, we can detect the time zones on their ":" instead:

<xs:simpleType name="dateWithTimezone">
  <xs:restriction base="xs:date">
    <xs:pattern value=".+[:Z].*"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="dateWithoutTimezone">
  <xs:restriction base="xs:date">
    <xs:pattern value="[^:Z]*"/>
  </xs:restriction>
</xs:simpleType>

Applications may also simply impose a set of time zones to use:

<xs:simpleType name="dateTimeInMyTimezones">
  <xs:restriction base="xs:dateTime">
    <xs:pattern value=".+\+02:00"/>
    <xs:pattern value=".+\+01:00"/>
    <xs:pattern value=".+\+00:00"/>
    <xs:pattern value=".+Z"/>
    <xs:pattern value=".+-04:00"/>
  </xs:restriction>
</xs:simpleType>

We promised earlier to look at xs:duration and see how we can define two datatypes that have a complete sort order. The first datatype will consist of durations expressed only in months and years, and the second will consist of durations expressed only in days, hours, minutes, and seconds. The criteria used for the test can be the presence of a "D" (for day) or a "T" (the time delimiter). If neither of those characters are detected, then the datatype uses only year and month parts. The test for the other type cannot be based on the absence of "Y" and "M", since there is also an "M" in the time part. We can test that, after an optional sign, the first field is either the day part or the "T" delimiter:

<xs:simpleType name="YMduration">
  <xs:restriction base="xs:duration">
    <xs:pattern value="[^TD]+"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="DHMSduration">
  <xs:restriction base="xs:duration">
    <xs:pattern value="-?P((\d+D)|T).*"/>
  </xs:restriction>
</xs:simpleType>
Previous | Home | Next
6.4. More Atoms | Book Index | 6.6. Back to Our Library
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Back to Our Library (XML Schema)

 XML SchemaSearch this book

6.6. Back to Our Library

Let's see where we can use our Swiss army knife in our library. The first datatype, which we promised to improve at the end of the last chapter, is the ISBN number. Without fiddling the details of the constitution of an ISBN number (which can't be fully checked with W3C XML Schema), we can check that the total number of characters actually used is 10 and limit its contents to digits and the letter "X.":

<xs:simpleType name="isbn">
  <xs:restriction base="xs:NMTOKEN">
    <xs:length value="10"/>
    <xs:pattern value="[0-9]{9}[0-9X]"/>
  </xs:restriction>
</xs:simpleType>
TIP: You may wonder why we kept the xs:length, since as far as validation is concerned, it is less constraining than the xs:pattern that we added. This is a question worth asking, but it doesn't have a complete answer yet. However, applications which use the PSVI as a source of meta information may or may not be able to deduce from a pattern that the length of a string has been fixed. It might be good practice to keep redundant facets to provide extra information to these future applications.

W3C XML Schema doesn't allow expression of the fact that the book ID is the same value as the ISBN number with a "b" used as a prefix, but we can still define that it is a "b" with 9 digits and a trailing digit or "X":

<xs:simpleType name="bookID">
  <xs:restriction base="xs:ID">
    <xs:pattern value="b\[0-9]{9}[0-9X]"/>
  </xs:restriction>
</xs:simpleType>

To use this new datatype, we must be aware that we are using a global attribute that was referenced in the element book, but that was also referenced in the elements character and author, which do not have the same format. This is the main limitation in using global elements and attributes: they can be referenced only if they have the same types at all the locations in which they appear. We can work around this problem by creating a local attribute definition for the id attribute of book with the new datatype.

The last things we may want to constrain are the dates for which no time zones are needed and which, in fact, could just be a potential source of issues if we need to compare them:

<xs:simpleType name="date">
  <xs:restriction base="xs:date">
    <xs:pattern value="[^:Z]*"/>
  </xs:restriction>
</xs:simpleType>

Our new schema is then:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="string255">
    <xs:restriction base="xs:token">
      <xs:maxLength value="255"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="string32">
    <xs:restriction base="xs:token">
      <xs:maxLength value="32"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="isbn">
    <xs:restriction base="xs:NMTOKEN">
      <xs:length value="10"/>
      <xs:pattern value="[0-9]{9}[0-9X]"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="bookID">
    <xs:restriction base="xs:ID">
      <xs:pattern value="b\d[0-9]{9}[0-9X]"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="supportedLanguages">
    <xs:restriction base="xs:language">
      <xs:enumeration value="en"/>
      <xs:enumeration value="es"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="date">
    <xs:restriction base="xs:date">
      <xs:pattern value="[^:Z]*"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="name" type="string32"/>
  <xs:element name="qualification" type="string255"/>
  <xs:element name="born" type="date"/>
  <xs:element name="dead" type="date"/>
  <xs:element name="isbn" type="isbn"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="supportedLanguages"/>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="string255">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="id" type="bookID"/>
      <xs:attribute ref="available"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="qualification"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

Previous | Home | Next
6.5. Common Patterns | Book Index | 7. Creating Complex Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Creating Complex Datatypes (XML Schema)

 XML SchemaSearch this book

Chapter 7. Creating Complex Datatypes

Contents:

Simple Versus Complex Types
Examining the Landscape
Simple Content Models
Complex Content Models
Mixed Content Models
Empty Content Models
Back to Our Library
Derivation or Groups

We have seen how to create simple datatypes that can be applied to attributes or simple type elements. It's now time to learn how complex types can be created.

7.1. Simple Versus Complex Types

Before we start diving into complex types, I would like to reiterate the fundamental difference between simple and complex types. The simple datatypes that we saw in the previous chapters describe the content of a text node or an attribute value. They are completely independent of the other nodes and, therefore, independent of the markup. The same datatype system can be used to describe the content of any format, even if it is not XML but an RDBMS (Relational DataBase Management System), CSV (Comma Separated Values), or a fixed-sized text format.

The complex types discussed in this chapter (and, more specifically, the complex content models) are, on the contrary, a description of the markup structure. They use simple datatypes to describe their leaf element nodes and attribute values, but have no other links with simple datatypes. Keep this in mind, especially when we study the derivation methods for complex datatypes. Even though the names (and elements) are sometimes the same as those we've seen for simple datatypes, their meaning, usage, and content models are different. When we discuss the xs:restriction element, for instance, you will see that this element has a different meaning and content model for simple types than it does for complex types. (In fact, this element even has two different content models for complex types, depending on its context.) Among the different content models composing complex types, the simple and mixed content models are special cases in which elements may have text nodes.

There is a kind of no man's land between simple types and complex contents, where the distinction between data and markup (or datatypes and structures) becomes fuzzier for W3C XML Schema. This ambiguity is a frequent source of confusion and complexity for human readers, but also for W3C XML Schema editing software and reference guides.

Previous | Home | Next
6.6. Back to Our Library | Book Index | 7.2. Examining the Landscape
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Examining the Landscape (XML Schema)

 XML SchemaSearch this book

7.2. Examining the Landscape

W3C XML Schema has introduced many different ways of reaching your information modeling goals, and we will try to draw a global picture of the landscape to avoid getting lost! We have to make two key choices: which content model to use, and whether to create new types or to derive them from previously defined types.

7.2.1. Content Models

Let's go back on the definition of the content models and try to illustrate the different cases in Table 7-1. It shows the relationship between content model and child text and element nodes.

Table 7-1. Content models

Content model | Mixed | Complex | Simple | Empty
Child elements | Yes | Yes | No | No
Child text | Yes | No | Yes | No

W3C XML Schema provides two main ways to define complex types: one for complex content models and one for simple content models. It also offers several tricks for piggybacking the definition of mixed and empty contents on these definitions (through a mixed attribute on a complex type definition for mixed contents, and by omitting the option to declare elements or assigning a simple content that imposes a null value for empty contents).

7.2.2. Named Versus Anonymous Types

Like simple datatypes, complex datatypes can be either named (i.e., global) or anonymous (i.e., local). Global definitions must have a name and be a top-level element that is included directly in the xs:schema document element. The global definitions can then be referenced directly in an element definition using the element type attribute; new complex types can be derived from the global definitions. Local complex types are defined directly where they are needed in a schema; they are anonymous (i.e., no name attribute); and they have a local scope.

7.2.3. Creation Versus Derivation

For simple datatypes, there is no choice: you cannot create new primitive datatypes and we must define them by derivation. For complex datatypes, the situation is the opposite: there are no primitive complex types, and complex types must be created before we can do any derivation. When we create our first complex types, we have the choice of defining new content models from scratch or deriving them by extension or restriction from previously defined complex types. This makes it possible for libraries of complex datatypes to be reused within a schema or between different schemas. As far as validation is concerned, these derivations do not change anything compared to simpler definitions: they allow definition of exactly the same models applying to the same instance documents. On the other hand, some applications might be able to draw conclusions from the chain of derivations.

Previous | Home | Next
7. Creating Complex Datatypes | Book Index | 7.3. Simple Content Models
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Simple Content Models (XML Schema)

 XML SchemaSearch this book

7.3. Simple Content Models

We will start by looking at complex types containing simple content because they are closest to simple types, which we've seen recently, and they also provide an easier transition to the more complex world of complex contents. We will not discuss the creation and derivation of simple types, already covered in Chapter 5, "Creating Simple Datatypes", but instead will focus on complex types' simple content models (i.e., elements having only text nodes and attributes) and study how they are created and derived.

7.3.1. Creation of Simple Content Models

Complex types with simple content models are created by adding a list of attributes to a simple type. The operation of adding attributes to a simple type to create a simple content complex type is called an extension of the simple type. The syntax is straightforward and we have already seen examples of such creation in Chapter 4, "Using Predefined Simple Datatypes":

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="string255">
        <xs:attribute ref="lang"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

The only things that need to change here are that the definition of the simple type cannot be directly embedded in the xs:extension(complex content) and that it needs to be referenced through its base attribute.

This same syntax, with the same meaning, can be used to create global complex types, which can be used to define elements:

<xs:complexType name="tokenWithLang">
  <xs:simpleContent>
    <xs:extension base="xs:token">
      <xs:attribute ref="lang"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>

<xs:element name="title" type="tokenWithLang"/>

7.3.2. Derivation from Simple Contents

Complex types provide a number of options for extending simple content models.

7.3.2.1. Derivation by extension

Derivation by extension is reserved for complex types and has no equivalent for simple types. It increases the number of child node elements or attributes allowed or expected in the complex type. For simple content complex types, child elements cannot be added and we stay with an extension that is identical to the method used to create a simple content complex type from a simple type. To add an attribute to the complex type tokenWithLang, just shown in the previous example, we could write:

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="tokenWithLang">
        <xs:attribute name="note" type="xs:token"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

7.3.2.2. Derivation by restriction

The derivation by restriction of simple content complex types is a feature at the border between the two parts of W3C XML Schema (Part 1: Structure and Part 2: Datatypes). It's also very similar to the derivation by restriction of simple datatypes, discussed in Chapter 6, "Using Regular Expressions to Specify Simple Datatypes". The only difference between the derivations by restriction in these two contexts is that the derivation by restriction of a simple content complex type allows not only restriction of the scope of the text node, but also the restriction of the scope of the attribute. This restriction follows the same principle as the restriction of a simple type: any instance structure deemed valid per the restricted type must also be valid per the base type (with the exception already mentioned for the xs:whiteSpace facet).

The syntax used to restrict the text child is the same as the syntax used to derive simple types by restriction. The facets are the same as well. These facets must be followed by the new list of attributes, which may have different types as long as they are derived from the types of the attributes from the base type. Attributes that are not mandatory in the base type can be specified in the new list as "prohibited," and attributes that are not included are considered unchanged. Following are some examples of derivations that start from a simple content datatype equivalent to the content model just shown:

<xs:complexType name="tokenWithLangAndNote">
  <xs:simpleContent>
    <xs:extension base="xs:token">
      <xs:attribute name="lang" type="xs:language"/>
      <xs:attribute name="note" type="xs:token"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>

We can first show how to restrict the length of the text node, as we've done for simple types:

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:restriction base="tokenWithLangAndNote">
        <xs:maxLength value="255"/>
         <xs:attribute name="lang" type="xs:language"/>
        <xs:attributename="note" type="xs:token"/>
      </xs:restriction>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

To remove the note attribute from the element title, we declare note to be prohibited in the list of attributes in the restriction:

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:restriction base="tokenWithLangAndNote">
        <xs:maxLength value="255"/>
        <xs:attribute name="lang" type="xs:language"/>
        <xs:attribute name="note" use="prohibited"/>
      </xs:restriction>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

We can also restrict the datatype by restricting its attributes. For instance, if we want to restrict the number of possible languages, we can do it directly in the definition of the lang attribute in the derived type:

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:restriction base="tokenWithLangAndNote">
        <xs:maxLength value="255"/>
        <xs:attribute name="lang">
          <xs:simpleType>
            <xs:restriction base="xs:language">
              <xs:enumeration value="en"/>
              <xs:enumeration value="es"/>
            </xs:restriction>
          </xs:simpleType>
        </xs:attribute>
      </xs:restriction>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

7.3.2.3. Comparison of these two methods

Despite apparent similarities, derivations by extension and restriction do not have much more in common than deriving new simple content types from base types! Derivation by extension can only add new attributes. It can neither change the datatype of the text node nor the type of an attribute defined in its base type. Derivation by restriction appears to be more flexible and can restrict the datatype of the text node and of the attributes of the base type. It can also remove attributes that are not mandatory in its base type.

Previous | Home | Next
7.2. Examining the Landscape | Book Index | 7.4. Complex Content Models
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Complex Content Models (XML Schema)

 XML SchemaSearch this book

7.4. Complex Content Models

Restricting or extending simple content models is useful, but XML is not very useful without more complex models.

7.4.1. Creation of Complex Content

Complex contents are created by defining the list (and order) of its elements and attributes. We have already seen a couple of examples of complex content models, defined as local complex types in Chapter 1, "Schema Uses and Development" and Chapter 2, "Our First Schema":

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="book" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

These examples show the basic structure of a complex type with complex content definition: the xs:complexType element is holding the definition. Here, this definition is local (xs:complexType is not top-level since it is included under an xs:element element) and, thus, anonymous. Under xs:complexType, we find the sequence of children elements (xs:sequence) and the list of attributes.

7.4.1.1. Compositors and particles

In these examples, the xs:sequence elements have a role as "compositors" and the xs:element elements, which are included in xs:sequence, play a role of "particle." This simple scenario may be extended using other compositors and particles.

W3C XML Schema defines three different compositors: xs:sequence, to define ordered lists of particles; xs:choice, to define a choice of one particle among several; and xs:all, to define nonordered list of particles. The xs:sequence and xs:choice compositors can define their own number of occurrences using minOccurs and maxOccurs attributes and they can be used as particles (some important restrictions apply to xs:all, which cannot be used as a particle, as we will see in the next section).

The particles are xs:element, xs:sequence, xs:choice, plus xs:any and xs:group, which we will see later in the section. The ability to include compositors within compositors is key to defining complex structures, although it is unfortunately subject to the allergy of W3C XML Schema for "nondeterminism."

To give an idea of the kind of structures that can be defined, let's suppose that the names in our library may be expressed in two different ways: either as a name element, as we have shown up to now, or as three different elements to define the first, middle, and last name (the middle name should be optional). Names could then be expressed as one of the three following combinations:

<first-name>
  Charles
</first-name>
      <middle-name>
  M
</middle-name>
       <last-name>
  Schulz
</last-name>

or:

<first-name>
  Peppermint
</first-name>
      <last-name>
  Patty
</last-name>

or:

<name>
  Snoopy
</name>

To describe this, we will replace the reference to the name element with a choice between either a name element or a sequence of first-name, middle-name (optional), and last-name. The definition of author then becomes:

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:choice>
        <xs:element ref="name"/>
        <xs:sequence>
          <xs:element ref="first-name"/>
          <xs:element ref="middle-name" minOccurs="0"/>
          <xs:element ref="last-name"/>
        </xs:sequence>
      </xs:choice>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

The name element also appears in the character element, and a copy/paste can be used to replace it with the xs:choice structure, but we would rather take this opportunity to introduce a new feature that is very handy to manipulating reusable sets of elements.

7.4.1.2. Element and attribute groups

Element and attribute groups are containers in which sets of elements and attributes may be embedded and manipulated as a whole. These simple and flexible structures are very convenient for defining bits of content models that can be reused in multiple locations, such as the xs:choice structure that we created for our name.

The first step is to define the element group. The definition needs to be named and global (i.e., immediately under the xs:schema element) and has the following form:

<xs:group name="name">
  <xs:choice>
    <xs:element ref="name"/>
    <xs:sequence>
      <xs:element ref="first-name"/>
      <xs:element ref="middle-name" minOccurs="0"/>
      <xs:element ref="last-name"/>
    </xs:sequence>
  </xs:choice>
</xs:group>

These groups can then be used by reference as particles within compositors:

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

<xs:element name="character">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="qualification"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

Groups of attributes can be created in the same way using xs:attributeGroup:

<xs:attributeGroup name="bookAttributes">
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
</xs:attributeGroup>

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="isbn"/>
      <xs:element ref="title"/>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attributeGroup ref="bookAttributes"/>
  </xs:complexType>
</xs:element>

7.4.1.3. Unique Particle Attribution Rule

Let's try a new example to illustrate one of the most constraining limitations of W3C XML Schema. We may want to describe all the pages of our books and to have a different description using different elements, such as odd-page and even-page for odd and even pages that require a different pagination. We can try to describe the new content model in the following group:

<xs:group name="pages">
  <xs:sequence>
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:element ref="odd-page"/>
      <xs:element ref="even-page"/>
    </xs:sequence>
    <xs:element ref="odd-page" minOccurs="0"/>
  </xs:sequence>
</xs:group>

This seems like a simple, smart way to describe the sequences of odd and even pages: a sequence of odd and even pages eventually followed by a last odd page. The model covers books with an odd or even number of pages as well as tiny booklets with a single page. Neither XSV not Xerces appear to enjoy it, though:

XSV:

vdv@evlist:~/w3c-xml-schema/user/examples/complex-types$ xsd -n first-ambigous.xsd
first-ambigous.xml
using xsv (default)
<?xml version='1.0'?>
<xsv docElt='{None}library' instanceAssessed='true' instanceErrors='0'
rootType='[Anonymous]' schemaDocs='first-ambigous.xsd' schemaErrors='1'
target='/home/vdv/w3c-xml-schema/user/examples/complex-types/first-ambigous.xml'
validation='strict' version='XSV 1.203.2.20/1.106.2.11 of 2001/11/01 17:07:43'
xmlns='http://www.w3.org/2000/05/xsv'>
<schemaDocAttempt URI='/home/vdv/w3c-xml-schema/user/examples/complex-types/first-
ambigous.xsd'
outcome='success' source='command line'/>
<schemaError char='7' line='65' phase='instance'
resource='file:///home/vdv/w3c-xml-schema/user/examples/complex-types/first-ambigous.
xsd'>
non-deterministic content model for type None: {None}:odd-page/{None}:odd-page
</schemaError>
</xsv>

Xerces:

vdv@evlist:~/w3c-xml-schema/user/examples/complex-types$ xsd -n first-ambigous.xsd
-p xerces-cvs first-ambigous.xml
using xerces-cvs
startDocument
[Error] first-ambigous.xml:2:10: Error: cos-nonambig: (,odd-page)
and (,odd-page) violate the "Unique Particle Attribution" rule.
endDocument

Misled by the apparent flexibility of construction with compositors and particles, we violated an ancient taboo known in SGML as "ambiguous content models," which was imported into XML's DTDs as "nondeterministic content models," and preserved by W3C XML Schema as the "Unique Particle Attribution Rule."

In practice, this rule adds a significant amount of complexity to writing a W3C XML Schema, since it must be matched after all the many features, which allow you to define, redefine, derive, import, reference, and substitute complex types, have been resolved by the schema processor. The Recommendation recognizes that "given the presence of element substitution groups and wildcards, the concise expression of this constraint is difficult." When these features have been resolved, the remaining constraint requires that a schema processor should never have any doubt about which branch it is in while doing the validation of an element and looking only at this element. Applied to the previous example, which was as simple as possible, there is a problem. When a schema processor meets the first odd-page element, it has no way of knowing if the page will be followed by an even-page element without first looking ahead to the next element. This is a violation of the Unique Particle Attribution Rule.

This example, adapted from an example describing a chess board, is one of the famous instances in which the content model cannot be written in a "deterministic" way. This is not always the case, and many nondeterministic constructions describe content models that may be rewritten in a deterministic fashion. We should differentiate those that are fundamentally nondeterministic from those that are only "accidentally" nondeterministic. Let's go back to our example with a "name" sequence that can have two different content models, and imagine that instead of using first-name, we reused the name name. The content model is now either name or a sequence of name, "middle-name," and "last-name":

<xs:group name="name">
  <xs:choice>
    <xs:element ref="name"/>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="middle-name" minOccurs="0"/>
      <xs:element ref="last-name"/>
    </xs:sequence>
  </xs:choice>
</xs:group>

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

Here again, when the processor meets a name element, it has no way of knowing (without looking ahead) if this element matches the first or the second branch of the choice. In this case, though, the content model may be simplified if we note that the name element is common to both branches and that, in fact, we now have a mandatory name element followed by an optional sequence of an optional middle-name and a mandatory last-name. The content model can then be rewritten in a deterministic way as:

<xs:group name="name">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:sequence minOccurs="0">
      <xs:element ref="middle-name" minOccurs="0"/>
      <xs:element ref="last-name"/>
    </xs:sequence>
  </xs:sequence>
</xs:group>

This is a slippery path, though, which frequently depends on slight nuances in the content model and leads to schemas that are very difficult to maintain and may require nonsatisfactory compromises. If the requirement for the content model we have just written is changed and the name element in the second branch is no longer mandatory, then we are in trouble. The new content model is as follows:

<xs:group name="name">
  <xs:choice>
    <xs:element ref="name"/>
    <xs:sequence>
      <xs:element ref="name" minOccurs="0"/>
      <xs:element ref="middle-name" minOccurs="0"/>
      <xs:element ref="last-name"/>
    </xs:sequence>
  </xs:choice>
</xs:group>

But this model is nondeterministic for the same reason that the previous one was, and we need to reevaluate the different possible combinations to find that the new content model can now be expressed as:

<xs:group name="name">
  <xs:choice>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:sequence minOccurs="0">
        <xs:element ref="middle-name" minOccurs="0"/>
        <xs:element ref="last-name"/>
      </xs:sequence>
    </xs:sequence>
    <xs:sequence>
      <xs:element ref="middle-name" minOccurs="0"/>
      <xs:element ref="last-name"/>
    </xs:sequence>
  </xs:choice>
</xs:group>
NOTE: Formal theories and algorithms can rewrite nondeterministic content models in a deterministic way when possible. Hopefully, W3C XML Schema development tools will integrate some of these algorithms to propose an alternative when a schema author creates nondeterministic content models.

Ambiguous content models were already a controversial issue in the 90s among the SGML community, and the restriction has been maintained in XML DTDs under the name "nondeterministic content models" despite the dissent of Tim Bray, Jean Paoli, and Peter Sharpe, three influential members of the XML Special Interest Group who wanted to maintain a compatibility with SGML parsers. The motivation to maintain the restriction in W3C XML Schema is to keep schema processors simple to implement and to allow implementations through finite state machines (FSM). The execution time of these automatons could grow exponentially when the Unique Particle Attribution Rule is violated. This decision has been heavily criticized by experts including Joe English, James Clark, and Murata Makoto, who have proved that other simple algorithms might be used that keep the processing time linear when this rule is not met. This is also one of the main differences between the descriptive powers of schema languages, such as RELAX, TREX, and RELAX NG, which do not impose this rule, and W3C XML Schema.

7.4.1.4. Consistent Declaration Rule

Although not related, strictly speaking, the Unique Particle Attribution Rule and the Consistent Declaration Rule are often associated, since, in practice, when the Constant Declaration Rule is violated, the Unique Particle Attribution Rule is often violated too. This new rule is much easier to explain and understand, since it only states that W3C XML Schema explicitly forbids choices between elements with the same name and different types, such as in the following:

<xs:choice>
  <xs:element name="name" type="xs:string"/>
  <xs:element name="name">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="first-name"/>
        <xs:element ref="middle-name"/>
        <xs:element ref="last-name"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:choice>

We will see a workaround using the xsi:type attribute, which may be used by some applications, in Chapter 11, "Referencing Schemas and Schema Datatypes in XML Documents".

7.4.1.5. Limitations on unordered content models

While useful, unordered content models have their own sets of limitations.

7.4.1.5.1. Limitations of xs:all

Unordered content models (i.e., content models that do not impose any order on the children elements) not only increase the risks of nondeterministic content models, but are also an important complexity factor for schema processors. For the sake of implementation simplicity, the Recommendation has imposed huge limitations on the xs:all element, which makes it hardly usable in practice. xs:all cannot be used as a particle, but as a compositor only; xs:all cannot have a number of occurrences greater than one; the particles included within xs:all must be xs:element; and these particles must not specify numbers of occurrences greater than one.

To illustrate these limitations, let's imagine we have decided to simplify the life of document producers and want to create a vocabulary that doesn't care about the relative order of children elements. With a simple vocabulary such as the one defined in our first schema, this wouldn't add a big burden to the applications handling our vocabulary. When you think about it, there is no special reason to impose the definition the title of a book after its ISBN number or the definition of the list of authors before the list of characters.The first content model that may be affected by this decision is the content model of the book element:

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="isbn"/>
      <xs:element ref="title"/>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
    <xs:attribute ref="available"/>
  </xs:complexType>
</xs:element>

Unfortunately, here the xs:sequence cannot be replaced by xs:all, since two of the children elements (author and character) have a maximum number of occurrences that is "unbounded" and thus higher than one. The second group of candidates includes the content models of author and character, which are relatively similar:

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

<xs:element name="character">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="qualification"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

The good news here is that both author and character match the criteria for xs:all, so we can write:

<xs:element name="author">
  <xs:complexType>
    <xs:all>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:all>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

<xs:element name="character">
  <xs:complexType>
    <xs:all>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="qualification"/>
    </xs:all>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

We can have two elements (author and character) in which the order of children elements is not significant. One may question, though, whether this is very interesting since this independence is not consistent throughout the schema. More importantly, we must note that we have lost a great deal of flexibility and extensibility by using a xs:all compositor. Since the maximum number of occurrences for each child element needs to be one, we can no longer, for instance, change the number of occurrences of the qualification element to accept several qualifications in different languages. And since the particles used in xs:all cannot be compositors or groups, we can't extend the content model to accept both name and the sequence first-name, middle-name, and last-name either.

Since xs:all appears to be pretty ineffective in general, there are a couple of workarounds that may be proposed for people who would like to develop order-independent vocabularies.

7.4.1.5.2. Adapting the structure of your document

The first workaround, which may be used only if you are creating your own vocabulary from scratch, is to adapt the structures of your document to the constraint of xs:all. In practice, this means that each time we have to use a xs:choice, a xs:sequence, or include elements with more than one occurrence, we will add a new element as a container. For instance, we will create containers named authors and characters that will encapsulate the multiple occurrences of author and character. The result is instance documents such as:

<?xml version="1.0"?>
<library>
  <book id="b0836217462" available="true">
    <title lang="en">
      Being a Dog Is a Full-Time Job
    </title>
    <isbn>
      0836217462
    </isbn>
    <authors>
      <author id="CMS">
        <born>
          1922-11-26
        </born>
        <dead>
          2000-02-12
        </dead>
        <name>
          Charles M Schulz
        </name>
      </author>
    </authors>
    <characters>
      <character id="PP">
        <name>
          Peppermint Patty
        </name>
        <qualification>
          bold, brash and tomboyish
        </qualification>
        <born>
          1966-08-22
        </born>
      </character>
      <character id="Snoopy">
        <born>
          1950-10-04
        </born>
        <name>
          Snoopy
        </name>
        <qualification>
          extroverted beagle
        </qualification>
      </character>
      <character id="Schroeder">
        <qualification>
          brought classical music to the Peanuts strip
        </qualification>
        <name>
          Schroeder
        </name>
        <born>
          1951-05-30
        </born>
      </character>
      <character id="Lucy">
        <name>
          Lucy
        </name>
        <born>
          1952-03-03
        </born>
        <qualification>
          bossy, crabby and selfish
        </qualification>
      </character>
    </characters>
  </book>
</library>

This instance document defined by a full schema, which could be:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="name" type="xs:token"/>
  <xs:element name="qualification" type="xs:token"/>
  <xs:element name="born" type="xs:date"/>
  <xs:element name="dead" type="xs:date"/>
  <xs:element name="isbn" type="xs:NMTOKEN"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:token">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="authors">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:all>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:all>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:all>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="authors"/>
        <xs:element ref="characters"/>
      </xs:all>
      <xs:attribute ref="id"/>
      <xs:attribute ref="available"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="characters">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:all>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="qualification"/>
      </xs:all>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

This adaptation of the instance document will be more painful if we want to implement our alternative "name" content model. Since we cannot include a xs:choice in a xs:all compositor, we have to add a first level of container, which is always the same, and a second level of container, which contains only the choice that would lead to instance documents such as:

<?xml version="1.0"?>
<library>
  <book id="b0836217462" available="true">
    <title lang="en">
      Being a Dog Is a Full-Time Job
    </title>
    <isbn>
      0836217462
    </isbn>
    <authors>
      <author id="CMS">
        <born>
          1922-11-26
        </born>
        <dead>
          2000-02-12
        </dead>
        <name>
          <complex-name>
            <last-name>
              Schulz
            </last-name>
            <first-name>
              Charles
            </first-name>
            <middle-name>
              M
            </middle-name>
          </complex-name>
        </name>
      </author>
    </authors>
    <characters>
      <character id="PP">
        <name>
          <complex-name>
            <first-name>
              Peppermint
            </first-name>
            <last-name>
              Patty
            </last-name>
          </complex-name>
        </name>
        <qualification>
          bold, brash and tomboyish
        </qualification>
        <born>
          1966-08-22
        </born>
      </character>
      <character id="Snoopy">
        <born>
          1950-10-04
        </born>
        <name>
          <simple-name>
            Snoopy
          </simple-name>
        </name>
        <qualification>
          extroverted beagle
        </qualification>
      </character>
      <character id="Schroeder">
        <qualification>
          brought classical music to the Peanuts strip
        </qualification>
        <name>
          <simple-name>
            Schroeder
          </simple-name>
        </name>
        <born>
          1951-05-30
        </born>
      </character>
      <character id="Lucy">
        <name>
          <simple-name>
            Lucy
          </simple-name>
        </name>
        <born>
          1952-03-03
        </born>
        <qualification>
          bossy, crabby and selfish
        </qualification>
      </character>
    </characters>
  </book>
</library>

The adaptation of the schema is then straightforward and could be (keeping a flat design):

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="simple-name" type="xs:token"/>
  <xs:element name="first-name" type="xs:token"/>
  <xs:element name="middle-name" type="xs:token"/>
  <xs:element name="last-name" type="xs:token"/>
  <xs:element name="qualification" type="xs:token"/>
  <xs:element name="born" type="xs:date"/>
  <xs:element name="dead" type="xs:date"/>
  <xs:element name="isbn" type="xs:NMTOKEN"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
  <xs:element name="name">
    <xs:complexType>
      <xs:choice>
        <xs:element ref="simple-name"/>
        <xs:element ref="complex-name"/>
      </xs:choice>
    </xs:complexType>
  </xs:element>
  <xs:element name="complex-name">
    <xs:complexType>
      <xs:all>
        <xs:element ref="first-name"/>
        <xs:element ref="middle-name" minOccurs="0"/>
        <xs:element ref="last-name"/>
      </xs:all>
    </xs:complexType>
  </xs:element>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:token">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="authors">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:all>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:all>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:all>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="authors"/>
        <xs:element ref="characters"/>
      </xs:all>
      <xs:attribute ref="id"/>
      <xs:attribute ref="available"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="characters">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:all>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="qualification"/>
      </xs:all>
      <xs:attribute ref="id"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

This process may be generalized and used for purposes other than adapting instance documents to the constraints of xs:all. It is interesting to note that we have "externalized" the complexity, which was previously hidden from the instance document in the schema, to bring the full structure of the content model into the instance document itself. The choices and sequences (an element with multiple occurrences is nothing more than an implicit sequence) are now expressed through containers in the instance documents. Since the structure is more apparent in the instance documents, it can be considered more readable; some people find it a good practice to use such container.

7.4.1.5.3. Using xs:choice instead of xs:all

When it is not possible or not practical to adapt the structure of a document to the limitations of xs:all, another workaround that may be used is to replace xs:all compositors by xs:choice, when possible. This trick is far less generic than the adaptation of structures we just saw, and it may be surprising that two compositors with a very different meaning could be "interchanged." This applies only when a loose control on the number of occurrences can be applied, such as in a container that accepts both author and character elements in any order with any number of occurrences. Such a container can be defined as:

<xs:element name="persons">
  <xs:complexType>
    <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element ref="author"/>
      <xs:element ref="character"/>
    </xs:choice>
  </xs:complexType>
</xs:element>

This definition has the same meaning as the following xs:all definition, which is forbidden:

<xs:element name="persons">
  <xs:complexType>
    <xs:all>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:all>
  </xs:complexType>
</xs:element>

7.4.2. Derivation of Complex Content

Complex contents can also be derived, by extension or by restriction, from complex types. Before we see the details of these mechanisms, note that they are not symmetrical and their semantic is very different. The derivation of a complex content by restriction is a restriction of the set of matching instances. All the instance structures that match the restricted complex type must also match the base complex type. The derivation of a complex content by extension of a complex type is an extension of the content model by addition of new particles. A content that matches the base type does not necessarily match the extended complex type. This also means that there is no "roundtrip": in the general case, neither a restricted complex type nor an extended type can be extended or restricted back into its base type.

7.4.2.1. Derivation by extension

Derivation by extension is similar to the extension of simple content complex types. It is functionally very similar to joining groups of elements and attributes to create a new complex type. The idea behind this feature is to let people add new elements and attributes after those already defined in the base type. This is virtually equivalent to creating a sequence with the current content model followed by the new content model. Let's go back to our library to illustrate this. The content models of our elements author and character are relatively similar: author expects name, born, and dead, while character expects name, born, and qualification. If we want to use a derivation by extension, we can first create a base type that contains the first elements common to the content model of both elements:

<xs:complexType name="basePerson">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="born"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
</xs:complexType>

It is then possible to use derivations by extension to append new elements (dead for author and qualification for character) after those that have already been defined in the base type:

<xs:element name="author">
  <xs:complexType>
    <xs:complexContent>
      <xs:extension base="basePerson">
        <xs:sequence>
          <xs:element ref="dead" minOccurs="0"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

<xs:element name="character">
  <xs:complexType>
    <xs:complexContent>
      <xs:extension base="basePerson">
        <xs:sequence>
          <xs:element ref="qualification"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

Technically, the meaning of this derivation is equivalent to creating a sequence containing the compositor used to define the base type as well as the base type included in the xs:extension element. Thus, the content models of these elements are similar to the content models defined as:

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
      </xs:sequence>
      <xs:sequence>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

<xs:element name="character">
  <xs:complexType>
    <xs:sequence>
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
      </xs:sequence>
      <xs:sequence>
        <xs:element ref="qualification"/>
      </xs:sequence>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

This equivalence clearly shows the feature of this derivation mechanism. As stated in the introduction of complex content derivation mechanisms, this is not an extension of the set of valid instance structures. An element character, with its mandatory qualification, cannot have a valid basePerson content model but rather the merge of two content models. This merge itself is subject to limitations: you cannot choose the point where the new content model is inserted; this addition is always done by appending the new compositor after the one of the base type. In our example, if the common elements name and born were not the first two elements, we couldn't have used a derivation by extension.

Another caveat in derivations by extension is we can't choose the compositor that is used to merge the two content models. This means that when we derive content models using xs:choice as compositors, it is not the scope of the choices that is extended, but rather the choices that are included in a xs:sequence. We could, for instance, extend the content model of the element persons, which we just created and which could be defined as a global complex type:

<xs:complexType name="basePersons">
  <xs:choice minOccurs="0" maxOccurs="unbounded">
    <xs:element ref="author"/>
    <xs:element ref="character"/>
  </xs:choice>
</xs:complexType>

If we add a new element using a derivation by extension:

<xs:complexType name="persons">
  <xs:complexContent>
    <xs:extension base="basePersons">
      <xs:sequence>
        <xs:element name="editor" type="xs:token" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

The result is a content type that is equivalent to:

<xs:complexType name="personsEquivalent">
  <xs:sequence>
    <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element ref="author"/>
      <xs:element ref="character"/>
    </xs:choice>
    <xs:sequence>
      <xs:element name="editor" type="xs:token" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:sequence>
</xs:complexType>

There is no way to obtain an extension of the xs:choice such as:

<xs:complexType name="personsAsWeWouldHaveLiked">
  <xs:choice minOccurs="0" maxOccurs="unbounded">
    <xs:element ref="author"/>
    <xs:element ref="character"/>
    <xs:element name="editor" type="xs:token"/>
  </xs:choice>
</xs:complexType>

The situation with xs:all is even worse: the restrictions on the composition of xs:all still apply. This means you can't add any content to a complex type defined with a xs:all--although you can still add new attributes--and also you can only use a xs:all compositor in a derivation by extension if the base type has an empty content model.

7.4.2.2. Derivation by restriction

Whereas derivation by extension is similar to merging two content models through a xs:sequence compositor, derivation by restriction is a restriction of the number of instance structures matching the complex type. In this respect, it is similar to the derivation by restriction of simple datatypes or simple content complex types (even though we've seen that a facet such as xs:whiteSpace expanded the number of instance documents matching a simple type). Note that this is the only similarity between derivations by restriction of simple and complex datatypes. This is highly confusing, since W3C XML Schema uses the same word and even the same element name in both cases, but these words have a different meaning and the content models of the xs:restriction elements are different.

Unlike simple type derivation, there are no facets to apply to complex types, and the derivation is done by defining the full content model of the derived datatype, which must be a logical restriction of the base type. Any instance structure valid per the derived datatype must also be valid per the base datatype. The W3C XML Schema specification does not define the derivation by restriction in these terms, but defines a formal algorithm to be followed by schema processors, which is roughly equivalent.

The derivation by restriction of a complex type is a declaration of intention that the derived type is a subset of the base type. (Rather than a derivation we've seen for simple types, this declaration is needed for features allowing substitutions and redefinitions of types, which we will see in Chapter 8, "Creating Building Blocks" and Chapter 12, "Creating More Building Blocks Using Object-Oriented Features" and which may provide useful information used by some applications.) When we derive simple types, we can take a base type without having to care about the details of the facets that are already applied, and just add our own set of facets. Here, on the contrary, we need to provide a full definition of a content model, except for attributes that can be declared as "prohibited" to be excluded from the restriction, something we have seen for the restriction of complex types with simple contents.

Moving on, let's try to find a base from which we can derive both the author and character elements by restriction. This time, we can be sure that such a complex type exists since all the complex types can be derived from an abstract xs:anyType, allowing any elements and attributes. In practice, however, we will try to find the most restrictive base type that can accommodate our needs. Since the name and born elements are present in both author and character, with the same number of occurrences, we can keep them as they appear. We then have two elements (dead and qualification, which appear only in one of the two elements author and character). Since both author and character will need to be valid per the base type, we will take both of them in the base type but make them optional by giving them a minOccurs attribute equal to 0. Our base type can then be:

<xs:complexType name="person">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="born"/>
    <xs:element ref="dead" minOccurs="0"/>
    <xs:element ref="qualification" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
</xs:complexType>

The derivations are then done by defining the content model within a xs:restriction element (note that we have not repeated the attribute declarations which are not modified):

<xs:element name="author">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="person">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="dead" minOccurs="0"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

<xs:element name="character">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="person">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="qualification"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

We see here that the syntax of a derivation by restriction is more verbose than the syntax of the straight definition of the content model. The purpose of this derivation is not to build modular schemas, but rather to give applications that use this schema the indication that there is some commonality between the content models, and if they know how to handle the complex type "person," they can handle the elements author and character. We will see W3C XML Schema features that rely on this derivation method in Chapter 8, "Creating Building Blocks" and Chapter 12, "Creating More Building Blocks Using Object-Oriented Features".

Changing the number of occurrences of particles is not the only modification that can be done during a derivation by restriction. Other operations that result in a reduction of the number of valid instance structures are also possible, such as changing a simple type to a more restrictive one or fixing values. The main constraint in this mechanism is that each particle of the derived type must be an explicit derivation of the corresponding particle of the base type. The effect of this statement is to limit the "depth" of the restrictions that can be performed in a single step, and when we need to restrict particles at a deeper level of imbrication, we may have to transform local definitions into global ones. We will see a concrete example in Section 7.5.1, "Creating Mixed Content Models", which are similar in this respect.

7.4.2.3. Asymmetry of these two methods

We now have all the elements we need to look back at the claim about the asymmetry of these derivation methods. This lack of symmetry is not a defect as such, but studying it is a good exercise to understanding the meaning of these two derivation methods. Let's examine the derivation by extension of basePerson into the character element:

<xs:complexType name="basePerson">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="born"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
</xs:complexType>

<xs:element name="character">
  <xs:complexType>
    <xs:complexContent>
      <xs:extension base="basePerson">
        <xs:sequence>
          <xs:element ref="qualification"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

The content model of character contains a mandatory qualification element. Valid characters are not valid per basePerson; thus, there is no hope to be able to derive character back into basePerson by restriction, since all the instance structures that are valid per the derived type must be valid per the base type in a derivation by restriction.

Let's look back at the derivation by restriction of the person base type into a character element:

<xs:complexType name="person">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="born"/>
    <xs:element ref="dead" minOccurs="0"/>
    <xs:element ref="qualification" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
</xs:complexType>

<xs:element name="character">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="person">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="qualification"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

Again, it is not possible to derive the complex type of character into person, since it means changing the number of minimum occurrences of qualification from 1 to 0 and adding an optional dead element between born and qualification. None of these operations are possible during a derivation by extension, which can only append new content after the content of the base type, and can't update an existing particle (to change the number of occurrences) nor insert a new particle between two existing particles.

Previous | Home | Next
7.3. Simple Content Models | Book Index | 7.5. Mixed Content Models
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Mixed Content Models (XML Schema)

 XML SchemaSearch this book

7.5. Mixed Content Models

Although W3CXML Schema permits mixed content models and describes them better than in XML DTDS, W3CXML Schema treats them as an add-on plugged on top of complex content models. The good news is that this allows control of children elements exactly as we've just seen for complex contents. The bad news is that we abandon any control over the child text nodes whose values cannot be constrained at all, and, of course, the descriptions of the child elements are subject to the same limitations as in the case of complex content models. The limitations on unordered content models are probably even more unfriendly for mixed content models, which are more "free style," than the limitation is for complex content models.

7.5.1. Creating Mixed Content Models

This add-on is implemented through a mixed attribute in the xs:complexType(global definition), which is otherwise used exactly as we've seen for complex content models. The effect of this attribute when its value is set to "true" is to allow any text nodes within the content model, before, between, and after the child elements. The location, the whitespace processing, and the datatype of these text nodes cannot be restricted in any way.

Let's go back to the definition of our title element and change it to accept a reduced version of XHTML with the a link and an em element to highlight some parts of its text. The definition, which was previously done by extending a simple type to create a simple content complex type, needs to be re-written as a complex content definition with a mixed attribute set to "true". The full definition, including the definition of the a element, the definition of a markedText complex type and its usage to define the title element, could be:

<xs:element name="a">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="href" type="xs:anyURI"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

<xs:complexType name="markedText" mixed="true">
  <xs:choice minOccurs="0" maxOccurs="unbounded">
    <xs:element name="em" type="xs:token"/>
    <xs:element ref="a"/>
  </xs:choice>
  <xs:attribute ref="lang"/>
</xs:complexType>

<xs:element name="title" type="markedText"/>

This definition matches elements such as:

<title lang="en">
  Being a
  <a href="http://dmoz.org/Shopping/Pets/Dogs/">
    Dog
  </a>
  Is a
  <em>
    Full-Time
  </em>
  Job
</title>

Note that the length of the title can no longer be restricted.

7.5.2. Derivation of Mixed Content Models

Mixed content models are derived exactly like the complex content models on which they have been plugged. The semantic of both methods stays exactly the same.

7.5.2.1. Derivation by extension

Mixed contents complex types can be derived by extension from other complex content complex types and the meaning will be the same. If I want to add a strong element to my markedText mixed content type, I can define the following content model:

<xs:element name="title">
  <xs:complexType mixed="true">
    <xs:complexContent mixed="true">
      <xs:extension base="markedText">
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="strong" type="xs:string"/>
        </xs:choice>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

One must note, though, that this extension is equivalent to:

<xs:complexType name="resultingType" mixed="true">
  <xs:sequence>
    <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element name="em" type="xs:token"/>
      <xs:element ref="a"/>
    </xs:choice>
    <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element name="strong" type="xs:string"/>
    </xs:choice>
  </xs:sequence>
  <xs:attribute ref="lang"/>
</xs:complexType>

This is probably what we would like to see in practice since this content model expects to see all the occurrences of a and em before any instance of strong. We will see later, in Chapter 12, "Creating More Building Blocks Using Object-Oriented Features", that this specific issue can be solved using a feature named "substitution groups" instead of using xs:choice.

7.5.2.2. Derivation by restriction

The derivation of mixed content models by restriction is also done using the method defined for complex content models, with the same constraint that each particle must be an explicit derivation of the corresponding particle of the base type. To illustrate the consequences of this constraint, let's look again at the definition and the use of our markedText:

<xs:element name="a">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="href" type="xs:anyURI"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

<xs:complexType name="markedText" mixed="true">
  <xs:choice minOccurs="0" maxOccurs="unbounded">
    <xs:element name="em" type="xs:token"/>
    <xs:element ref="a"/>
  </xs:choice>
  <xs:attribute ref="lang"/>
</xs:complexType>

<xs:element name="title" type="markedText"/>

If we want to forbid em elements in our title, force the href to be an http absolute URI, and require the lang attribute to be either en or es, we need to do some refactoring to show that the a element included in our title is an explicit derivation of the general definition of a. We also need to use a global complex type definition for a instead of the previous anonymous definition:

<xs:element name="a" type="link"/>

We can now either derive a new global complex type from the new link complex type or embed its derivation in the definition of our title element:

<xs:element name="title">
  <xs:complexType mixed="true">
    <xs:complexContent mixed="true">
      <xs:restriction base="markedText">
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="a">
            <xs:complexType>
              <xs:simpleContent>
               <xs:restriction base="link">
               <xs:attribute name="href">
               <xs:simpleType>
               <xs:restriction base="xs:anyURI">
               <xs:pattern value="http://.*"/>
               </xs:restriction>
               </xs:simpleType>
               </xs:attribute>
               </xs:restriction>
              </xs:simpleContent>
            </xs:complexType>
          </xs:element>
        </xs:choice>
        <xs:attribute name="lang">
          <xs:simpleType>
            <xs:restriction base="xs:language">
              <xs:enumeration value="en"/>
              <xs:enumeration value="es"/>
            </xs:restriction>
          </xs:simpleType>
        </xs:attribute>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

This example is a caricature. In practice it would be more readable to create an intermediate global type definition to avoid embedding several derivations, but it provides an overview of this derivation process.

7.5.2.3. Derivation between complex and mixed content models

Since complex and mixed content models are built using the same mechanism, one may wonder what the possibilities are for deriving complex contents from mixed contents and vice versa. The answer to this question lurks in the semantic of these two derivation methods.

Derivation by extension appends new content after the content of the base type and the structure of the base type is kept unchanged. It is therefore not possible to derive a mixed content model from complex content model. When a content model is mixed, the position of the text nodes cannot be constrained, and this permits text nodes within the base type at any location. For the same reason, it is impossible to extend a mixed content model into a complex content model because the text nodes that are allowed in the base type would become forbidden.

Derivation by restriction defines a subset of the base type. It is forbidden to derive a mixed content model from a complex content model The resulting type would allow text nodes that are forbidden in the base type and would expand rather than restrict the content model. There is one workable possibility, however. The last combination is the only possible one: a mixed content model can be restricted into a complex content model. Forbidding the text nodes of a mixed content model is a valid restriction and can be done by setting the mixed attribute to "false" in the xs:complexType definition. It is even possible to derive a simple content model into a mixed content model since this is, in fact, a restriction removing the sibling elements and keeping the text nodes. This assumes, of course, that the sibling elements are optional; i.e., they have a minOccurs attribute equal to 0.

Previous | Home | Next
7.4. Complex Content Models | Book Index | 7.6. Empty Content Models
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Empty Content Models (XML Schema)

 XML SchemaSearch this book

7.6. Empty Content Models

Empty content models are elements that can only accept attributes. W3C XML Schema does not include any special support for empty content models, which can be considered either complex content models without elements or simple content models with a value restricted to the null string.

7.6.1. Creation of Empty Content Models

W3C XML Schema considers empty content models to be the intersection between complex content models (in the case in which no compositors are specified) and simple content models (in the case in which no text nodes are expected, which W3C XML Schema handles as if an empty text node was found). We will, therefore, be able to choose between the two methods to create an empty content model. Where we extended our title element to become mixed content, we carefully avoided adding empty elements, such as the HTML img or br. Let's see how we could define a br element with its id and class attributes using both methods.

7.6.1.1. As simple content models

This is done by defining a simple type that can only accept the empty string as a value. Strictly speaking, empty content models do not accept any whitespace between their start and end tags. Since we want to control this, we must use a datatype that does not alter the whitespaces, i.e., xs:string. Our empty content model is then derived by extension from this simple type:

<xs:simpleType name="empty">
  <xs:restriction base="xs:string">
    <xs:enumeration value=""/>
  </xs:restriction>
</xs:simpleType>

<xs:element name="br">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="empty">
        <xs:attribute name="id" type="xs:ID"/>
        <xs:attribute name="class" type="xs:NMTOKEN"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

7.6.1.2. As complex content models

The other (more straightforward) way to do this is to create a complex content model without any subelements:

<xs:element name="br">
  <xs:complexType>
    <xs:attribute name="id" type="xs:ID"/>
    <xs:attribute name="class" type="xs:NMTOKEN"/>
  </xs:complexType>
</xs:element>

7.6.2. Derivation of Empty Content Models

Each of the two empty content types keeps the derivation methods of its content model (simple or complex). The main difference between these two methods is essentially a matter of which derivations may be applied on the base type and what effect it will have.

7.6.2.1. Derivation by extension

If we try to remember and compare what we've learned about deriving complex and simple contents by extension, we can see that both allow addition of new attributes to the complex type. However, while we can add new subelements to complex content, we cannot change the type of the text node for a simple content model. Thus, this is the first difference between the two methods: when the empty content model is built on a simple type, it will not be possible to add anything other than attributes, while if it is built on top of a complex type, it will be possible to extend it to accept elements.

7.6.2.2. Derivation by restriction

At first glance, it seems that there are fewer differences here. The restriction methods of both simple and complex contents allow the restriction the scope of the attributes; restricting the content, which is already empty, doesn't seem to be very interesting. It's time, though, to remember what we've learned about a simple type derivation facet, which actually extends the set of valid instance documents! The "empty" simple type that we created to derive our empty simple content model has a base type equal to xs:string. When this simple type is derived through xs:whiteSpace, the result may be an expansion of the sets of valid instance structures. In our case, setting xs:whiteSpace to "collapse" has the effect of accepting any sequence of whitespaces between the start and closing tags. This new type is not "empty," strictly speaking, but may be useful for some (if not for most) applications that are normalizing the whitespaces and do not make any difference between these two cases. Such a derivation can be done on the simple content complex type like this:

<xs:simpleType name="empty">
  <xs:restriction base="xs:string">
    <xs:enumeration value=""/>
  </xs:restriction>
</xs:simpleType>

<xs:complexType name="emptyBr">
  <xs:simpleContent>
    <xs:extension base="empty">
      <xs:attribute name="id" type="xs:ID"/>
      <xs:attribute name="class" type="xs:NMTOKEN"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>

<xs:complexType name="allmostEmptyBr">
  <xs:simpleContent>
    <xs:restriction base="emptyBr">
      <xs:whiteSpace value="collapse"/>
      <xs:attribute name="id" type="xs:ID"/>
      <xs:attribute name="class" type="xs:NMTOKEN"/>
    </xs:restriction>
  </xs:simpleContent>
</xs:complexType>

7.6.3. Simple or Complex Content Models for Empty Content Models?

As we have seen, choosing a simple or complex type doesn't make an awful lot of difference, except for extensibility. If we want to keep the possibility of adding subelements by derivation in the content model, we'd better choose an empty complex content model. However, if we want to be able to accept whitespaces in a derived type, an empty simple content model is a better bet.

Previous | Home | Next
7.5. Mixed Content Models | Book Index | 7.7. Back to Our Library
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Back to Our Library (XML Schema)

 XML SchemaSearch this book

7.7. Back to Our Library

We've covered so much ground in this chapter that it's not obvious which features could be the most beneficial! This choice also depends on external factors such as the level of W3C XML Schema support available from the tools that will be used. For instance, some tools that produce Java classes or binding may take advantage of complex type derivation by restriction. This is the path we will follow for now. We will create a complex type complex content, which will be a superset of the content models of author and character, which we will derive by restriction. First, we can also define an empty content model with an id attribute, which can be derived by extension for all the content models that have an id attribute:

<xs:complexType name="elementWithID">
  <xs:attribute ref="id"/>
</xs:complexType>

Note that we cannot use this type directly to define the book element, since its id attribute is a restriction of xs:ID:

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="isbn"/>
      <xs:element ref="title"/>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="id" type="bookID"/>
    <xs:attribute ref="available"/>
  </xs:complexType>
</xs:element>

To use our elementWithID complex type to define the book element, we need to derive by extension a complex type corresponding to the complex type of book without the restriction on the id attribute. The following code is quite verbose, but it is shown here as an exercise:

<xs:complexType name="bookTmp">
  <xs:complexContent>
    <xs:extension base="elementWithID">
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="author" minOccurs="0"
          maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute ref="available"/>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

<xs:element name="book">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="bookTmp">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="id" type="bookID"/>
        <xs:attribute ref="available"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

A more concise option is to derive by restriction first:

<xs:complexType name="elementWithBookID">
  <xs:complexContent>
    <xs:restriction base="elementWithID">
      <xs:attribute name="id" type="bookID"/>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

<xs:complexType name="book">
  <xs:complexContent>
    <xs:extension base="elementWithBookID">
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="author" minOccurs="0"
          maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute ref="available"/>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

Using the elementWithID to derive by extension a personType, which can then be used to derive the author and character elements by restriction, is straightforward, if not concise. We have already seen this example. The full schema is then:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="string255">
    <xs:restriction base="xs:token">
      <xs:maxLength value="255"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="string32">
    <xs:restriction base="xs:token">
      <xs:maxLength value="32"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="isbn">
    <xs:restriction base="xs:NMTOKEN">
      <xs:totalDigits value="10"/>
      <xs:pattern value="[0-9]{9}[0-9X]"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="bookID">
    <xs:restriction base="xs:ID">
      <xs:pattern value="b[0-9]{9}[0-9X]"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="supportedLanguages">
    <xs:restriction base="xs:language">
      <xs:enumeration value="en"/>
      <xs:enumeration value="es"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="date">
    <xs:restriction base="xs:date">
      <xs:pattern value="[^:Z]*"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="name" type="string32"/>
  <xs:element name="qualification" type="string255"/>
  <xs:element name="born" type="date"/>
  <xs:element name="dead" type="date"/>
  <xs:element name="isbn" type="isbn"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="supportedLanguages"/>
  <xs:complexType name="elementWithID">
    <xs:attribute ref="id"/>
  </xs:complexType>
  <xs:complexType name="bookTmp">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute ref="available"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="personType">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="dead" minOccurs="0"/>
          <xs:element ref="qualification" minOccurs="0"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="string255">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="bookTmp">
          <xs:sequence>
            <xs:element ref="isbn"/>
            <xs:element ref="title"/>
            <xs:element ref="author" minOccurs="0"
              maxOccurs="unbounded"/>
            <xs:element ref="character" minOccurs="0"
              maxOccurs="unbounded"/>
          </xs:sequence>
          <xs:attribute name="id" type="bookID"/>
          <xs:attribute ref="available"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="personType">
          <xs:sequence>
            <xs:element ref="name"/>
            <xs:element ref="born"/>
            <xs:element ref="dead" minOccurs="0"/>
          </xs:sequence>
          <xs:attribute ref="id"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="personType">
          <xs:sequence>
            <xs:element ref="name"/>
            <xs:element ref="born"/>
            <xs:element ref="qualification"/>
          </xs:sequence>
          <xs:attribute ref="id"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
</xs:schema>
Previous | Home | Next
7.6. Empty Content Models | Book Index | 7.8. Derivation or Groups
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Derivation or Groups (XML Schema)

 XML SchemaSearch this book

7.8. Derivation or Groups

Since the derivation methods for complex types do not widen the scope of structures that can be defined by W3C XML Schema and are rather complex, their usage is controversial. Kohsuke Kawaguchi has published a convincing article on XML.com (http://www.xml.com/pub/a/2001/06/06/schemasimple.html) that explains how to avoid using complex type derivations without losing much in modularity.

Previous | Home | Next
7.7. Back to Our Library | Book Index | 8. Creating Building Blocks
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Creating Building Blocks (XML Schema)

 XML SchemaSearch this book

Chapter 8. Creating Building Blocks

Contents:

Schema Inclusion
Schema Inclusion with Redefinition
Other Alternatives
Simplifying the Library

We have already seen most of the basic building blocks: elements, attributes, simple and complex types, element and attribute groups. In this chapter, we will see how we can reuse these building blocks between schemas. In doing so, we will see how schemas can be included and redefined to create schema libraries.

8.1. Schema Inclusion

The first and most straightforward way to build schema libraries is through inclusion, a feature similar to the inclusion in traditional programming languages, such as C. Compared to a "physical" inclusion, such as the result of expanding an external entity reference, or using XInclude (described in Section 8.3.2, "XInclude", later in this chapter), schema inclusion is a "logical" inclusion, which can control the semantic of the inclusion. Schema inclusion may also be seen as a specific form of schema redefinition (seen in the next section). Note that a schema inclusion or redefinition is restricted to the definition of a single namespace (or lack of namespace) and that another mechanism (schema import), which is discussed in Chapter 10, "Controlling Namespaces", must be used to import definitions for other namespaces.

Schema inclusions must be top-level elements, children of the xs:schema element. Their effect is to include all the top-level declarations of the included schema (which doesn't need to be a complete schema). The included top-level elements are then considered top-level elements of the resulting schema. There are no priority or precedence rules and the conflicts that may arise if a local definition is duplicated in both schemas are considered errors. We could use this feature to locate all our simple type definitions in a separate schema. This sub-schema would look like:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="string255">
    <xs:restriction base="xs:token">
      <xs:maxLength value="255"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="string32">
    <xs:restriction base="xs:token">
      <xs:maxLength value="32"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="isbn">
    <xs:restriction base="xs:NMTOKEN">
      <xs:totalDigits value="10"/>
      <xs:pattern value="[0-9]{9}[0-9X]"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="bookID">
    <xs:restriction base="xs:ID">
      <xs:pattern value="b[0-9]{9}[0-9X]"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="supportedLanguages">
    <xs:restriction base="xs:language">
      <xs:enumeration value="en"/>
      <xs:enumeration value="es"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="date">
    <xs:restriction base="xs:date">
      <xs:pattern value="[^:Z]*"/>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

And then include it in our main schema using:

<xs:include schemaLocation="simple-types.xsd"/>

In this example, there is a one-way dependency: the simple types are defined in simple-types.xsd and used in our main schema. The included schema is not very useful by itself. It has no element declaration, and cannot be used as a standalone schema, since it couldn't validate any instance document. However, this is a complete schema that doesn't contain any reference except to predefined simple types. This completeness of the included schema is not a requirement, as we see if we do the same for our complex type definitions:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="elementWithID">
    <xs:attribute ref="id"/>
  </xs:complexType>
  <xs:complexType name="bookTmp">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute ref="available"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="personType">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="dead" minOccurs="0"/>
          <xs:element ref="qualification" minOccurs="0"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:schema>

We can now include both these fragments in our main schema:

<xs:include schemaLocation="simple-types.xsd"/>
<xs:include schemaLocation="complex-types.xsd"/>

We now have an included schema (complex-types.xsd), which references elements (such as author, character, or dead), that are defined in the main schema using datatypes defined in either simple-types.xsd or complex-types.xsd. This combination is perfectly valid for W3C XML Schema since the schema processor collects all the pieces it needs (or at least most of the pieces it needs since wildcards may introduce exceptions, discussed in Chapter 12, "Creating More Building Blocks Using Object-Oriented Features") before checking the references. This flexibility is powerful, handy for building flexible libraries, and eventually error-prone: a complex datatype, such as personType, will have the same children elements but these elements will have a different content model depending on the schema in which complex-types.xsd is included. While using these mechanisms, one must take care to keep track of the interdependencies that will be created!

Previous | Home | Next
7.8. Derivation or Groups | Book Index | 8.2. Schema Inclusion with Redefinition
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schema Inclusion with Redefinition (XML Schema)

 XML SchemaSearch this book

8.2. Schema Inclusion with Redefinition

Inclusion does not provide any means to modify the definitions that are being included, and since they are considered global definitions after the import, they can't be modified afterward either. W3C XML Schema contains a feature that allows derivation of global types and group definitions during an inclusion; it keeps the same name after the derivation. Thus, the semantic of these redefinitions is "take this definition instead of the one you've found in the included schema, but make sure that it's a valid derivation so that applications are not too surprised about the change." These are implemented using the xs:redefine element with a schemaLocation attribute (like xs:include). Its children are component definitions that replace the definition found in the included schema. The definitions that are not included in the xs:redefine element are kept unchanged, which means that a xs:redefine with no child element is strictly equivalent to xs:include.

It is noteworthy that the effect of the redefinition is global to the resulting schema. References made to redefined components are all impacted by the modifications made to these components, even if they are made within the redefined schema.

8.2.1. Redefining of Simple and Complex Types

Simple and complex types are redefined by deriving them (by restriction for simple types and by restriction or extension for complex types) inside the xs:redefine element. We can apply this to our last example. The definition of bookTmp is currently used to describe the book element though derivation:

<xs:element name="book">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="bookTmp">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="id" type="bookID"/>
        <xs:attribute ref="available"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

Instead of doing this, we can also redefine the definition of the book complex type. The new schema to define the complex types is then:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="elementWithID">
    <xs:attribute ref="id"/>
  </xs:complexType>
  <xs:complexType name="book">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute ref="available"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="personType">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="dead" minOccurs="0"/>
          <xs:element ref="qualification" minOccurs="0"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:schema>

The redefinition--note how a book complex type is redefined using a base type with the same name, which would be forbidden anywhere else--and usage of the book element looks like:

<xs:redefine schemaLocation="complex-types2.xsd">
  <xs:complexType name="book">
    <xs:complexContent>
      <xs:restriction base="book">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="id" type="bookID"/>
        <xs:attribute ref="available"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:redefine>

<xs:element name="book" type="book"/>

8.2.2. Redefinition of Element and Attribute Groups

The redefinition of complex and simple types seems quite natural and should not be much of a surprise, since it builds on things we've discussed in detail in previous chapters. The new part of xs:redefine is that element and attribute groups--which cannot be derived--can also be redefined. Redefinition of element and attribute groups is done without any special schema element: a group redefinition that contains a reference to itself is considered an extension; otherwise, it's considered a restriction. These two methods have their own rules and semantics, which are similar but not identical to the rules and semantics of the derivation of complex types. These deserve a specific description. As we will see, the general principles are the same, and the asymmetry between extension and restriction is preserved for group redefinitions.

8.2.2.1. Extension

Group extensions are done by referencing the group somewhere in its redefinition. The semantic is, therefore, similar to the semantic of the derivation by extension of complex content complex types (some new content is added to the base type) with more flexibility. The location where the content of the base type is added may be chosen during the extension of a complex content complex type, and the new content is always appended after the content of the base type. If we have, for instance, a group definition such as:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:group name="character">
    <xs:sequence>
      <xs:element ref="born"/>
      <xs:element ref="qualification"/>
    </xs:sequence>
  </xs:group>
</xs:schema>

We can redefine it to add the name element, which is missing at the beginning of the content:

<xs:redefine schemaLocation="character-group.xsd">
  <xs:group name="character">
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:group ref="character"/>
    </xs:sequence>
  </xs:group>
</xs:redefine>

<xs:element name="character">
  <xs:complexType>
    <xs:group ref="character"/>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

We see that we have been able to choose the insertion point of the content of the base group, which is after the name element. The name element has been added; this is an enhancement over complex content complex type derivation.

This method of extending element or attribute groups is clearly underspecified in the Recommendation and should be used in its simplest form with caution to avoid interoperability issues. The Recommendation specifies that the minOccurs and maxOccurs attributes of the reference need to be exactly one, which shows a wish to include the content of the base group during an extension exactly one time. However, the wording of the Recommendation does not forbid inclusion of this reference in a branch that has a different number of occurrences, such as:

<xs:redefine schemaLocation="bar.xsd">
  <xs:group name="foo">
    <xs:sequence>
      <xs:sequence minOccurs="0">
        <xs:group ref="foo"/>
      </xs:sequence>
      <xs:element ref="bar"/>
    </xs:sequence>
  </xs:group>
</xs:redefine>

This is functionally equivalent to having minOccurs equal to zero on the group reference and allows content models without any occurrences of the base group. Since this is contrary to the philosophy behind derivations by extension, these kind of structures shouldn't be used. Similarly, the Recommendation does not forbid the use of another compositor other than a xs:sequence to redefine a group. However, since using xs:choice instead of xs:sequence leads to redefined groups in which the content of the base can be omitted, this is certainly something to avoid.

The references used to extend groups during a redefinition must be done at the top level of the group definition. The last thing to note about element group extensions is that even though its syntax uses a group reference to the group being defined, self references cannot be used in regular global group definitions for defining recursive content models. These need to be done at a lower level, such as:

<xs:group name="group">
  <!-- This group definition is *not* valid -->
  <xs:sequence>
    <xs:element name="foo">
      <xs:complexType>
        <xs:group ref="group" minOccurs="0"/>
      </xs:complexType>
    </xs:element>
    <xs:element name="bar" type="xs:token"/>
  </xs:sequence>
</xs:group>

8.2.2.2. Restriction

The redefinition of attribute and element groups by restriction is similar, in principle, to a derivation of a complex content complex type by restriction. A new definition of the group is given; this new definition must match the same criteria as that of a complex content complex type restriction, and must be a valid restriction of the base group. A content that matches the redefined group must always match the base group and the elements used by the new definition must be explicit restrictions of the elements used in the base group. If we have a group definition available, such as:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:group name="author">
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
      <xs:element name="nationality" type="xs:NMTOKEN" minOccurs="0"/>
    </xs:sequence>
  </xs:group>
</xs:schema>

We can redefine it to remove the element nationality, which is optional:

<xs:redefine schemaLocation="author.xsd">
  <xs:group name="author">
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
  </xs:group>
</xs:redefine>

<xs:element name="author">
  <xs:complexType>
    <xs:group ref="author"/>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

Before we leave this subject, we need to note that the rules for restricting attribute groups are different than the rules for restricting complex types. The list of attributes must include all the attributes that are kept. (This is unlike complex type restrictions in which attributes that are not mentioned are considered unchanged.) If we have an attribute group such as:

<xs:attributeGroup name="commonAttributes">
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
</xs:attributeGroup>

If we want to restrict it to remove the available attribute through a redefinition, we then must repeat the definitions of the two other attributes:

<xs:redefine schemaLocation="attributes.xsd">
  <xs:attributeGroup name="commonAttributes">
    <xs:attribute name="id" type="xs:ID"/>
    <xs:attribute name="lang" type="xs:language"/>
  </xs:attributeGroup>
</xs:redefine>

Previous | Home | Next
8. Creating Building Blocks | Book Index | 8.3. Other Alternatives
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Other Alternatives (XML Schema)

 XML SchemaSearch this book

8.3. Other Alternatives

The xs:include and xs:redefine elements are features which provide a safe way to include "pieces" of schemas. Their processing model is designed to provide a result that is a coherent schema. However, the price for this safety is a certain rigidity: only full schemas documents can be included and the insertion can only occur at a global level in a schema. (It is not possible, for instance, to pick a couple of definitions in a schema without including the others.) These rules mean that these features cannot be used to include local elements, such as annotations or commonly used facets. Let's imagine, for instance, that we want to require that all our dates and related datatypes specify a time zone, and that we have worked very hard to define a generic pattern to use to enforce this constraint. This can be something such as:

<xs:pattern value=".+(Z|[+-].{5})"/>

We could derive user-defined datatypes for each of the eight primitive times--which can have a time zone using this pattern--and ask to our schema designers to use only these datatypes in their schemas. However, we may prefer to give them this pattern as a tool, which they can use in their schemas by reference instead of copying it (we may want to keep the possibility of modifying the pattern without having to update all the schemas). In this case, xs:include and xs:redefine cannot be used, and we must consider using one of the generic XML inclusion methods, which are external parsed entities and XInclude.

8.3.1. External Parsed Entities

External parsed entities are one of the SGML features inherited by XML though its DTD. As the name indicates, these are entities (i.e., something you need to declare in the DTD and can reference later on in your document) that are external (i.e., their replacement value is read from an external file when they are referenced) and parsed (i.e., their content is parsed and merged into the infoset of the including document).

To use external parsed entities, we will create an XML document with the pattern we want to include:

<?xml version="1.0"?>
<xs:pattern value=".+(Z|[+-].{5})"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"/>

Note that including a namespace declaration in this file (which will be used as an external parsed entity) is not strictly mandatory, if we are sure that this entity will always be used in documents in which the namespace has already been defined with the same prefix. However, even in this case, the redefinition of the namespace is allowed though it will have no effect. Defining it will guarantee that if another prefix has been used in the included document, the snippet that we include will still be understood as belonging to the W3C XML Schema namespace. To use this entity, it must be declared in the internal or external DTD of our schema and referred to in our derivations:

<?xml version="1.0"?>
<!DOCTYPE xs:schema[
<!ENTITY TZ-pattern SYSTEM "pattern.ent">
]>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:simpleType name="myDate">
                <xs:restriction base="xs:date">
                                &TZ-pattern;
                </xs:restriction>
        </xs:simpleType>
        <xs:element name="myDate" type="myDate"/>
</xs:schema>

The interesting thing here is we have a finer granularity than we could have achieved using the W3C XML Schema inclusion mechanisms, which manipulate only global components. The price for using a general purpose inclusion mechanism such as external parsed entities (or XInclude, discussed in the next section) is that this mechanism doesn't implement any of the semantics of W3C XML Schema and doesn't allow any redefinition. Beyond this simple example, other DTD features, such as internal parsed entities, and even parameter entities can be used in conjunction with W3C XML Schema to produce innovative combinations!

8.3.2. XInclude

Currently a W3C Candidate Recommendation, XInclude is a XML application that relies on XPointer. XInclude will eventually replace external parsed entities and can be used in a similar way; the main difference is that a XInclude reference doesn't need to be declared prior to its use and can include a fragment of a XML document. The same example can then be implemented using XInclude, taking advantage of its feature to fetch our pattern by its id even if it is defined within a more complete schema such as:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="date">
    <xs:restriction base="xs:date">
      <xs:pattern value=".+(Z|[+-].{5})" id="TZ-pattern"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="myDate" type="myDate"/>
</xs:schema>

Now that the id attribute of the xs:pattern element is defined, we can use the XPointer "bare names" syntax, which allows us to use the value of an id as a fragment identifier. In our case, the XPointer reference to our xs:pattern definition is thus pattern.xsd#TZ-pattern. We can write:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xi="http://www.w3.org/2001/XInclude">
  <xs:simpleType name="myDateTime">
    <xs:restriction base="xs:dateTime">
      <xi:include href="pattern.xsd#TZ-pattern" parse="xml"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="myDate" type="myDateTime"/>
</xs:schema>

Note that XInclude is still a work in progress, and that this syntax may change before XInclude reaches the status of W3C Recommendation. Also note that a parser implementing XInclude should be used to read such a schema.

Previous | Home | Next
8.2. Schema Inclusion with Redefinition | Book Index | 8.4. Simplifying the Library
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Simplifying the Library (XML Schema)

 XML SchemaSearch this book

8.4. Simplifying the Library

Our library, with its single instance document, doesn't really deserve redefinition, so we will just use inclusion to isolate simple and complex type definitions in their own schemas to keep these schemas shorter. To do this, we can create a partial schema to define all our simple types:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="string255">
    <xs:restriction base="xs:token">
      <xs:maxLength value="255"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="string32">
    <xs:restriction base="xs:token">
      <xs:maxLength value="32"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="isbn">
    <xs:restriction base="xs:unsignedLong">
      <xs:totalDigits value="10"/>
      <xs:pattern value="\d{10}"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="bookID">
    <xs:restriction base="xs:ID">
      <xs:pattern value="b\d{10}"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="supportedLanguages">
    <xs:restriction base="xs:language">
      <xs:enumeration value="en"/>
      <xs:enumeration value="es"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:simpleType name="date">
    <xs:restriction base="xs:date">
      <xs:pattern value="[^:Z]*"/>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

We can then create a second schema containing all the complex type definitions (note that this second schema doesn't need to include the simple type definitions that will be included directly into the main schema):

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="elementWithID">
    <xs:attribute ref="id"/>
  </xs:complexType>
  <xs:complexType name="bookTmp">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute ref="available"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="personType">
    <xs:complexContent>
      <xs:extension base="elementWithID">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="dead" minOccurs="0"/>
          <xs:element ref="qualification" minOccurs="0"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:schema>

We can leave all the other definitions in our main schema, which includes (using xs:include) the schemas containing the simple and complex type definitions:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:include schemaLocation="simpleTypes.xsd"/>
  <xs:include schemaLocation="complexTypes.xsd"/>
  <xs:element name="name" type="string32"/>
  <xs:element name="qualification" type="string255"/>
  <xs:element name="born" type="date"/>
  <xs:element name="dead" type="date"/>
  <xs:element name="isbn" type="isbn"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="supportedLanguages"/>
  <xs:element name="title">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="string255">
          <xs:attribute ref="lang"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="bookTmp">
          <xs:sequence>
            <xs:element ref="isbn"/>
            <xs:element ref="title"/>
            <xs:element ref="author" minOccurs="0"
              maxOccurs="unbounded"/>
            <xs:element ref="character" minOccurs="0"
              maxOccurs="unbounded"/>
          </xs:sequence>
          <xs:attribute name="id" type="bookID"/>
          <xs:attribute ref="available"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="author">
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="personType">
          <xs:sequence>
            <xs:element ref="name"/>
            <xs:element ref="born"/>
            <xs:element ref="dead" minOccurs="0"/>
          </xs:sequence>
          <xs:attribute ref="id"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="character">
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="personType">
          <xs:sequence>
            <xs:element ref="name"/>
            <xs:element ref="born"/>
            <xs:element ref="qualification"/>
          </xs:sequence>
          <xs:attribute ref="id"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
</xs:schema>

Previous | Home | Next
8.3. Other Alternatives | Book Index | 9. Defining Uniqueness, Keys, and Key References
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Defining Uniqueness, Keys, and Key References (XML Schema)

 XML SchemaSearch this book

Chapter 9. Defining Uniqueness, Keys, and Key References

Contents:

xs:ID and xs:IDREF
XPath-Based Identity Checks
ID/IDREF Versus xs:key/xs:keyref
Using and As Co-occurrence Constraints

Like any storage system, a XML document needs to provide ways to identify and reference pieces of the information it contains. In this chapter, we will present and compare the two features that allow XML to do so with W3C XML Schema. One directly emulates the ID, IDREF, and IDREFs attribute types from the XML DTDs, while the other was introduced to provide more flexibility through the use of XPath expressions.

9.1. xs:ID and xs:IDREF

The first way to describe identifiers and references with W3C XML Schema is inherited from XML's DTDs. We already discussed this in Chapter 5, "Creating Simple Datatypes": the xs:ID, xs:IDREF, and xs:IDREFS datatypes introduced in W3C XML Schema emulate the behavior of the XML DTD's ID, IDREF, and IDREFS attribute types.

Unlike their DTD counterparts, these simple types can be used to describe both elements and attributes, but inherit the other restrictions from the DTDs: their lexical space is the same as the unqualified XML name (known as the xs:NCName datatype), and they are global to a document, meaning that you won't be allowed to use the same ID value to identify, for instance, both an author and a character within the same document.

The restriction on the lexical space can often prevent you from using an existing node as an identifier. For instance, in our library, we will not be able to use an ISBN number as an ID since xs:NCName cannot start with a number and whitespace is prohibited. We will therefore need to create completely arbitrary IDs and derive their values from existing nodes. The ISBN number "0836217462" can, for instance, be used to build the ID isbn-0836217462, and the name "Charles M. Schulz" can become the ID au-Charles-M.-Schulz. Adding a prefix (ISBN, AU, etc.) is also a way to avoid a collision between IDs used for different element types.

These IDs can be used to define either attributes or elements; however, the Recommendation reminds us that if we want to maintain compatibility with XML 1.0 IDs and IDREFs, they should be used only for attributes. In both cases (elements or attributes), the contribution to the PSVI is done in a similar fashion through a "ID/IDREF table"; except for maintaining compatibility with the feature as it was previously defined in XML 1.0, there is no reason to avoid using ID, IDREF, and IDREFS to define elements.

For example, to show how these styles can be combined, a book element of our library can be written as:

<book identifier="isbn-0836217462">
  <isbn>
    0836217462
  </isbn>
  <title>
    Being a Dog Is a Full-Time Job
  </title>
  <author-ref ref="au-Charles_M._Schulz"/>
  <character-refs>
    ch-Peppermint_Patty ch-Snoopy ch-Schroeder ch-Lucy
  </character-refs>
</book>

The book element is identified by an identifier (ID) attribute, and references its author though the ref (IDREF) attribute of an author-ref element as well as a whitespace-separated list of characters through a character-refs (IDREFS) element. The piece of schema for this element can be:

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="isbn" type="xs:NMTOKEN"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="author-ref">
        <xs:complexType>
          <xs:attribute name="ref" type="xs:IDREF" use="required"/>
        </xs:complexType>
      </xs:element>
      <xs:element name="character-refs" type="xs:IDREFS"/>
    </xs:sequence>
    <xs:attribute name="identifier" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:element>
Previous | Home | Next
8.4. Simplifying the Library | Book Index | 9.2. XPath-Based Identity Checks
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath-Based Identity Checks (XML Schema)

 XML SchemaSearch this book

9.2. XPath-Based Identity Checks

The IDs and IDREFs are stored in the PSVI in a table (called the "ID/IDREF table") and can eventually be used by the applications to locate the corresponding nodes. We can expect XPath applications (including XPointer) to provide shortcuts and fast access to the nodes identified by W3C XML Schema, as is already the case with the DTD IDs.

Simple and easy to use within their domain, IDs and IDREFs keep the limitations of their DTDs ancestors. W3C XML Schema provides a more flexible feature for defining identity constraints without limitation on its lexical space and allowing local keys and references, as well as multinodes keys.

Another important difference is that the ID/IDREF checks are done on datatypes based on xs:NMTOKEN datatypes, while the checks that we will see hereafter can be performed on other datatypes, and the comparisons will be done on the actual value spaces rather than on their string representations from the lexical space. These checks are based on a set of XPath expressions and are defined through three different (but similar) constructs to test the uniqueness of a value, define a key, and define a key reference.

9.2.1. Uniqueness

The first of these constructs defines a simple check for uniqueness. We will spend some time explaining this in detail, since the two other constructs are based on the same pattern.

The definition of these constraints is done using two consecutive relative XPath expressions evaluated against the position of the element under which they are defined. We need a clear picture of the structure of the instance documents to define them. The starting point is the location of the element under which the check is defined. This location determines the scope of the test and must be carefully chosen, since it is the basis from which all the checks will be performed for this constraint.

For instance, in our library, we can choose to define a check for the uniqueness of the ISBN number of our books under the library element, since we need to check it within the scope of the whole library. However, within a book, we may also test that the reference to a character is unique within the scope of this book. We can define this second check inside the book element.

Once we have chosen the location of the test, we can start writing it at the end of the definition of the element:

<xs:element name="book" maxOccurs="unbounded">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:unique name="book">
    .../...
  </xs:unique>
</xs:element>

The name attribute used here will be useful if we want to refer to this constraint through a keyref.

Now that we have defined the name and the root of the test, we will define the selector that is the relative path of the node being identified. In our example, the relative path to access a book element from library is book, so we write:

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:unique name="book">
    <xs:selector xpath="book"/>
    .../...
  </xs:unique>
</xs:element>

We have expressed the fact that a book must be unique within a library. To complete the description of this check, we need to define how a book is identified through field elements.

In our case, the identifier is the isbn subelement, and the complete definition is:

<xs:element name="book" maxOccurs="unbounded">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:unique name="book">
    <xs:selector xpath="book"/>
    <xs:field xpath="isbn"/>
  </xs:unique>
</xs:element>

Translated into plain English, this definition can be read as "for each library, each book identified by its ISBN should be unique."

TIP: A unique condition doesn't impose that the node used as an identifier (the field) is required. Selectors whose field is not available are just ignored. To define the same check when the field is required, a "key" should be defined instead of "unique."

9.2.2. Composite Fields

If the names of our authors were split in our library into first, middle, and last names, we may find it convenient to define a composite field to identify our authors. W3C XML Schema provides this feature by allowing definition of several fields within a single constraint--for instance:

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:unique name="author">
    <xs:selector xpath="author"/>
    <xs:field xpath="first-name"/>
    <xs:field xpath="middle-name"/>
    <xs:field xpath="last-name"/>
  </xs:unique>
</xs:element>

The check is then done on the triple that is composed of the values of the three fields (first-name, middle-name, last-name) that need to be unique as a combination.

9.2.3. Keys

A key is a unique constraint with the additional restriction that all the nodes corresponding to all the fields are required.

The syntax for defining a key is the same as the syntax for defining a unique condition, except the unique element is replaced by a key element:

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:key name="book">
    <xs:selector xpath="book"/>
    <xs:field xpath="isbn"/>
  </xs:key>
</xs:element>
TIP: There is clearly an overlap between the additional existence check done by a key constraint and the other ways to control the number of occurrences of an element or attribute. In our example, if the minimum number of occurrences for the author's name is set to one, using xs:unique or xs:key is equivalent, except when the author's name can have a "nil" value. (We will discuss the "nil" value in Chapter 11, "Referencing Schemas and Schema Datatypes in XML Documents".)

9.2.4. Key References

Despite its name, xs:keyref can be used not only to define a reference to xs:key, but also to xs:unique.

The usage of xs:keyref is straightforward and similar to the usage of xs:key or xs:unique, with an important point worth mentioning: the refer attribute of xs:keyref should refer to a xs:key or xs:unique element defined under the same element or under one of their ancestors.

TIP: The reason for this rule is that the "identity-constraint tables" where the keys and references are stored are local to an element and its ancestors.

The definitions of matching xs:unique or xs:key and xs:keyref need to be done within the same element, or else one of its ancestors has an impact on the choice of this location. If, for instance, our books and authors are kept in separate sections of our document:

<library>
  <books>
    <book>
      .../...
      <author-ref ref="Charles M. Schulz"/>
      .../...
    </book>
    .../...
  </books>
  <authors>
    <author>
      <name>
        Charles M. Schulz
      </name>
      .../...
    </author>
    .../...
  </authors>
</library>

It's good practice to define a modular schema by locating the constraints as near as possible to the elements they control. A natural fit is to locate a key in the authors element and the matching keyref in the books element. However, since a xs:keyref needs to be in the same element as the matching xs:key or one of its ancestors, and books isn't an ancestor of authors, the xs:keyref definition can only be done in the library element. (The xs:key can be defined either in the library or in the authors element.)

In the previous example, locating the xs:key definition within library or authors was only a matter of style, since the authors are unique both within a library and within the authors elements. However, W3C XML Schema allows for situations in which this isn't the case and in which a key is unique within the scope of a subelement without being unique within the whole document.

Let's modify the previous example to define several categories of authors:

<library>
  <books>
    <book>
      .../...
      <author-ref ref="Charles M. Schulz"/>
      .../...
    </book>
    .../...
  </books>
  <authors>
    <category id="comics">
      <author>
        <name>
          Charles M. Schulz
        </name>
        .../...
      </author>
      .../...
    </category>
    <category id="novels">
      .../...
    </category>
    .../...
  </authors>
</library>

Defining a xs:key (or xs:unique) within library or authors specifies a uniqueness within the scope of the entire library. Defining a list of authors within category specifies a uniqueness within this category only, and allows authors with the same name to be defined under several categories.

It is perfectly valid, per W3C XML Schema, to define a xs:key under category and a matching xs:keyref under library (since library is an ancestor of category). By doing so, a new constraint is added to authors' names. When an author is referenced within a book, her name has to be unique within the scope of the xs:keyref. Applied to our instance document, this means that if "Charles M. Schulz" was not referenced in one of the books, he can be defined in several categories; since he is referenced in one book, his name must be defined once only.

TIP: While this behavior is described in the Recommendation, the results may be surprising for schema designers. It is probably good practice to keep the definitions of the xs:key (or xs:unique) and their matching xs:keyref in the same elements.

9.2.5. Permitted XPath Expressions

The W3C XML Schema Recommendation states that "to reduce the burden on implementers, in particular implementers of streaming processors, only restricted subsets of XPath expressions are allowed" in xs:selector and xs:field. The result of this statement is a limited subset of XPath that allows only the selection of nodes that are descendants of or are part of the current locations.

The XPath expressions allowed in xs:selector must exclusively go deeper into the hierarchy of the XML element nodes, do not allow any tests in the XPath steps, and must match a set of elements. In addition, the XPath expressions allowed in xs:field can also select attributes.

The full BNF for this subset is given in the reference guide. Rather than giving a verbose explanation, let's see some examples of what is possible and what is not.

The following are allowed:

xpath="author"
Selects the child elements named author that do not belong to any namespace.

xpath="author|character"
Selects the child elements named author or character that do not belong to any namespace.

xpath="lib:author"
Selects the child elements named author that belong to the namespace whose prefix is "lib".

xpath="*"
Selects all the child elements.

xpath="lib:*"
Selects all the child elements that belong to the namespace whose prefix is "lib".

xpath="authors/author"
Selects all the authors/author child elements.

xpath=".//author"
Selects all the elements that are descendants of the current node, named author, and don't belong to any namespace.

xpath="author/@id"
Selects the id attribute of the author child element (allowed only for xs:field, and not for xs:selector).

xpath="@id|@name"
Selects @id or @name (valid only in xs:field, since attributes are forbidden in xs:selector).

The following are forbidden:

xpath="/library/author"
Absolute paths are not allowed.

xpath="../author"
The parent axis is not allowed.

xpath=".//*[@id]"
Tests are not allowed.

xpath="author[@type='comics']"
Tests are not allowed.

xpath="substring-after(@xlink:href, `#')"
Function calls are not allowed.

xpath="//author"
Absolute paths are not allowed.

TIP: Default namespaces do not apply within XPath expressions, and elements and attributes must always be qualified by a prefix if they belong to a namespace.

Previous | Home | Next
9. Defining Uniqueness, Keys, and Key References | Book Index | 9.3. ID/IDREF Versus /
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


ID/IDREF Versus / (XML Schema)

 XML SchemaSearch this book

9.3. ID/IDREF Versus xs:key/xs:keyref

We have enumerated the features that key and key references provide beyond those of ID and IDREF: no constraint on datatypes, tests done on values rather than on lexical representation, and independent sets of values for each key. To get a complete picture, we need to see if key and key references can emulate ID and IDREF; in other words, we must determine which features of ID and IDREF are missing from key and key references.

First of all, the location of our key and keyref definition needs to be on the root of the document to fully emulate the ID and IDREF that is global to a document, whatever its document element is. Our best move with W3C XML Schema, which doesn't directly constrain the root node, is to locate our declaration in the global element that is likely to be used as a document element.

Then, to define the xs:selector, we will need to provide the list of all the elements holding ID attributes within a single XPath expression.

The last difference is that ID allows definition of a whitespace-separated list of ID references (through IDREFS datatypes), while there is no similar possibility with xs:key. (There is no xs:keyrefs!)

To use xs:key and xs:keyref, we, therefore, have to modify the instance document that is used in the section about ID and IDREF to transform the list of IDs referencing characters into a series of references, and to use the same convention for IDs and references in all our elements:

<library xsi:noNamespaceSchemaLocation="library-keys-id-key.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <book id="isbn-0836217462">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <author-ref ref="au-Charles_M._Schulz"/>
    <character-ref ref="ch-Peppermint_Patty"/>
    <character-ref ref="ch-Snoopy"/>
    <character-ref ref="ch-Schroeder"/>
    <character-ref ref="ch-Lucy"/>
  </book>
  .../...
  <author id="au-Charles-M.-Schulz">
    <name>
      Charles M. Schulz
    </name>
    <nickName>
      SPARKY
    </nickName>
    <born>
      November 26, 1922
    </born>
    <dead>
      February 12, 2000
    </dead>
  </author>
  <character id="ch-Peppermint-Patty">
    <name>
      Peppermint Patty
    </name>
    <since>
      Aug. 22, 1966
    </since>
    <qualification>
      bold, brash and tomboyish
    </qualification>
  </character>
  ...
</library>

The definition follows:

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:key name="ID">
    <xs:selector xpath="book|author|character"/>
    <xs:field xpath="@id"/>
  </xs:key>
  <xs:keyref name="IDREF" refer="ID">
    <xs:selector xpath="book/author-ref|book/character-ref"/>
    <xs:field xpath="@ref"/>
  </xs:keyref>
</xs:element>

This example illustrates the main difference between the two mechanisms: ID/IDREF declarations are done at the level where they are used, and are, therefore, fully integrated with the pseudo-object-oriented features of W3C XML Schema, while key/keyref definitions are done at the level of a common ancestor and rely on the actual structure of the instance documents rather than on its object-oriented schema.

TIP: Since key/keyref rely on the actual structure of the instance documents, they ignore features such as substitution groups. Their XPath expressions need to explicitly define each of the possible element names (except when they use a "*" to indicate "any element" at a particular level).

Previous | Home | Next
9.2. XPath-Based Identity Checks | Book Index | 9.4. Using and As Co-occurrence Constraints
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Using and As Co-occurrence Constraints (XML Schema)

 XML SchemaSearch this book

9.4. Using xs:key and xs:unique As Co-occurrence Constraints

Co-occurrence constraints are interdependent conditions given on the child elements or attributes of a node, such as "if this element is present, then this attribute must be absent." Under-implemented within W3C XML Schema, these constraints can be a workaround to the "Consistent Declaration rule," which forbids definition of two different content models for an element at the same location in an instance document. With co-occurrence constraints, one can define a superset of the two content models and add the constraints to forbid the unwanted combinations. This is frequently useful with vocabularies (such as RDF) in which some properties can be expressed either as an attribute or an element, and in which we may want to extend our book example to accept the ISBN number and the title to be expressed as elements or attributes. The two following instance documents are then valid (with their two remaining combinations):

<book id="b0836217462" available="true">
  <isbn>
    0836217462
  </isbn>
  <title lang="en">
    Being a Dog Is a Full-Time Job
  </title>
  ../..
</book>

or:

<book id="b0836217462" available="true" isbn="0836217462"
  title="Beinga Dog Is a Full-Time Job">
  .../...
</book>

The obvious way is to define the book element as a choice between the four different valid content models (with the four combinations of elements and attributes). However, this is forbidden by the Consistent Declaration rule, which states that only one content model may be used for a given element. The workaround is to define a content model that accepts both optional elements and attributes:

<xs:complexType>
  <xs:sequence>
    <xs:element ref="isbn" minOccurs="0"/>
    <xs:element ref="title" minOccurs="0"/>
    <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="character" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
  <xs:attribute ref="available"/>
  <xs:attribute ref="isbn"/>
  <xs:attribute ref="title"/>
</xs:complexType>

This definition allows instance documents with both a title (or isbn) element and attribute or instance documents without any title or isbn at all. We need to add co-occurrence constraints. In a more general case, these constraints cannot be expressed using W3C XML Schema, and we need to embed other languages (as shown in Chapter 14, "Documenting Schemas") but when we think about it, xs:unique, xs:key, and xs:keyref can be considered very specific co-occurrence constraints and they can be used here. If we want to insure that we have only one title and ISBN number, we can add a xs:key definition in the book element itself:

<xs:key name="isbn">
  <xs:selector xpath="."/>
  <xs:field xpath="isbn|@isbn"/>
</xs:key>

<xs:key name="title">
  <xs:selector xpath="."/>
  <xs:field xpath="title|@title"/>
</xs:key>

These keys are evaluated in the scope of a book element and won't have any effect outside each book element. They will consider the book invalid if the XPath expression in their field element returns either no nodes or multiple nodes. Note that if we had used xs:unique instead of xs:key, we would still have required that only one of the elements or attributes be present, but that would have made the property optional. For the record, the full definition of our book element would then be:

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="isbn" minOccurs="0"/>
      <xs:element ref="title" minOccurs="0"/>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
    <xs:attribute ref="available"/>
    <xs:attribute ref="isbn"/>
    <xs:attribute ref="title"/>
  </xs:complexType>
  <xs:key name="isbn">
    <xs:selector xpath="."/>
    <xs:field xpath="isbn|@isbn"/>
  </xs:key>
  <xs:key name="title">
    <xs:selector xpath="."/>
    <xs:field xpath="title|@title"/>
  </xs:key>
</xs:element>
Previous | Home | Next
9.3. ID/IDREF Versus / | Book Index | 10. Controlling Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Controlling Namespaces (XML Schema)

 XML SchemaSearch this book

Chapter 10. Controlling Namespaces

Contents:

Namespaces Present Two Challenges to Schema Languages
Namespace Declarations
To Qualify Or Not to Qualify?
Disruptive Attributes
Namespaces and XPath Expressions
Referencing Other Namespaces
Schemas for XML, XML Base and XLink
Namespace Behavior of Imported Components
Importing Schemas with No Namespaces
Chameleon Design
Allowing Any Elements or Attributes from a Particular Namespace

The W3C released Namespaces in XML about a year after XML 1.0. Namespaces provide a URI-based mechanism that helps differentiate XML vocabularies. Rather than update XML 1.0's DTDs to provide explicit namespace support, the W3C chose to implement namespace support in W3C XML Schema. Support of namespaces was eagerly awaited by the XML community and, thus, are especially well-polished by the W3C XML Schema editors.

Namespaces caused two problems to DTDs. One was how to recognize namespaces defined using different prefixes in instance documents. The other was how best to facilitate the definition of schemas with multiple namespaces. The problem of open schemas tightly controlling some namespaces while keeping the flexibility to add unknown elements and attributes from unknown namespaces, was especially difficult.

W3C XML Schema has gone beyond these expectations for its use of namespaces by associating a namespace to all the objects (elements and attributes, but also simple and complex types as well as groups of elements and attributes) defined in a schema, allowing the use of namespaces to build modular libraries of schemas.

10.1. Namespaces Present Two Challenges to Schema Languages

Namespace prefixes should only be considered to be local shortcuts to replace the URI references that are the real identifiers for a namespace. The following documents should, therefore, be considered strictly equivalent by namespace-aware applications:

<?xml version="1.0"?>
<library xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462">
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <person id="CMS">
        <name>
          Charles M Schulz
        </name>
      </person>
    </authors>
  </book>
</library>

In the document above, the namespace "http://dyomedea.com/ns/library" is defined as the default namespace and applies to all the elements within the document. Next, we'll show a namespace-equivalent, but very different-looking, document:

<?xml version="1.0"?>
<!-- Namespace: http://dyomedea.com/ns/library -->
<lib:library xmlns:lib="http://dyomedea.com/ns/library">
  <lib:book id="b0836217462">
    <lib:title>
      Being a Dog Is a Full-Time Job
    </lib:title>
    <lib:authors>
      <lib:person id="CMS">
        <lib:name>
          Charles M Schulz
        </lib:name>
      </lib:person>
    </lib:authors>
  </lib:book>
</lib:library>

The namespace "http://dyomedea.com/ns/library" is defined as mapping to the prefix lib and is used as a prefix for all the elements within the document. Next, we'll create another namespace-equivalent document using a different prefix.

<?xml version="1.0"?>
<!-- Namespace: http://dyomedea.com/ns/library -->
<l:library xmlns:l="http://dyomedea.com/ns/library">
  <l:book id="b0836217462">
    <l:title>
      Being a Dog Is a Full-Time Job
    </l:title>
    <l:authors>
      <l:person id="CMS">
        <l:name>
          Charles M Schulz
        </l:name>
      </l:person>
    </l:authors>
  </l:book>
</l:library>

The namespace "http://dyomedea.com/ns/library" is defined as mapping to the prefix l and is used as a prefix for all the elements within the document. Finally, we'll mix all of these possibilities in a single document still namespace-equivalent to the others.

<?xml version="1.0"?>
<!-- Namespace: http://dyomedea.com/ns/library -->
<l:library xmlns:l="http://dyomedea.com/ns/library">
  <l:book id="b0836217462" xmlns:lib="http://dyomedea.com/ns/library">
    <l:title>
      Being a Dog Is a Full-Time Job
    </l:title>
    <lib:authors>
      <l:person id="CMS" xmlns="http://dyomedea.com/ns/library">
        <name>
          Charles M Schulz
        </name>
      </l:person>
    </lib:authors>
  </l:book>
</l:library>

The same namespace is defined and used as l, lib, and even as a default namespace, depending on its location in the document. This last example is, of course, an extreme case that isn't recommended. This document conforms to the namespaces recommendation, however, and the specification states that it is strictly equivalent to the three previous ones.

DTDs are not aware of the namespaces. Since the colon (:) is allowed in the XML names, lib:person, l:person, and person are three different, valid names for a DTD. Furthermore, a DTD sees namespace declaration attributes (xmlns, xmlns:l, xmlns:lib) as ordinary attributes that need to be declared.

A XML document using namespaces is a well-formed XML 1.0 document and it is perfectly possible to write a DTD to describe it. Nevertheless, you must define the prefixes that can be used and the location where the namespace declarations must be inserted. This is acceptable only if you can fully control or specify the authoring processes of the documents.

The second and larger issue is design. Since XML is often used as the glue between different applications, it is becoming increasingly important to be able to define modular vocabularies that can live together in the same document, and namespaces were invented to make this possible. To take advantage of this feature, it is often necessary to define open vocabularies that will define places where external elements and attributes from external namespaces may be included without breaking the applications.

Imagine a marketing department wants to add the type of cover and the number of pages to the information about a particular book. A neat way to do this--if this new information is specific to their needs and we don't want to break the existing applications--is to create a new namespace:

<?xml version="1.0"?>
<!-- Namespace: http://dyomedea.com/ns/library -->
<library xmlns="http://dyomedea.com/ns/library"
  xmlns:mkt="http://dyomedea.com/ns/library/mkt">
  <book id="b0836217462">
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <person id="CMS">
        <name>
          Charles M Schulz
        </name>
      </person>
    </authors>
    <mkt:cover>
      Paperback
    </mkt:cover>
    <mkt:pages>
      128
    </mkt:pages>
  </book>
</library>

However, if we want to keep our schema independent of the marketing application, we need a flexible way to open it and say "accept any element from the marketing namespace at the end of our book element." Also, even if there might be other applications that work with our vocabulary, we can say to accept any element from any other namespace at the end of our book element.

Previous | Home | Next
9.4. Using and As Co-occurrence Constraints | Book Index | 10.2. Namespace Declarations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespace Declarations (XML Schema)

 XML SchemaSearch this book

10.2. Namespace Declarations

Until now, we have seen schemas for documents that had no namespace declarations of any kind and, therefore, did not belong to any namespace. To match the documents without namespaces, the schemas had no namespace declaration either, except the one needed to identify the W3C XML Schema namespace itself.

To match the elements and attributes that belong to a namespace, we need to associate this namespace with our schema through the targetNamespace attribute of the xs:schema element.

If we modify our library to use a single namespace:

<library xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462" available="yes">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    .../...
  </book>
</library>

We need to modify our schema to declare the namespace and to define it as the target namespace:

<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:lib="http://dyomedea.com/ns/library"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  .../...
</xs:schema>

The definition of the namespaces is especially important here, since W3C XML Schema uses them for two purposes.

As for any XML document that conforms to the namespaces Recommendation, the first purpose of the namespace declaration is to associate a URI reference that is the identifier of a namespace to a prefix, which is a shortcut for this identifier.

In our example, we have two such declarations: xmlns:xs="http://www.w3.org/2001/XMLSchema" and xmlns:lib="http://dyomedea.com/ns/library".

The first declaration associates the W3C XML Schema namespace with the prefix xs. We could, of course, have chosen any prefix, or even used this namespace as the default namespace; the choice of xs is just common usage.

The second declaration defines the namespace used in our instance document, xmlns:lib="http://dyomedea.com/ns/library". Here we chose to use the lib prefix, even though this namespace is never used for any element or attribute of the schema itself. We could also have chosen any prefix for this namespace, or even have defined it as our default namespace.

This second declaration is needed for the second usage of namespace prefixes. W3C XML Schema uses the namespace prefixes to resolve all the references to the components of a schema (datatypes, elements, attributes, groups, etc.), as well as for the XPath expressions used in the xs:unique, xs:key, and xs:keyref declarations.

We haven't yet mentioned which namespace this schema describes. We must do so using the targetNamespace attribute that defines the URI reference that identifies the target namespace.

With this last piece of information, a schema processor knows what the target namespace is. With the two namespaces declarations already complete, it also knows which prefix we want to use for it and for the W3C XML Schema namespace. This is sufficient information to write our schema.

NOTE: This use of the namespace prefixes, common to W3C XML Schema and XSLT, is very controversial, since it creates a dependency between W3C XML Schema (considered an application) and the prefixes chosen for the namespaces. This breaks the layered structure of the XML specifications: the markup and its content become interdependent and cannot be changed independently any longer.

Not unlike a communication protocol, the XML specifications may be seen as a set of envelopes. XML 1.0 is the outermost envelope into which the namespaces are included. While the applications should be independent of these envelopes, the fact that W3C XML Schema is making use of the namespace prefixes inside its own attributes glues the schema to its envelope. This is a very dangerous practice that should be discouraged for other vocabularies that define their own sets of prefixes.

One of the consequences of this practice is that Canonical XML has been obliged to remove namespace prefix rewriting from its requirements, meaning that the four flavors of our library that are strictly equivalent, per the namespace recommendation, will have four different canonical values, and different digital signatures as a result.

Previous | Home | Next
10. Controlling Namespaces | Book Index | 10.3. To Qualify Or Not to Qualify?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


To Qualify Or Not to Qualify? (XML Schema)

 XML SchemaSearch this book

10.3. To Qualify Or Not to Qualify?

The schemas that we have written up to this point have had no target namespace declaration. We also could only describe elements and attributes that didn't belong to any namespace.

The declaration of a target namespace gives us the possibility of defining elements and attributes that belong to the target namespace (called "qualified") and elements and attributes that don't belong to any namespace (called "unqualified").

TIP: The purpose of a schema is to describe a vocabulary, in which top-level nodes belong to its target namespace. For this reason, it is forbidden to define global elements that are unqualified when a target namespace is declared.

The distinction between qualified and unqualified elements and attributes is made through their <form> attributes--for example:

<xs:element name="book" form="qualified"/>

<xs:attribute name="isbn" form="qualified"/>

<xs:attribute name="lang" form="unqualified"/>

<xs:element name="character" form="unqualified"/>

The default values of these form attributes are defined in the elementFormDefault and attributeFormDefault attributes that we have added in our xs:schema element.

These attributes both have default values of their own, which are: elementFormDefault="unqualified" and attributeFormDefault="unqualified". These values are appropriate to the case in which only the document element uses a namespace:

<lib:library xmlns:lib="http://dyomedea.com/ns/library">
  <book id="b0836217462" available="yes">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
  </book>
</lib:library>

Since global elements and attributes must be qualified, defining this schema as a single schema requires that all the elements and attributes are locally defined.

Another combination, elementFormDefault="qualified" and attributeFormDefault="unqualified" matches the common case in which a namespace is attached to the root element as the default namespace that will, by definition apply to the included elements but not to the attributes. (Per the Namespaces Recommendation, the default namespace does not apply to attributes.)

<library xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462" available="yes">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
  </book>
</library>

The usage considers that unqualified attributes belong to the same vocabulary as their parent element, and the vocabularies that take advantage of qualified attributes are often vocabularies that supply attributes used in elements from other namespaces. Examples of such vocabularies include RDF and attribute-only vocabularies such as XLink and XML Base. The schemas for these vocabularies will benefit from an attributeFormDefault set to qualified.

One should pay attention to the special status of the attributes in the namespace specification. The default namespace does not apply to attributes. This means that an attribute without a prefix is considered to have no namespace.

Another confusing aspect of attributes with a prefix (which thus belong to a namespace) is these attributes are called "global attributes" in the namespaces recommendation. In W3C XML Schema the term global is used differently, in opposition to local elements or attributes. A global attribute, per namespaces, is therefore a qualified attribute per W3C XML Schema, and may be globally or locally defined.

TIP: Remember that <elementFormDefault> and <attributeFormDefault> define default values and that you can specify--element by element and attribute by attribute--if they are qualified or not.

Before we see how we can bring more namespaces into the game, let's look at a simple instance document with a single namespace and unqualified attributes:

<?xml version="1.0"?>
<library xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462" available="yes">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <person id="CMS">
        <name>
          Charles M Schulz
        </name>
        <born>
          1922-11-26
        </born>
        <dead>
          2000-02-12
        </dead>
      </person>
    </authors>
    <characters>
      <person id="PP">
        <name>
          Peppermint Patty
        </name>
        <born>
          1966-08-22
        </born>
        <qualification>
          bold, brash and tomboyish
        </qualification>
      </person>
      <person id="Snoopy">
        <name>
          Snoopy
        </name>
        <born>
          1950-10-04
        </born>
        <qualification>
          extroverted beagle
        </qualification>
      </person>
      <person id="Schroeder">
        <name>
          Schroeder
        </name>
        <born>
          1951-05-30
        </born>
        <qualification>
          brought classical music to the Peanuts strip
        </qualification>
      </person>
      <person id="Lucy">
        <name>
          Lucy
        </name>
        <born>
          1952-03-03
        </born>
        <qualification>
          bossy, crabby and selfish
        </qualification>
      </person>
    </characters>
  </book>
</library>

If we want to avoid confusion while writing a schema for this instance document, we can define prefixes in the schema for both our target namespace and for the W3C XML Schema namespace, leading to a schema such as:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:lib="http://dyomedea.com/ns/library"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="lib:bookType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="person">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="born" type="xs:date"/>
        <xs:element name="dead" type="xs:date" minOccurs="0"/>
        <xs:element name="qualification" type="xs:string"
          minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:ID" use="required"/>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="bookType">
    <xs:sequence>
      <xs:element name="isbn" type="xs:NMTOKEN"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="authors">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="lib:person" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="characters">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="lib:person" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="available" type="xs:string" use="required"/>
  </xs:complexType>
</xs:schema>

In this example, the names of components defined by the schema are always unprefixed when they are defined. Because this schema only defines components in the single http://dyomedea.com/ns/library namespace (identified as the targetNamespace attribute of the xs:schema element), there isn't any risk of confusion. The only other namespaces used here are the namespace for W3C XML Schema itself, identified with an xs prefix, and another mapping for the http://dyomedea.com/ns/library namespace, using lib as the prefix. The lib-prefixed form is used for cross-references between declarations.

A strictly equivalent schema, defining the exact same data model, can be defined using the target namespace as the default namespace of the schema document:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://dyomedea.com/ns/library">
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="bookType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="person">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="born" type="xs:date"/>
        <xs:element name="dead" type="xs:date" minOccurs="0"/>
        <xs:element name="qualification" type="xs:string"
          minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:ID" use="required"/>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="bookType">
    <xs:sequence>
      <xs:element name="isbn" type="xs:NMTOKEN"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="authors">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="person" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="characters">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="person" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="available" type="xs:string" use="required"/>
  </xs:complexType>
</xs:schema>

In this version, all references to W3C XML Schema's own elements are still made using the xs prefix. Names of components are still unprefixed, but the lib prefix is now unnecessary, so all of those prefixes can disappear. Because the default namespace is defined in this document, W3C XML Schema will understand the connection between the components it defines and references to those components.

If you prefer to use prefixes on the components you are defining and use the W3C XML Schema vocabulary without prefixes, you can also define the W3C XML Namespace as the default namespace and declare a prefixed namespace (here, lib) for the components you're defining:

<?xml version="1.0"?>
<schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns="http://www.w3.org/2001/XMLSchema"
  xmlns:lib="http://dyomedea.com/ns/library">
  <element name="library">
    <complexType>
      <sequence>
        <element name="book" type="lib:bookType"/>
      </sequence>
    </complexType>
  </element>
  <element name="person">
    <complexType>
      <sequence>
        <element name="name" type="string"/>
        <element name="born" type="date"/>
        <element name="dead" type="date" minOccurs="0"/>
        <element name="qualification" type="string" minOccurs="0"/>
      </sequence>
      <attribute name="id" type="ID" use="required"/>
    </complexType>
  </element>
  <complexType name="bookType">
    <sequence>
      <element name="isbn" type="NMTOKEN"/>
      <element name="title" type="string"/>
      <element name="authors">
        <complexType>
          <sequence>
            <element ref="lib:person" maxOccurs="unbounded"/>
          </sequence>
        </complexType>
      </element>
      <element name="characters">
        <complexType>
          <sequence>
            <element ref="lib:person" maxOccurs="unbounded"/>
          </sequence>
        </complexType>
      </element>
    </sequence>
    <attribute name="id" type="ID" use="required"/>
    <attribute name="available" type="string" use="required"/>
  </complexType>
</schema>

The references to W3C XML Schema data types are now done without a prefix while the references to components defined in the target namespace are done with a "lib" prefix.

Previous | Home | Next
10.2. Namespace Declarations | Book Index | 10.4. Disruptive Attributes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Disruptive Attributes (XML Schema)

 XML SchemaSearch this book

10.4. Disruptive Attributes

So far we have seen that W3C XML Schema treats global attributes and elements alike, requiring both types to be explicitly namespace-qualified to be global. Although this approach makes sense--there's little reason to treat elements and attributes differently--it's different from the approach the Namespaces in XML Recommendation took. Global attributes turn out to be a rather unusual, though useful, case.

To understand this phenomenon, we need to examine the Namespaces in XML Recommendation closely. This differentiates qualified and unqualified attributes, explaining that only attributes applied to elements from another namespace need to be qualified (these are also called global attributes), and that unqualified attributes are considered to belong to the vocabulary from their parent element without needing to belong to their namespace. In this case, they inherit the membership to the namespace of their parent element without needing to show it. This is tightly linked to the fact that default namespaces do not apply to attributes (they don't need to since attributes are considered members of the namespace of their parent without needing default namespaces). In practice, most XML vocabularies will use unqualified attributes, such as:

<book id="b0836217462" xmlns="http://dyomedea.com/ns/library"/>

or:

<lib:book id="b0836217462"
  xmlns:lib="http://dyomedea.com/ns/library"/>

Very few would use qualified attributes, such as:

<lib:book lib:id="b0836217462"
  xmlns:lib="http://dyomedea.com/ns/library"/>

Also, very few would use that or its equivalent if we use a default namespace for the element that doesn't apply to the attribute:

<book lib:id="b0836217462" xmlns="http://dyomedea.com/ns/library"
  xmlns:lib="http://dyomedea.com/ns/library"/>

Unfortunately, since the W3C XML Schema requires that all the global attributes be qualified, this means that global attributes can't be used for those unqualified attributes that are used most of the time in XML vocabularies, and that our unqualified id attribute cannot be declared as global. In practice, this means that most of the time we will just define local attributes within the element or complex type definitions. When we want to define unqualified attributes that may be reused in different elements, we will either define them in a specific schema without a target namespace, which will be imported (but there is risk of collision if we mix several schemas for different target namespaces following this policy), or "hide" those attributes inside of attribute groups, such as:

<xs:attributeGroup name="id">
  <xs:attribute name="id" form="unqualified" type="xs:ID"/>
</xs:attributeGroup>
Previous | Home | Next
10.3. To Qualify Or Not to Qualify? | Book Index | 10.5. Namespaces and XPath Expressions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespaces and XPath Expressions (XML Schema)

 XML SchemaSearch this book

10.5. Namespaces and XPath Expressions

It's time to remember that default namespaces do not apply to XPath expressions, and that a prefix needs to be defined for each namespace used in the XPath expressions of the xs:unique, xs:key, and xs:keyref declarations!

A prefix declaration is therefore required to write expressions that reference qualified elements and attributes, even if the target namespace is defined as the default namespace. It's also important to notice that the xs:keyref refer attribute is a QName that needs to be prefixed, and a xs:key/xs:keyref on the authors in (in our previous example) with a namespace could become:

<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:lib="http://dyomedea.com/ns/library"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="lib:bookType"
          maxOccurs="unbounded"/>
        <xs:element name="author" type="lib:authorType"
          maxOccurs="unbounded"/>
        <xs:element name="characters" type="lib:characterType"
          maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
    <xs:key name="author">
      <xs:selector xpath="lib:author"/>
      <xs:field xpath="@id"/>
    </xs:key>
    <xs:keyref name="keyref" refer="lib:author">
      <xs:selector xpath="lib:book/lib:author-ref"/>
      <xs:field xpath="@ref"/>
    </xs:keyref>
  </xs:element>
</xs:schema>
Previous | Home | Next
10.4. Disruptive Attributes | Book Index | 10.6. Referencing Other Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Referencing Other Namespaces (XML Schema)

 XML SchemaSearch this book

10.6. Referencing Other Namespaces

One of the goals of the namespaces specification is to allow the use of documents mixing elements and attributes from different vocabularies. W3C XML Schema lets you take full advantage of this possibility.

Part of the library vocabulary describes persons. This could be reused by other applications, and we might want to define a specific namespace and give it the URI reference "http://dyomedea.com/ns/people":

<?xml version="1.0"?>
<library xmlns:ppl="http://dyomedea.com/ns/people"
  xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462" available="yes">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <ppl:person id="CMS">
        <ppl:name>
          Charles M Schulz
        </ppl:name>
        <ppl:born>
          1922-11-26
        </ppl:born>
        <ppl:dead>
          2000-02-12
        </ppl:dead>
      </ppl:person>
    </authors>
    <characters>
      <ppl:person id="PP">
        <ppl:name>
          Peppermint Patty
        </ppl:name>
        <ppl:born>
          1966-08-22
        </ppl:born>
        <ppl:qualification>
          bold, brash and tomboyish
        </ppl:qualification>
      </ppl:person>
      <ppl:person id="Snoopy">
        <ppl:name>
          Snoopy
        </ppl:name>
        <ppl:born>
          1950-10-04
        </ppl:born>
        <ppl:qualification>
          extroverted beagle
        </ppl:qualification>
      </ppl:person>
      <ppl:person id="Schroeder">
        <ppl:name>
          Schroeder
        </ppl:name>
        <ppl:born>
          1951-05-30
        </ppl:born>
        <ppl:qualification>
          brought classical music to the Peanuts strip
        </ppl:qualification>
      </ppl:person>
      <ppl:person id="Lucy">
        <ppl:name>
          Lucy
        </ppl:name>
        <ppl:born>
          1952-03-03
        </ppl:born>
        <ppl:qualification>
          bossy, crabby and selfish
        </ppl:qualification>
      </ppl:person>
    </characters>
  </book>
</library>

To handle these two namespaces, we need to define two different schemas (one per namespace). One will describe our vocabulary about persons as well as include the definitions of the element person and its child elements:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/people"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:ppl="http://dyomedea.com/ns/people"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="person">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="born" type="xs:date"/>
        <xs:element name="dead" type="xs:date" minOccurs="0"/>
        <xs:element name="qualification" type="xs:string"
          minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:ID" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

This schema describes the namespace "http://dyomedea.com/ns/people", and this vocabulary doesn't include anything from any other namespace. The schema is then similar to the examples we've seen in this chapter so far. It can be used alone with documents that use only this namespace, but can also be "imported" by schemas that describe other namespaces but would like to use some of its definitions.

To do this, the schema that describes the including vocabulary needs two pieces of information. It must have a prefix for the namespaces that will be included; this is done by a usual namespace declaration. It also must have a hint on where it can find the schema for this namespace; this is done using a xs:import element:

<xs:import namespace="http://dyomedea.com/ns/people"
  schemaLocation="simple-2-ns-ppl.xsd"/>

The schema now has all the information it needs to resolve references to schema components that belong to the http://dyomedea.com/ns/people namespace. References can be made just using its prefix:

<xs:element ref="ppl:person"/>

A full schema for the library vocabulary can then be:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:ppl="http://dyomedea.com/ns/people"
  xmlns:lib="http://dyomedea.com/ns/library">
  <xs:import namespace="http://dyomedea.com/ns/people"
    schemaLocation="simple-2-ns-ppl.xsd"/>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="lib:bookType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="bookType">
    <xs:sequence>
      <xs:element name="isbn" type="xs:NMTOKEN"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="authors">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="ppl:person"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="characters">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="ppl:person" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="available" type="xs:string" use="required"/>
  </xs:complexType>
</xs:schema>
TIP: When importing schemas from other namespaces, we can only refer to the global components defined in the imported schemas.

Previous | Home | Next
10.5. Namespaces and XPath Expressions | Book Index | 10.7. Schemas for XML, XML Base and XLink
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schemas for XML, XML Base and XLink (XML Schema)

 XML SchemaSearch this book

10.7. Schemas for XML, XML Base and XLink

XML Base, XLink, and XML 1.0 all define vocabularies that are composed only of attributes. Attributes such as xml:base, xlink:href, or xml:lang are designed to work with elements from any vocabulary, imparting their meaning to those elements and sometimes the descendants of those elements.

10.7.1. XML Attributes

In this section, we will discuss how to allow the use of a XML attribute in our document. To illustrate this, let's use a xml:lang attribute to qualify the language of several descriptions in several languages for our book:

<?xml version="1.0"?>
<library xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462">
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <description xml:lang="en">
      Its title says it all !
    </description>
    <description xml:lang="fr">
      Son titre le résume parfaitement !
    </description>
  </book>
</library>

Following Namespaces in XML 1.0, W3C XML Schema considers xml:lang, xml:space (from the XML 1.0 recommendation) and xml:base (from the XML Base specification) to belong to the XML 1.0 namespace identified by the URI reference "http://dyomedea.com/ns/people".

TIP: The XML 1.0 namespace is an exception in the namespaces world, since we don't need to declare it in the instance documents. Its prefix "xml" is reserved for this usage and has a special meaning, even for the parsers that do not support the namespaces. In theory, any other prefix can be assigned to the XML 1.0 namespace, but this practice is confusing and shouldn't be encouraged.

To use attributes from this namespace, we will declare the XML namespace, import a schema, and reference both of them, as we've done previously for the elements from our "people" namespace:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:lib="http://dyomedea.com/ns/library"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:import namespace="http://www.w3.org/XML/1998/namespace"
    schemaLocation="xml.xsd"/>
  <xs:element name="description">
    <xs:complexType>
      <xs:simpleContent>
        <xs:restriction base="xs:string">
          <xs:attribute ref="xml:lang"/>
        </xs:restriction>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="lib:book"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="title" type="xs:string"/>
        <xs:element ref="lib:description" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:ID" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

In this example, we import a local schema (xml.xsd) for the XML namespace. The W3C has defined its own schema for the xml:lang, xml:space, and xml:base attributes. This is available at http://www.w3.org/2001/xml.xsd:

<?xml version='1.0'?>
<!DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" >
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xmlns:xs="http://
www.w3.org/2001/XMLSchema" xml:lang="en">

 <xs:annotation>
  <xs:documentation>
   See http://www.w3.org/XML/1998/namespace.html and
   http://www.w3.org/TR/REC-xml for information about this namespace.
  </xs:documentation>
 </xs:annotation>

 <xs:annotation>
  <xs:documentation>This schema defines attributes and an attribute group
        suitable for use by
        schemas wishing to allow xml:base, xml:lang or xml:space attributes
        on elements they define.

        To enable this, such a schema must import this schema
        for the XML namespace, e.g. as follows:
        &lt;schema . . .>
         . . .
         &lt;import namespace="http://www.w3.org/XML/1998/namespace"
                    schemaLocation="http://www.w3.org/2001/03/xml.xsd"/>

        Subsequently, qualified reference to any of the attributes
        or the group defined below will have the desired effect, e.g.

        &lt;type . . .>
         . . .
         &lt;attributeGroup ref="xml:specialAttrs"/>

         will define a type which will schema-validate an instance
         element with any of those attributes</xs:documentation>
 </xs:annotation>

 <xs:annotation>
  <xs:documentation>In keeping with the XML Schema WG's standard versioning
   policy, this schema document will persist at
   http://www.w3.org/2001/03/xml.xsd.
   At the date of issue it can also be found at
   http://www.w3.org/2001/xml.xsd.
   The schema document at that URI may however change in the future,
   in order to remain compatible with the latest version of XML Schema
   itself.  In other words, if the XML Schema namespace changes, the version
   of this document at
   http://www.w3.org/2001/xml.xsd will change
   accordingly; the version at
   http://www.w3.org/2001/03/xml.xsd will not change.
  </xs:documentation>
 </xs:annotation>

 <xs:attribute name="lang" type="xs:language">
  <xs:annotation>
   <xs:documentation>In due course, we should install the relevant ISO 2- and 3-letter
         codes as the enumerated possible values . . .</xs:documentation>
  </xs:annotation>
 </xs:attribute>

 <xs:attribute name="space" default="preserve">
  <xs:simpleType>
   <xs:restriction base="xs:NCName">
    <xs:enumeration value="default"/>
    <xs:enumeration value="preserve"/>
   </xs:restriction>
  </xs:simpleType>
 </xs:attribute>

 <xs:attribute name="base" type="xs:anyURI">
  <xs:annotation>
   <xs:documentation>See http://www.w3.org/TR/xmlbase/ for
                     information about this attribute.</xs:documentation>
  </xs:annotation>
 </xs:attribute>

 <xs:attributeGroup name="specialAttrs">
  <xs:attribute ref="xml:base"/>
  <xs:attribute ref="xml:lang"/>
  <xs:attribute ref="xml:space"/>
 </xs:attributeGroup>

</xs:schema>
TIP: Among the many controversial issues surrounding the usage of URIs, the decision to use a schema at its original location (on the W3C web site) versus using a local copy is not easy. Using a schema at its original location provides a guarantee of always accessing the most recent version, but this can be a problem if the schema becomes out of sync with the applications using it. It can also impact the ability to process documents when the hosting site is unreachable and may open a possibility of malicious substitutions of schemas.

10.7.2. XLink Attributes

To illustrate the usage of XLink attributes, we can replace the definition of the author that is currently within the description of a book under the authors element, with a reference to an author described as a "person" in another XML document:

<?xml version="1.0"?>
<library xmlns="http://dyomedea.com/ns/library"
  xmlns:xlink="http://www.w3.org/1999/xlink">
  <book id="b0836217462">
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <person xlink:href="authors.xml#CMS"/>
    </authors>
  </book>
</library>

Again, we need to define the namespace for XLink and to import a schema before referencing the XLink attributes:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:lib="http://dyomedea.com/ns/library">
  <xs:import namespace="http://www.w3.org/1999/xlink"
    schemaLocation="xlink.xsd"/>
  <xs:element name="person">
    <xs:complexType>
      <xs:attribute ref="xlink:href" use="required"/>
      <xs:attribute ref="xlink:type" fixed="simple"/>
      <xs:attribute ref="xlink:show" fixed="embed"/>
      <xs:attribute ref="xlink:actuate" fixed="onLoad"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="lib:book"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="title" type="xs:string"/>
        <xs:element ref="lib:authors"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:ID" use="required"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="authors">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="lib:person"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>
TIP: Following the suggestion of the XLink specification, we have defined additional XLink attributes (type, show, and actuate) as fixed values. While this is legitimate, one should note that these values are only available to the applications that use a W3C XML Schema post-schema validation infoset (PSVI).

Although the XLink specification doesn't provide a schema, creating the parts we need is very straightforward:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://www.w3.org/1999/xlink"
  elementFormDefault="qualified" attributeFormDefault="qualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xlink="http://www.w3.org/1999/xlink">
  <xs:attribute name="type">
    <xs:simpleType>
      <xs:restriction base="xs:NMTOKEN">
        <xs:enumeration value="simple"/>
        <xs:enumeration value="extended"/>
        <xs:enumeration value="locator"/>
        <xs:enumeration value="arc"/>
        <xs:enumeration value="resource"/>
        <xs:enumeration value="title"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>
  <xs:attribute name="href" type="xs:anyURI"/>
  <xs:attribute name="role" type="xs:anyURI"/>
  <xs:attribute name="arcrole" type="xs:anyURI"/>
  <xs:attribute name="title" type="xs:string"/>
  <xs:attribute name="show">
    <xs:simpleType>
      <xs:restriction base="xs:NMTOKEN">
        <xs:enumeration value="new"/>
        <xs:enumeration value="replace"/>
        <xs:enumeration value="embed"/>
        <xs:enumeration value="other"/>
        <xs:enumeration value="none"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>
  <xs:attribute name="label" type="xs:NMTOKEN"/>
  <xs:attribute name="actuate">
    <xs:simpleType>
      <xs:restriction base="xs:NMTOKEN">
        <xs:enumeration value="onLoad"/>
        <xs:enumeration value="onRequest"/>
        <xs:enumeration value="other"/>
        <xs:enumeration value="none"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>
  <xs:attribute name="from" type="xs:NMTOKEN"/>
  <xs:attribute name="to" type="xs:NMTOKEN"/>
</xs:schema>

Previous | Home | Next
10.6. Referencing Other Namespaces | Book Index | 10.8. Namespace Behavior of Imported Components
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespace Behavior of Imported Components (XML Schema)

 XML SchemaSearch this book

10.8. Namespace Behavior of Imported Components

You may be wondering about the different approaches used to identify the namespaces for the elements defined by the schema and the data types defined by W3C XML Schema. Importing schemas imposes certain restrictions on the use of namespaces in the imported schema.

When we define an element or attribute, we give it a namespace. That namespace must be the same as the target namespace of the schema doing the importing, even if the datatype of the element or attribute belongs to a different namespace.

The rules are slightly different when we define an element or attribute by reference to a component which is in a different namespace, rather than a datatype. In that case, the name of the referenced component is imported, and that namespace must be the target namespace of the imported schema.

To illustrate how this works, we'll take a closer look at two ways to create schemas described in this simple example:

<?xml version="1.0"?>
<!-- Namespace: http://dyomedea.com/ns/library -->
<library xmlns:ppl="http://dyomedea.com/ns/people"
  xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462">
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <ppl:person id="CMS">
        <ppl:name>
          Charles M Schulz
        </ppl:name>
      </ppl:person>
    </authors>
  </book>
</library>

This document contains two namespaces. Everything except the contents of the authors element is in the http://dyomedea.com/ns/library namespace. The contents of the authors element (ppl:person and ppl:name) are in the http://dyomedea.com/ns/people namespace.

We have two main options for representing this document using W3C XML Schema. Both approaches start by defining a schema for the elements in the http://dyomedea.com/ns/library namespace. The first approach imports the schema defining the http://dyomedea.com/ns/people namespace, and then uses a reference to the ppl:person element to use it inside the authors element:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:ppl="http://dyomedea.com/ns/people"
  xmlns:lib="http://dyomedea.com/ns/library">
  <xs:import namespace="http://dyomedea.com/ns/people"
    schemaLocation="very-simple-2-ns-ppl.xsd"/>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="lib:bookType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="bookType">
    <xs:sequence>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="authors">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="ppl:person"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:schema>

The second approach does the same import, but defines the authors element as having the type ppl:authorType rather than defining its complex type explicitly, resulting in a shorter schema:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:lib="http://dyomedea.com/ns/library"
  xmlns:ppl="http://dyomedea.com/ns/people"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:import namespace="http://dyomedea.com/ns/people"
    schemaLocation="very-simple-2-ns-ppl.xsd"/>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="lib:bookType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="bookType">
    <xs:sequence>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="authors" type="ppl:authorType"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:schema>

Although the two schemas will validate the same instance documents, the design style is quite different. Applications relying on the schema for information about the document will see it in two very different ways. The first approach provides a cleaner separation between the two namespaces. The use of the reference allows the ppl:person element to appear inside the authors element but does nothing to mix the authors element with the http://dyomedea.com/ns/people namespace directly. The second approach is briefer, but assigns a datatype in one namespace to an element in another namespace.

If you are using your schemas purely for validation, this distinction is unimportant. Both schemas will validate identical sets of documents. If, however, your applications rely on your schemas for type information (using the PSVI or perhaps compile-time data-binding based on the schema), the perspective shift may matter. Using the datatype approach will mean that your applications need to understand quite a bit more about the contents of your schema and creates new dependencies between your application and the details of W3C XML Schema processing.

Previous | Home | Next
10.7. Schemas for XML, XML Base and XLink | Book Index | 10.9. Importing Schemas with No Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Importing Schemas with No Namespaces (XML Schema)

 XML SchemaSearch this book

10.9. Importing Schemas with No Namespaces

We have seen how we can reference components from other namespaces and how the elements and attributes included in these components keep their full name, namespaces, and form (in Section 10.3, "To Qualify Or Not to Qualify?"). However, this mechanism has a restriction: you can only reference global elements and attributes, and global elements and attributes must always be qualified.

Therefore, the only solution to importing elements and attributes with no namespace is to import a schema without any target namespace. Let's say that we want to describe a document in which the vocabulary to describe people has no namespace:

<?xml version="1.0"?>
<lib:library xmlns:lib="http://dyomedea.com/ns/library">
  <lib:book id="b0836217462">
    <lib:title>
      Being a Dog Is a Full-Time Job
    </lib:title>
    <lib:authors>
      <person id="CMS">
        <name>
          Charles M Schulz
        </name>
      </person>
    </lib:authors>
  </lib:book>
</lib:library>

The import of the schema without a namespace is done exactly as we've seen earlier with schemas having target namespaces, except now we omit the namespace attribute. Another point to note is that to be able to reference the components from the schema you are including, you will have to keep the default namespace (unprefixed) available:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:lib="http://dyomedea.com/ns/library"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:import schemaLocation="very-simple-2-ns-ppl-nons.xsd"/>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="lib:bookType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="bookType">
    <xs:sequence>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="authors">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="person"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:schema>

The included schema is:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="person" type="personType"/>
  <xs:complexType name="personType">
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:schema>

In this case, all the components are considered unqualified. All the other behavior, including the difference between referencing elements or attributes and using datatypes, is relevant.

We see then that to define unqualified elements and attributes in a schema with a target namespace, we have to choose between defining them locally in the schema or defining them in a separate schema without an imported target namespace.

Previous | Home | Next
10.8. Namespace Behavior of Imported Components | Book Index | 10.10. Chameleon Design
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Chameleon Design (XML Schema)

 XML SchemaSearch this book

10.10. Chameleon Design

This chapter wouldn't be complete without a look at the impact of namespaces on the inclusion of schemas through xs:include and xs:redefine.

When we initially introduced these features, we didn't worry about namespaces. However, these features have some important interactions with namespaces, since W3C XML Schema restricts inclusion (or redefinition) to pieces of schema with either the same target namespace or without any target namespace (schemas for other languages being "imported" rather than "included").

When a piece of schema that has no target namespace is included in a schema with a target namespace, the included definition acquires the target namespace of the including schema and behaves exactly as if it added the same target namespace. This feature allows creation of libraries with pieces of schema that are "namespace-transparent," and which take the namespace of the schema in which they are imported. This method is often called "chameleon," since the included schema takes the "color" of the context in which it is included.

To illustrate this feature and see its implication, let's look again at the example that we've used throughout this chapter:

<?xml version="1.0"?>
<library xmlns="http://dyomedea.com/ns/library">
  <book id="b0836217462">
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <person id="CMS">
        <name>
          Charles M Schulz
        </name>
      </person>
    </authors>
  </book>
</library>

If we want a library of schema parts that describe a person and can be included within several vocabularies, we can include a piece of schema that has no target namespace. This piece of schema would be something like the following (note that we do not define any target namespace):

<?xml version="1.0"?>
<xs:schema elementFormDefault="qualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="person">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="name" type="xs:string"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

And the including schema would look similar to the following example:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified"
  xmlns="http://dyomedea.com/ns/library"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:include schemaLocation="very-simple-1-ns-ppl.xsd"/>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="title" type="xs:string"/>
              <xs:element name="authors">
                <xs:complexType>
                 <xs:sequence>
                 <xs:element ref="person"/>
                 </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

While this looks very easy and neat, we may wonder if we are really building something modular. Including a piece of schema is similar to including a C header file. While this is slightly better than a copy/paste, the level of modularity that can be achieved in this way is very restricted.

The namespace of the element person is the namespace given to our library. An application cannot guess from looking at the instance document that this element is the same as a person element that it will find in another document describing, for example, an employee.

Looking at the modified infoset and checking the datatype doesn't help either, since the datatype will be defined as a datatype from a schema with the target namespace of our library, and won't match other target namespaces, including the same piece of schema.

The fact that the same person element is used by different vocabularies is totally lost by the processing of the include (or redefine). Before using it, you may consider if it isn't more useful to include information in the instance document using a separate namespace, or at least include information in the modified infoset by importing datatypes from another namespace rather than including common definitions.

Previous | Home | Next
10.9. Importing Schemas with No Namespaces | Book Index | 10.11. Allowing Any Elements or Attributes from a Particular Namespace
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Allowing Any Elements or Attributes from a Particular Namespace (XML Schema)

 XML SchemaSearch this book

10.11. Allowing Any Elements or Attributes from a Particular Namespace

We are going to see how to accommodate any element or attribute from other namespaces using our marketing extension, the http://dyomedea.com/ns/library/mkt namespace, as an example:

<?xml version="1.0"?>
<!-- Namespace: http://dyomedea.com/ns/library -->
<library xmlns="http://dyomedea.com/ns/library"
  xmlns:mkt="http://dyomedea.com/ns/library/mkt">
  <book id="b0836217462">
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <authors>
      <person id="CMS">
        <name>
          Charles M Schulz
        </name>
      </person>
    </authors>
    <mkt:cover>
      Paperback
    </mkt:cover>
    <mkt:pages>
      128
    </mkt:pages>
  </book>
</library>

To allow any elements from the http://dyomedea.com/ns/library/mkt namespace after the author element, we use a xs:any element:

<xs:any namespace="http://dyomedea.com/ns/library/mkt"
  processContents="skip"minOccurs="0" maxOccurs="unbounded"/>

xs:anyAttribute should be used to allow attributes:

<xs:anyAttribute namespace="http://dyomedea.com/ns/library/mkt"
  processContents="skip"/>

The two new attributes shown above are namespace and processContents.

namespace specifies the namespaces of the elements or attributes that will be accepted. The value should be a list of namespaces that URIs allow a number of wildcards. The wildcards permitted within the list are ##local (a nonqualified element) and ##targetNamespace (the target namespace). Two wildcards can also be used instead of the list: ##any (any namespace) and ##other (any namespace other than the target namespace).

TIP: It is not possible to specify that the possible namespaces are all the namespaces not defined in a schema, or even all the namespaces except those in a list. This is a serious limitation for multi-namespace vocabularies that would like to restrict some of the imported namespaces while remaining open to undefined namespaces.

processContents specifies the behavior of the validator regarding the elements or attributes from the specified namespaces. The possible values are "skip" (no validation is attempted on these elements or attributes), "strict" (schemas for the namespaces that will be included need to be available, and validators will validate the elements and attributes against these schemas), or "lax" (validators will do their best to find a schema for the included elements and attributes, validate them when they have found one, and silently skip the validation when they haven't). For example:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://dyomedea.com/ns/library">
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="book"/>
      </xs:sequence>
      <xs:anyAttribute namespace="http://dyomedea.com/ns/library/mkt"
        processContents="skip"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="title" type="xs:string"/>
        <xs:element name="authors">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="person">
                <xs:complexType>
                 <xs:sequence>
                 <xs:element name="name" type="xs:string"/>
                 </xs:sequence>
                 <xs:attribute name="id" type="xs:string"
                   use="required"/>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:any namespace="http://dyomedea.com/ns/library/mkt"
          processContents="skip"minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="id" use="required">
        <xs:simpleType>
          <xs:restriction base="xs:hexBinary"/>
        </xs:simpleType>
      </xs:attribute>
    </xs:complexType>
  </xs:element>
</xs:schema>

This schema has been opened to accept any element from a single namespace and can be further opened to accept any element from any namespace other than the target namespace:

<xs:anyAttribute namespace="##other" processContents="skip"/>

This mechanism is flexible enough (with the exception of the lack of support for any undefined namespaces already mentioned) to accommodate a large majority of applications, but we must note that these wildcards are considered particles and can't replace global element definitions. The unfortunate consequence is that document elements cannot be wildcarded because a schema needs to provide a closed list of possible document elements.

Previous | Home | Next
10.10. Chameleon Design | Book Index | 11. Referencing Schemas and Schema Datatypes in XML Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Referencing Schemas and Schema Datatypes in XML Documents (XML Schema)

 XML SchemaSearch this book

Chapter 11. Referencing Schemas and Schema Datatypes in XML Documents

Contents:

Associating Schemas with Instance Documents
Defining Element Types
Defining Nil (Null) Values
Beware the Intrusive Nature of These Features...

So far, we have seen how W3C XML Schemas can be written outside XML documents without touching the actual instance documents. In this chapter, we will introduce a new namespace to be used inside XML documents to provide information for use by schema processors. This information may identify the location of the associated schemas, as well as further identify the schema types used, which opens a new level of flexibility and interaction between schemas and instance documents in the design of XML applications.

This namespace (which is the same for all W3C XML Schema meta-information located in the instance documents themselves) is http://www.w3.org/2001/XMLSchema-instance. The prefix usually used to designate this namespace is xsi. This namespace uses only four attributes, which are considered valid in any element of any instance document without being declared in the schema.

11.1. Associating Schemas with Instance Documents

The first piece of information that may be useful for a schema processor is some hints about the locations where the schema processor might find schemas relevant to the instance document. This feature is similar to the SYSTEM identifier of the XML doctype declaration, but with some important differences. The first difference is that a schema may not be enough to describe a document, since each schema might describe only one namespace (or lack of a namespace), and the composition of the schemas can be done in the instance document. The second difference is that the locations indicated in the instance documents are only considered hints and may be overridden by the user or by the schema processor.

The hard link between a XML document and its DTD has fed many debates in the XML community. Many developers remember what happened when Netscape restructured their web site. The address of the DTD for RSS 0.91 (http://my.netscape.com/publish/formats/rss-0.91.dtd) suddenly returned a 404 error, breaking hundreds of applications. Another motivation for "soft" links between instance documents and their schemas is they allow application of different schemas, depending on local business rules. For instance, a supplier receiving an order in a XML document may have specific rules to check the document with its own schema.

For all these reasons, the Recommendation states that if a schema processor finds such information in a document, it should try to retrieve the schemas at the locations indicated, but it could be directed otherwise by the invoking application or user. When such information is missing, a schema processor may also be directed by the invoking application to dereference specified locations. When no information is provided at all by the invoking application or in the instance document, the schema processor is free to try any method to find a schema. Among the methods mentioned in this case, a schema processor may try to load the resource that may be available at the namespace URI to see if a schema is published there, but it could use other techniques as well, such as RDDL or catalog systems.

W3C XML Schema defines two attributes to define a list of schema locations associated with target namespaces as well as the location of a schema without target namespace. The attribute to use when there is no target namespace is xsi:noNamespaceSchemaLocation , and its value is a URI pointing to the corresponding schema. Although this attribute can be used without a declaration in any element of any instance document, it must be found by the schema processor before it needs it to validate any element or attribute (i.e., at or before the last point in the first element without a namespace found in the document). Furthermore, its scope is global to the entire document and it cannot be redefined.

In practice, the xsi:noNamespaceSchemaLocation attribute will often be located in the document element. We can locate a schema named first.xsd in the same directory as the instance document in the example used in this chapter (which doesn't use any namespace) as:

<library xsi:noNamespaceSchemaLocation="first.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  .../...
</library>

To reference schemas with a target namespace, lists of URIs must be provided in xsi:schemaLocation attributes. This list is, in fact, a list of pairs of URIs. In each pair, the first URI identifies a target namespace and the second URI identifies the location of a schema with this target namespace. The same rule that applied to xsi:noNamespaceSchemaLocation applies to the location of this attribute: for each target namespace for which you want to provide a schema location, you need to provide this information before a schema processor needs it to do its job.

To illustrate the usage of the xsi:schemaLocation attribute, let's examine a simplified version of an example with the two namespaces that are described in Chapter 10, "Controlling Namespaces". The instance document is as follows (without any xsi:schemaLocation):

<?xml version="1.0"?>
<book id="b0836217462" xmlns="http://dyomedea.com/ns/library"
  xmlns:mkt="http://dyomedea.com/ns/library/mkt">
  <title>
    Being a Dog Is a Full-Time Job
  </title>
  <author>
    Charles M Schulz
  </author>
  <mkt:cover>
    Paperback
  </mkt:cover>
  <mkt:pages>
    128
  </mkt:pages>
</book>

We have an open schema for the main namespaces that allows arbitrary elements from other namespaces, such as:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:lib="http://dyomedea.com/ns/library"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="book">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="title" type="xs:token"/>
        <xs:element name="author" type="xs:token"
          maxOccurs="unbounded"/>
        <xs:any namespace="##other" processContents="lax"
          minOccurs="0"maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:ID"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

We also have a schema for the two elements that belong to the namespace for our marketing department:

<?xml version="1.0"?>
<xs:schema targetNamespace="http://dyomedea.com/ns/library/mkt"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="cover" type="xs:NMTOKEN"/>
  <xs:element name="pages" type="xs:nonNegativeInteger"/>
</xs:schema>

This example was carefully chosen to have two schemas for two namespaces that are not linked together: there is no reference to the marketing namespace from the library and vice versa. We have several possibilities, depending on the hints given to the schema processor. If we validate the instance document without any xsi:schemaLocation attribute or any other information from the command line or application, the schema validator is left alone to try to locate a schema. Depending on the algorithm implemented in the processor, it may try to dereference the namespace URIs of the document element (i.e., to attempt to load a resource that may be available here). In our case, this is http://dyomedea.com/ns/library. If there is no schema there, then it can't say whether the document is valid or not. Alternatively, the schema processor can try to dereference a RDDL document at this location, hoping to find a reference to a schema in the RDDL document.

More typically, the author of the instance document may be kind enough to give the location of the schema for the library namespace -- for instance:

<?xml version="1.0"?>
<book xsi:schemaLocation="http://dyomedea.com/ns/library library.xsd"
  id="b0836217462" xmlns="http://dyomedea.com/ns/library"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:mkt="http://dyomedea.com/ns/library/mkt">
  <title>
    Being a Dog Is a Full-Time Job
  </title>
  <author>
    Charles M Schulz
  </author>
  <mkt:cover>
    Paperback
  </mkt:cover>
  <mkt:pages>
    128
  </mkt:pages>
</book>

We don't have any choice above the location of xsi:schemaLocation because the information is needed to validate the document element. If we want to include it, we must locate it in the document element. This attribute contains a single pair of values separated by a space:

"http://dyomedea.com/ns/library library.xsd"

As mentioned, the first value identifies the target namespace while the second value identifies the schema location. With this information at hand, the processor can read the schema and start validating the instance document. However, when it finds the marketing namespace that matches the xs:any wildcard, with a processContents attribute asking to validate when possible, it may again try to find a schema for this namespace by dereferencing the namespace URI. If it can find a schema, it validates the elements from the marketing namespace; if not, it considers them valid, since the processContents attribute is set to "lax."

If we want to improve our chances of finding a schema for the marketing library, we can also define its location in a xsi:schemaLocation attribute. The place in the instance document that we can provide the information is in the first element that uses this namespace, such as:

<?xml version="1.0"?>
<book id="b0836217462"
  xsi:schemaLocation="http://dyomedea.com/ns/library library.xsd"
  xmlns="http://dyomedea.com/ns/library"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:mkt="http://dyomedea.com/ns/library/mkt">
  <title>
    Being a Dog Is a Full-Time Job
  </title>
  <author>
    Charles M Schulz
  </author>
  <mkt:cover xsi:schemaLocation="http://dyomedea.com/ns/library/mkt
    marketing.xsd">
    Paperback
  </mkt:cover>
  <mkt:pages>
    128
  </mkt:pages>
</book>

The schema processor now has all the hints it needs to retrieve the schemas for both namespaces, and it should fully validate the elements that belong to the marketing namespace. Alternatively, we can place all the schema location hints in the same xsi:schemaLocation attribute:

<?xml version="1.0"?>
<book id="b0836217462"
  xsi:schemaLocation="http://dyomedea.com/ns/library library.xsd
  http://dyomedea.com/ns/library/mkt marketing.xsd"
  xmlns="http://dyomedea.com/ns/library"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:mkt="http://dyomedea.com/ns/library/mkt">
  <title>
    Being a Dog Is a Full-Time Job
  </title>
  <author>
    Charles M Schulz
  </author>
  <mkt:cover>
    Paperback
  </mkt:cover>
  <mkt:pages>
    128
  </mkt:pages>
</book>
TIP: In these examples, we used relative URIs to locate the schemas. This is a good solution only if you assume that the schemas will be moved with the instance documents, and in many cases, absolute URIs will be preferred. When this is the case, they can be mapped back into local resources by a mechanism such as XML Catalogs (http://www.oasis-open.org/committees/entity/spec-2001-08-06.html), an OASIS specification that is implemented by an increasing number of tools.

Previous | Home | Next
10.11. Allowing Any Elements or Attributes from a Particular Namespace | Book Index | 11.2. Defining Element Types
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Defining Element Types (XML Schema)

 XML SchemaSearch this book

11.2. Defining Element Types

The next hint we can give to a schema processor helps it to determine the simple or complex type of an element. A schema validator usually guesses the type of an element from its name and the description of the content model of its parent. This guess can be overridden by the author of an instance document through the xsi:type attribute, as long as this new type is a derivation by restriction or extension of the type defined in the schema document. Since this type is defined using an attribute in the instance document, the definition is possible only for elements. (Attributes can't have attributes!)

At this point, the question is "why would we want to define a type in the instance document?". The answer is somewhat different for simple and complex types, as well as whether we are interested in a schema for validation purposes only or for data binding.

11.2.1. Defining Simple Types

An element (or attribute) can belong to several different simple types and a derivation by union is generally a good way to let a schema validator pick the right type without having to use xsi:type. We can go quite far in this direction. To illustrate this, now that we've seen both the principles of the derivation by union and the patterns, let's define a union type that can accept ISO 8601 dates, common English ("April 2nd, 1998"), and French ("2 avril 1998") formats. We can start by defining a ISO date without a time zone as discussed in Chapter 4, "Using Predefined Simple Datatypes":

<xs:simpleType name="dateISO">
  <xs:restriction base="xs:date">
    <xs:pattern value="[^:Z]*"/>
  </xs:restriction>
</xs:simpleType>

The English format can be described using different patterns for the months that have 31, 30, and 28 days (we do not cover leap years in this example). The following definition should give a fairly good approximation for years after AD with a maximum of four digits (the lines are split for readability but the patterns are on a single line):

<xs:simpleType name="EnglishDate">
  <xs:restriction base="xs:token">
    <xs:pattern
      value="(January|March|May|July|August|October|December)
      ([1-3]?1st|[12]?2nd|[12]?3rd|(30|[12]?[4-9])th),[0-9]{0,4}"/>
    <xs:pattern value="February
      ([1-2]?1st|[12]?2nd|[12]?3rd|[12]?[4-9]th),[0-9]{0,4}"/>
    <xs:pattern value="(April|June|September|November)
      ([1-2]?1st|[12]?2nd|[12]?3rd|(30|[12]?[4-9])th),[0-9]{0,4}"/>
  </xs:restriction>
</xs:simpleType>

After the English format, the French one looks simple! The same principle can be applied (line breaks have been added to the patterns for readability):

<xs:simpleType name="dateFrançaise">
  <xs:restrictionbase="xs:token">
    <xs:pattern value="(ler|[1-3][01]|[12]?[2-9])
        (janvier|mars|mai|juillet|aout|octobre|décembre)\d{0,4}"/>
    <xs:pattern value="(ler|[12][01]|[12]?[2-9]) février \d{0,4}"/>
    <xs:pattern value="(ler|[12][01]|[12]?[2-9]|30)
        (avril|juin|septembre|novembre)\d{0,4}"/>
  <xs:restriction>
</xs:simpleType>

The last step is to derive our type by union as follows:

<xs:simpleType name="anydate">
  <xs:union memberTypes="dateISO EnglishDate dateFrançaise"/>
</xs:simpleType>

We now have a simple type that will accept three different date formats. A schema processor should not only validate these three formats, but it should also mention which type it has recognized in the PSVI. We've achieved this without adding anything in the instance document. Why do we want to give the information in the instance document, then? There are a couple of reasons for this. The first is that we want to convey the information to an application that is not able to get it from the PSVI. This is often the case with current tools, since there is no specification of the interface that will allow an application to read the PSVI. This reason does not apply if we are interested only in validation, but may be important if we want to avoid making applications that manipulate our instance documents check which format they get.

The second reason isn't shown in the previous example. Because the lexical spaces of the different member types have no overlap, there is no confusion possible. This is not always the case. We may want to override the choice made by the schema validator, or even to use a generic "universal" type in the schema and rely on the instance documents to define which type is used. One type of application that is a good prospect for this scenario is protocol or binding applications for which XML is only a transient serialization format. These applications often need to define generic elements that can be used to hold parameters of any type.

For instance, a schema-based XML-RPC can be defined by the first example of the XML-RPC specification:

<methodCall>
  <methodName>
    examples.getStateName
  </methodName>
  <params>
    <param>
      <value>
        <i4>
          41
        </i4>
      </value>
    </param>
  </params>
</methodCall>

In an imaginary W3C XML Schema-aware version of XML-RPC, this could be replaced by:

<methodCall xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <methodName>
    examples.getStateName
  </methodName>
  <params>
    <param>
      <value xsi:type="xs:int">
        41
      </value>
    </param>
  </params>
</methodCall>
TIP: Without imposing such usage, SOAP allows this practice in the case of " Polymorphic Accessor." The W3C Working Draft of 2 October 2001 mentions this:

Many languages allow accessors that can polymorphically access values of several types, each type being available at run time. A polymorphic accessor instance MUST contain an xsi:type attribute that describes the type of the actual value. For example, a polymorphic accessor named "cost" with a value of type xs:float would be encoded as follows:

<cost xsi:type="xs:float"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  29.95
</cost>

11.2.2. Defining Complex Types

Although the mechanism to forcibly identify a complex type in an instance document is similar to the one we saw for simple types, the motivations for using it can be completely different. If it is still possible to use this feature in case of a polymorphic accessor, to use the terminology taken by the W3C Protocols WG, this is probably a relatively marginal use case for complex types that do not have extension by union. The equivalent to the simple type derivation by union for complex types would be the ability to define several content models for a same element and to let the schema processor try all these content models and keep the first one that matches the fragment of the instance document. This would indeed be a nice feature, but this is exactly what the Consistent Declaration and Unique Particle Attribution rules explicitly forbid. Therefore, xsi:type for complex types has no competition in the schema itself and is often used as a workaround against these rules.

Another way to understand this is to consider this feature a hint given to the schema processor that will allow it to disambiguate the choice it could have and avoid violating one of these rules. A typical use is to work around the Unique Particle Attribution rule to allow two different content models for the same element. We have seen in Chapter 9, "Defining Uniqueness, Keys, and Key References" that xs:key might be used to allow our title to be expressed either as an attribute or as an element, but this workaround doesn't help if we want to allow more complex combinations, such as either a title expressed as an attribute or one or more titles expressed as elements:

<book id="b0836217462" available="true" title="Being a Dog Is a
  Full-TimeJob">
  .../...
</book>

or:

<book id="b0836217462" available="true" type="bookTitleElements">
  <isbn>
    0836217462
  </isbn>
  <title lang="en">
    Being a Dog Is a Full-Time Job
  </title>
  <title lang="fr">
    Etre un chien est un travail à plein temps.
  </title>
  .../...
</book>

To do so, we will define a base type that is a superset of both content models:

<xs:complexType name="bookBase">
  <xs:sequence>
    <xs:element ref="isbn"/>
    <xs:element ref="title" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="character" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
  <xs:attribute ref="title"/>
  <xs:attribute ref="available"/>
</xs:complexType>

This base type accepts book elements with optional titles defined as attributes or elements. We can derive by restriction a first type which will accept only title attributes:

<xs:complexType name="bookTitleAttribute">
  <xs:complexContent>
    <xs:restriction base="bookBase">
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="author" minOccurs="0"
          maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

We can derive a second type that accepts only titles defined as one or more title elements:

<xs:complexType name="bookTitleElements">
  <xs:complexContent>
    <xs:restriction base="bookBase">
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title" minOccurs="1" maxOccurs="unbounded"/>
        <xs:element ref="author" minOccurs="0"
          maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute ref="title" use="prohibited"/>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

Now that we have all our building blocks, we can use them in the schema to define the book element as having a type bookBase:

<xs:element name="book" type="bookBase"/>

Then we can use them in the instance documents to declare which derived type we are using:

<book id="b0836217462" available="true" xsi:type="bookTitleElements">
  <isbn>
    0836217462
  </isbn>
  <title lang="en">
    Being a Dog Is a Full-Time Job
  </title>
  <title lang="fr">
    Etre un chien est un travail à plein temps.
  </title>
  .../...
</book>

or:

<book id="b0836217462" available="true" title="Being a Dog Is a
  Full-TimeJob" xsi:type="bookTitleAttribute">
  .../...
</book>

However, this allows instance documents to use the base type, which may not be something we want, since we can have either no title at all or an attribute and one or more elements (something we want to avoid). We can forbid the use of the base type by defining it as "abstract." Setting this attribute of the complex type definition blocks instance documents from using it. They will have to specify one of its derived types through a xsi:type attribute.

The feature of the abstract attribute is symmetrical to the block attribute we have already seen. While the block attribute was prevented from further derivation, abstract requires a derivation. The final definition of our base complex type is then:

<xs:complexType name="bookBase" abstract="true">
  <xs:sequence>
    <xs:element ref="isbn"/>
    <xs:element ref="title" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="character" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
  <xs:attribute ref="title"/>
  <xs:attribute ref="available"/>
</xs:complexType>
Previous | Home | Next
11. Referencing Schemas and Schema Datatypes in XML Documents | Book Index | 11.3. Defining Nil (Null) Values
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Defining Nil (Null) Values (XML Schema)

 XML SchemaSearch this book

11.3. Defining Nil (Null) Values

This feature was introduced into W3C XML Schema to map the notion of "null" values, which is one of the core requirements of the relational model as defined by Codd. The W3C makes a subtle distinction between an empty element and an element that is null (or "nil," to use the name chosen for the xsi attribute that conveys the information). The two notions are considered different, but related (an empty element is not always null, but a null element must be empty).

The fact that an element can be nil is defined in the element definition and short-circuits all the content type definitions. It may be used for simple types, complex content, and simple content complex types; and it may be used even if the content model forbids empty contents. In this case, the "nillable" character of the element takes precedence over the content model, and empty contents will be accepted when marked as nil. Again, since the fact that a value is nil is expressed using an attribute in the instance document, the mechanism does not apply to attributes that cannot have a null value.

One last thing to note before we look at some examples is that attributes are not affected by the fact that an element is nil, and they are still allowed or required if defined in the element.

The usage of this feature is rather simple. For example, if we want to allow nil values for the author of a book, we just set the nillable attribute in the element definition (nillable attributes cannot be specified in an element reference):

<xs:element name="author" nillable="true">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

This declaration lets us declare null values in the instance documents:

<author xsi:nil="true"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>

This is possible despite the fact that the author element had not been declared as allowing empty content.

NOTE: The difference between nil and empty values is very slim. Assuming the author element is defined as nillable and has a content model allowing empty contents, the difference between:

<author xsi:nil="true"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>

and:

<author/>

is only a xsi:nil flag in the PSVI.

Previous | Home | Next
11.2. Defining Element Types | Book Index | 11.4. Beware the Intrusive Nature of These Features...
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Beware the Intrusive Nature of These Features... (XML Schema)

 XML SchemaSearch this book

11.4. Beware the Intrusive Nature of These Features...

The title of this section says it all! Using the xsi namespace creates a dependency between the instance documents and a specific schema technology, and this dependency is questionable as such. One of the major strengths of XML is the decoupling between the information available in the documents and the applications that retrieve and process this information. If we consider a schema validator one of these applications, including schema features in the instance documents breaks this rule.

Another reason why the use of these features is questionable is we've learned that the life span of data or information is longer than the life span of the applications. We can suppose in many cases that the XML documents we create today will outlive the W3C XML Schema 1.0 technology. The Working Group started thinking about XML Schema 1.1 before 1.0 became a Recommendation, and these dependencies are potential migration issues. XML 1.0 has already seen two generations of schema languages (DTD and W3C XML Schema 1.0), and functionality bundled with the DTDs that has not been included into W3C XML Schema (such as external parsed and unparsed entities) are at risk of becoming deprecated in common use. Furthermore, there are other schema technologies; integrating your documents tightly with W3C XML Schema may close some doors that might be needed for your application.

On the other hand, these attributes are available in the instance documents for nonschema-aware applications, and you can easily process a xsi:type or xsi:nil attribute using a simple XSLT transformation.

The last thing to note about these attributes is they are attributes! This may seem obvious, but attributes can't be included within attributes. This means that one cannot qualify an attribute with xsi:nil or xsi:type. This introduces a new differentiation between elements and attributes which XML application designers might need to consider.

Previous | Home | Next
11.3. Defining Nil (Null) Values | Book Index | 12. Creating More Building Blocks Using Object-Oriented Features
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Creating More Building Blocks Using Object-Oriented Features (XML Schema)

 XML SchemaSearch this book

Chapter 12. Creating More Building Blocks Using Object-Oriented Features

Contents:

Substitution Groups
Controlling Derivations

We have already seen many features that have been borrowed from object-oriented languages. In this chapter, we will see substitution groups (similar to subclasses), abstract elements and datatypes (similar to abstract classes), and final datatypes (similar to final classes).

12.1. Substitution Groups

In many cases, a vocabulary needs the ability to accept a variety of different content models. We have two options: we can try to do it using a single generic element name, or we can define a schema smart enough to deal with the possible content model. Since we cannot define multiple different content models for the same element (because of the Consistent Declaration Rule), we can either use xsi:type attributes in the instance documents, or we can define a content model wide enough to accommodate all the possibilities. Such a model would likely be wide enough to also accept combinations that we do not want.

The easiest solution for accommodating different types with W3C XML Schema is to use a different element name for each case. We already saw that the xs:choice(outside a group) compositor allows us to build such constructs where a node in an instance document can accept an element chosen in a list. However, this list is fixed in the complex type definition. We have also seen that this list cannot be extended, since the rules for complex type derivations by extension do not allow it. Substitution groups offer a flexible way to create xs:choice(outside a group) compositors out of single element definitions or references, as well as a way to extend them. More simply, they are lists of elements that can be used in place of each other within documents. One important thing to note before we start, though, is that substitution groups apply only to global elements.

Substitution groups can be seen as extensible element groups. Before introducing them, let's look again at the "traditional" element groups to highlight the differences between these two concepts. Since the Recommendation is especially fuzzy on the extensibility of element groups and the restriction of substitution groups, I have chosen to present a conservative interpretation, which should be free of interoperability issues. I will discuss the different interpretations at the end of the chapter.

12.1.1. Using a "Traditional" Group

Let's come back to the definition of a name. (After all, universal names are one of the most controversial subjects in normalization spheres, so it's no surprise that we can use them as examples!) Instead of playing with datatypes, we may just use different element names, and say that a name is either a simple name, such as:

<simple-name>
  Snoopy
</simple-name>

or a full name, such as:

<full-name>
  <last>
    Schulz
  </last>
  <first>
    Charles
  </first>
  <middle>
    M
  </middle>
</full-name>

We have already seen how we can define a flexible schema that will match these documents. A good idea is to create a group with a xs:choice compositor that allows one of those two elements and can be reused in all the elements in which a name needs to be included. The logical steps are to define the two elements (full name and simple name), to create a group, and to use it in the definition of the author and character elements:

<xs:element name="full-name">
  <xs:complexType>
    <xs:all>
      <xs:element name="first" type="string32" minOccurs="0"/>
      <xs:element name="middle" type="string32" minOccurs="0"/>
      <xs:element name="last" type="string32"/>
    </xs:all>
  </xs:complexType>
</xs:element>

<xs:element name="simple-name" type="string32"/>

<xs:group name="name">
  <xs:choice>
    <xs:element ref="simple-name"/>
    <xs:element ref="full-name"/>
  </xs:choice>
</xs:group>

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

<xs:element name="character">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="qualification"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

Note that we are able to use xs:all in this case because the elements involved are isolated in the full name element. This is also a good time to mention that xs:all doesn't mean the order is not significant, but only that all the combinations are valid. In this case, writing the following:

<full-name>
  <first>
    Eric
  </first>
  <last>
    van der Vlist
  </last>
</full-name>

or:

<full-name>
  <last>
    van der Vlist
  </last>
  <first>
    Eric
  </first>
</full-name>

may express whether I prefer to be called "Eric van der Vlist" or "van der Vlist Eric." Applications that want access to the components of this full-name can still have it, but those that need a full-name must respect the document order.

12.1.2. Substitution Groups

12.1.2.1. Using substitution groups

Let's see how we can define the same content model using substitution groups. The first thing to do is to define an element that both full-name and simple name can be derived from. In this case, we have a simple type on one hand and a complex type with complex content on the other, and we cannot find a type that can be extended to both. We have no other choice but to start with the universal type, which accepts any content model. Known as xs:anyType, this very special type is also the default value when no type is specified, and we can define a generic name element without giving any type definition to keep it as open as possible:

<xs:element name="name"/>

This element will be what is known as the head of the substitution group. Without declaring anything on this head element, other elements can declare that they can be used wherever the head element is referenced in the schema. These elements are known as the members of the substitution group. The one restriction on the members is their types must be valid derivations of the type of the head element. This declaration is made through a substitutionGroup attribute that references the head element in each interchangeable element--for instance:

<xs:element name="simple-name" type="string32"
  substitutionGroup="name"/>

<xs:element name="full-name" substitutionGroup="name">
  <xs:complexType>
    <xs:all>
      <xs:element name="first" type="string32" minOccurs="0"/>
      <xs:element name="middle" type="string32" minOccurs="0"/>
      <xs:element name="last" type="string32"/>
    </xs:all>
  </xs:complexType>
</xs:element>

The effect of these declarations is these two elements can be used every time the head is used in the schema, such as in the definition of the character and author elements:

<xs:element name="character">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="qualification"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

12.1.2.2. Abstract elements

If we keep our schema like we've just seen it, the usage of the head in the instance documents is allowed, and since our head element allows any content, this is probably not something we would want. We need to use a mechanism similar to the abstract types we saw when we encountered the same kind of problem with xsi:type in Chapter 7, "Creating Complex Datatypes". We will define the head element as abstract using the abstract attribute in the definition of the head element, which then becomes:

<xs:element name="name" abstract="true"/>

12.1.2.3. Trees of substitution groups

What if our French offices define a composed-name element that is similar to the full name without its middle subelement? We may just add this element directly to our substitution group, but defining it as having the name element as its head will not clearly show the similarities between this new element and the full-name element. Furthermore, some applications might need to specify that they accept either full-name or composed-name. The solution is to use full-name as the head of a new substitution group. To do this, we need to define the type of the full-name element as global to show the explicit derivation between the two elements:

<xs:complexType name="full-name-type">
  <xs:all>
    <xs:element name="first" type="string32" minOccurs="0"/>
    <xs:element name="middle" type="string32" minOccurs="0"/>
    <xs:element name="last" type="string32"/>
  </xs:all>
</xs:complexType>

<xs:element name="full-name" substitutionGroup="name"
  type="full-name-type"/>

<xs:element name="composed-name" substitutionGroup="full-name">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="full-name-type">
        <xs:all>
          <xs:element name="first" type="string32" minOccurs="0"/>
          <xs:element name="last" type="string32"/>
        </xs:all>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

We have now defined not only two substitution groups (with the name and full name heads), but also a tree of substitution groups, since the allowed substitutions for name will include both full name and simple name, but also composed-name!

12.1.3. Traditional Declarations or Substitution Groups?

If we look back at the two solutions that we used to solve the same issue, we see that substitution groups are more extensible than a traditional group which uses a xs:choice compositor. While the element group can only be derived using a xs:redefine inclusion, the substitution group can be extended with new possible elements by just defining them. These elements can be defined in any namespace; the only constraint is that their types must be the same or valid derivations of the head element's type. (This restriction is justified to ensure that applications are not too surprised by an unexpected content model.)

There is another difference to note, though. We have seen that the derivation of a content model using a xs:choice compositor cannot extend the scope of the choice and add new alternatives. The situation for substitution groups is almost the opposite. Although the Recommendation says that substitution groups should be validated as choices, it doesn't define the order of the elements in the equivalent choice. I do not advise restricting substitution groups in practice, since it may lead to interoperability issues between schema processors.

We then have a paradoxical situation where one of the mechanisms (xs:all) can only be restricted while the other (substitution groups) can only be extended, even though the Recommendation states that these two mechanisms are equivalent as far as validation is involved. This characteristic needs to be taken into account when choosing between them.

The differences between these two features are summarized in Table 12-1, "Not advised" stands for "may work with some schema processors but relies on a liberal interpretation of the Recommendation, which may lead to interoperability issues."

Table 12-1. Element versus substitution groups

Feature | Element groups with a xs:choice(outside a group) compositor. | Substitution groups.
Definition | Centralized, using xs:group(definition) and xs:choice(outside a group). | Spread over global element definitions, using the substitutionGroup attribute.
Constraints on the choices | No constraint: the elements can be totally different. | The type of the elements needs to be an explicit derivation of the type of the head.
Allows global elements | Yes. | Yes.
Allows local elements | Yes. | No.
Restriction to remove choices | Yes, though xs:redefine. | Not advised.
Extension to add choices | Not advised. | Yes, by adding new elements with the same head element.
Extension to add new elements in sequence | Yes, through xs:redefine. | No.

12.1.4. Fuzzy Recommendation

Both the extension of xs:choice during element group redefinitions and the restriction of substitution groups are very fuzzy in the Recommendation and require some explanation.

12.1.4.1. Extension of xs:choice through group redefinitions

If we return to our group that is defined as:

<xs:group name="name">
  <xs:choice>
    <xs:element ref="simple-name"/>
    <xs:element ref="full-name"/>
  </xs:choice>
</xs:group>

There doesn't seem to be anything in the recommendation that explicitly forbids redefinition of this group to add another element in the choice by writing:

<xs:redefine schemaLocation="foo.xsd">
  <xs:group name="name">
    <xs:choice>
      <xs:group ref="name"/>
      <xs:element ref="bar"/>
    </xs:choice>
  </xs:group>
</xs:redefine>

However, the effect of this redefinition is to allow a new element (bar) to be accepted instead of simple-name and full-name. Although this would be a nice feature, the principles of redefinition by restriction (i.e., when the content of the group is restricted during a restriction) are the same as the principles of the complex type derivation by restriction. The intention of the Working Group seems to be to define the features of redefinitions by extension after the complex type derivation by extension, which explicitly forbids the addition of new particles in a xs:choice(outside a group).

Although some schema processors do support this feature and some specialists consider it fine, I do not advise using it, since it seems to violate the intent (if not the wording) of the Recommendation.

12.1.4.2. Restricting substitution groups

The restriction of the substitution groups is quite the opposite. The intent of the Working Group seems to be to allow such restrictions while the wording of the Recommendation makes its result undefined.

The Recommendation clearly specifies that during the check to determine if a particle is a valid restriction of another particle, substitution groups should be treated as xs:choice, which is a clear indication that substitution groups could be restricted through complex type derivations by restriction. To illustrate this, let's take the definition of the complex type of the element author, using the substitution group whose head is name, as defined previously:

<xs:complexType name="authorType">
  <xs:sequence>
    <xs:choice>
      <xs:element ref="name"/>
      <xs:element ref="simple-name"/>
      <xs:element ref="full-name"/>
    </xs:choice>
    <xs:element ref="born"/>
    <xs:element ref="dead" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
</xs:complexType>

If substitution groups are treated like xs:choice, and assuming that our head isn't defined as abstract, this definition is equivalent to:

<xs:complexType name="authorType">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="born"/>
    <xs:element ref="dead" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
</xs:complexType>

It should be possible to derive this complex type by restriction by writing, for instance:

<xs:complexType name="restrictedAuthorType">
  <xs:complexContent>
    <xs:restriction base="authorType">
      <xs:sequence>
        <xs:choice>
          <xs:element ref="simple-name"/>
          <xs:element ref="full-name"/>
        </xs:choice>
        <xs:element ref="born"/>
        <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

However, the Recommendation also states that during the derivation by restriction of a xs:choice compositor, "there is a complete order-preserving functional mapping" between the particles used to define the derived and original xs:choice. However, it does not define the order of the particles when substitution groups are mapped into xs:choice. Depending on the order chosen by the schema validator to build the xs:choice out of the substitution group, our derivation can thus be either valid or invalid!

Previous | Home | Next
11.4. Beware the Intrusive Nature of These Features... | Book Index | 12.2. Controlling Derivations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Controlling Derivations (XML Schema)

 XML SchemaSearch this book

12.2. Controlling Derivations

It's time to come back to the features already discussed--and to introduce new ones--to explain how to control the usage of the derivations of the different compositors. Basically, these features are attributes that allow you to block further derivation or, on the contrary, allow you to require derivations, but their granularity varies depending on the compositor being used.

12.2.1. Attributes

There are no substitution groups for attributes and no mechanism to derive them or define their types in instance documents. This also means no features are needed to control their derivation. Actually, to be completely accurate, we need to note that it is possible to indirectly derive attributes through a derivation by restriction of their parent elements or redefinitions of attribute groups.

12.2.2. Elements

When speaking of a xs:element, we need to differentiate between global definitions and local definitions or references that behave differently in regard to derivation. Therefore, derivation control is maintained as shown in Table 12-2.

Table 12-2. Controls on element derivation

Attribute | Global element | Local element definition | Element reference
block | Yes | Yes | No
abstract | Yes | No | No
final | Yes | No | No

12.2.2.1. Block attribute

The block attribute controls type substitution in the instance documents through the xsi:type attribute and substitution groups. This single attribute holds a whitespace-separated list of tokens-- of "restriction," "extension," and "substitution"--or the special value #all (which means all three values together and the attribute's default value can be defined through the blockDefault attribute of the xs:schema document element).

The first two values (restriction and extension) control any substitution through xsi:type or substitution groups, and block the substitution by datatypes that are derived by restriction or extension from the datatype defined in the element declaration. The third (substitution) is specific to the substitution groups, and defines if an element from the substitution group (for which the element is the head) is allowed. Since only global elements can participate in a substitution group, the last value is clearly meaningful for global definitions only.

The fact that the block attribute is used both for type substitution and for substitution groups can be misleading, especially with the values restriction and extension, which act on both aspects. A simple example makes this more concrete. Let's say that we have a complex type definition (personType) describing a person as having a name, a mandatory birth date, and optional death date and qualification:

<xs:complexType name="personType">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="born"/>
    <xs:element ref="dead" minOccurs="0"/>
    <xs:element ref="qualification" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
</xs:complexType>

We can also derive by extension from this complex type a datatype that describes an author as a person with a list of books:

<xs:complexType name="authorType">
  <xs:complexContent>
    <xs:extension base="personType">
      <xs:sequence>
        <xs:element name="book" type="xs:token" minOccurs="1"
          maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

We can derive by restriction a datatype that describes a character as a person with no death date and a mandatory qualification:

<xs:complexType name="characterType">
  <xs:complexContent>
    <xs:restriction base="personType">
      <xs:sequence>
        <xs:element ref="name"/>
        <xs:element ref="born"/>
        <xs:element ref="qualification"/>
      </xs:sequence>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>

The first purpose of block attributes in element definitions is to control the type substitution through xsi:type, which controls substitutions in the instance documents such as the following (if the person element is defined as having a type personType):

<person xsi:type="authorType" id="CMS"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <name>
    Charles M Schulz
  </name>
  <born>
    1922-11-26
  </born>
  <dead>
    2000-02-12
  </dead>
  <book>
    Being a Dog Is a Full-Time Job
  </book>
</person>

<person xsi:type="characterType" id="Snoopy"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <name>
    Snoopy
  </name>
  <born>
    1950-10-04
  </born>
  <qualification>
    extroverted beagle
  </qualification>
</person>

The first substitution uses authorType, which is derived by extension from personType, and can be blocked by specifying a block attribute including the value extension. The second one uses characterType, which is derived by restriction from personType and can be blocked by specifying a block attribute including the value restriction. Both can be blocked together by specifying a block attribute with the value #all.

TIP: This example shows that the impact of both derivation types on the applications is very different. A type substitution by restriction has virtually no impact on the applications, since the content model matching the restricted type must also match the original type. Alternatively, a type substitution by extension allows content models not allowed by the original type (such as the addition of the book element in the previous example), and the risk of breaking applications that do not expect these additions is higher. Thus, a conservative attitude might be to include extension in the default values of block in your schemas.

That being said, we've seen only one side of the block attribute. It serves a second, different, yet not independent, purpose and also restricts the usage of substitution groups. To illustrate this purpose, let's define three different element members of a substitution group whose head is person. There is also a fourth element member, which is just a synonym for the element person and has the same type:

<xs:element name="person" type="personType"/>

<xs:element name="author" type="authorType"
  substitutionGroup="person"/>

<xs:element name="character" type="characterType"
  substitutionGroup="person"/>

<xs:element name="human" type="personType"
  substitutionGroup="person"/>

After defining this substitution group and without blocking anything, we allow not only block and the type substitutions seen above, but also element substitutions, such as:

<author id="CMS">
  <name>
    Charles M Schulz
  </name>
  <born>
    1922-11-26
  </born>
  <dead>
    2000-02-12
  </dead>
  <book>
    Being a Dog Is a Full-Time Job
  </book>
</author>

<character id="PP">
  <name>
    Peppermint Patty
  </name>
  <born>
    1966-08-22
  </born>
  <qualification>
    bold, brash and tomboyish
  </qualification>
</character>
<human id="CMS.">
  <name>
    Charles M Schulz
  </name>
  <born>
    1922-11-26
  </born>
  <dead>
    2000-02-12
  </dead>
</human>

The first substitution is by an element of the substitution group whose type is derived by extension from the head. It can be blocked by specifying a block attribute including the value extension. The second one is a substitution by an element of the substitution group whose type is derived by restriction from the head. It can be blocked by specifying a block attribute including the value restriction. Those two are similar to the two substitutions given as examples for the type substitutions, with the difference that the element name is now used to differentiate the type instead of the xsi:type attribute. The third substitution is new, since the type is the same as the type of the head, and it is blocked only by specifying a block attribute that includes the value substitution. Note that including substitution in a block attribute blocks any element substitution, while stating that not all the combinations are possible to express. To block everything, you would define author as:

<xs:element name="author" type="authorType" block="#all"/>

I don't want to leave you with the impression than you can't block type substitution without blocking substitution groups. We need to mention that type substitution can also be blocked on complex type definitions, which is covered later on in this chapter in Section 12.2.3, "Complex Types".

12.2.2.2. Final elements

Like block, final has an impact on substitution groups, but works on a different level: constraining the schema itself while block constrains the instance documents. final can take a list of restriction and extension or the special value #all, and its default value can be defined using the finalDefault attribute of xs:schema. However, a substitution value is not necessary, since unlike block, final is about substitution groups, and #all can block all the substitutions (and only the substitutions).

final's effect is more radical than block: while block blocks the effects of the usage of substitution groups, final prohibits the usage of the element as a head of a substitution group. If the person element is defined with a final attribute set, it isn't possible to use it as head of a substitution group for either author, character, or both.

12.2.2.3. Abstract elements

The last attribute is abstract, which is the opposite of block. It prohibits the element from being used directly in an instance document and must be substituted through a substitution group. Defining the person element as abstract forbids its use in the instance documents. You must use one of the elements from its substitution group (such as author, character, or even human).

12.2.3. Complex Types

The attributes are the same for complex types as they are for elements, but their meaning is slightly different since one of them (block) operates on the substitution of the elements that use the datatype (as we have seen in the previous section). The others (final and abstract) work on the derivation of the complex type itself.

12.2.3.1. Blocking complex types

When the block attribute is used on complex type definitions, the type substitutions in the elements defined with this type are still blocked. The difference is that this time block doesn't act on element substitutions through substitution groups, but it does act on type substitutions. These block attributes can be seen as electric switches installed in a series, and each of them can block the derivation in its own "line," as shown in Figure 12-1.

Figure 12-1. Block attribute on elements and complex type definitions

For each restriction and extension, the substitutions can first be switched off for both element and type substitutions by the block attribute of the element definition, and then the type substitutions can be blocked by the block attribute of the complex type definition. The value substitution in the block attribute of the element definition acts as a global switch to block all element substitutions, including a third line, which allows element substitutions with the same type and cannot be blocked separately.

The default value supplied in the schema element is used at both levels and may be different for the element and complex type definitions if they belong to different schemas and have a different xs:schema ancestor. The fact that the default value, when defined in the xs:schema ancestor, is applied to both levels means that it may block the derivation for those to levels (by "opening" two switches). To override such a nonempty default value, you need to define a block attribute both in the element and in the complex type definition.

12.2.3.2. Final complex types

The final attribute of complex type definitions controls whether the type can be derived by restriction and or extension to create new complex types.

Unlike the block attribute which is linked with its counterpart in the element definition, the final attribute in a complex type definition applies only to the complex type itself and, like the abstract attribute in the element definition, it acts at the level of the schema itself and has no impact on the instance documents.

12.2.3.3. Abstract complex types

The abstract attribute of complex type definitions works on the instance documents. When an abstract datatype is used to define elements, the type must be substituted in the instance documents through a xsi:type attribute.

We need to insist that abstract by itself doesn't mean that the complex type cannot be used in a content model. However, if it is used, it must be substituted through xsi:type in the instance documents. To define a complex type that will not be usable in a content model, we need to use both the final and block attributes; the complex type's only use is as a base type for derivations.

12.2.4. Simple Types

Simple types are, in fact, simpler, as far as controlling their derivation is concerned, since they can only be final. Their final attribute can take the values list, restriction, union, or the special value #all, which means the three of them and the default value are controlled through the finalDefault attribute of the schema element used for elements and complex types.

The fact that simple types cannot be abstract or block ed avoids potential issues when they are used to define attributes for which these notions are meaningless. When needed to define elements, a simple content complex type may be created using the simple type as its base. This complex type can hold the abstract and block attribute.

Even though final is the only attribute controlling simple type element derivation available on the xs:simpleType(global definition) element, a finer granularity of control may be achieved through the fixed attributes available on each of the facets (discussed in Chapter 5, "Creating Simple Datatypes").

12.2.5. Other Components and Redefinitions

Other components, such as attributes and element and attribute groups, cannot be directly derived. Attributes can't be derived at all and groups can only be derived through redefinitions; therefore, no control is available to control their derivation. Similarly, the redefinitions of groups through xs:redefine escape this feature and cannot be controlled at all. Although the recommendation is fuzzy on this aspect, it is safer to consider that the final attribute of the complex types applies also to their redefinitions, which are processed as implicit derivations.

TIP: The interpretation of the block attribute is subject to multiple interpretations. For the latest opinion of the W3C XML Schema Working Group, you should refer to the errata for the specification at http://www.w3.org/2001/05/xmlschema-errata.

Previous | Home | Next
12. Creating More Building Blocks Using Object-Oriented Features | Book Index | 13. Creating Extensible Schemas
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Creating Extensible Schemas (XML Schema)

 XML SchemaSearch this book

Chapter 13. Creating Extensible Schemas

Contents:

Extensible Schemas
The Need for Open Schemas

The X from XML stands for "extensible." The goal of any schema language is to control and limit this extensibility to help the applications deal with it. Extensibility and schemas pursue two opposite goals. Carelessly written schemas may significantly reduce extensibility, and we need to keep this in mind when we design our own schemas.

Here again, we find the duality between the schema and the instance documents, and we need to distinguish between two different forms of extensibility. The extensibility of the schema, is the ability to reuse its components to create other schemas, while the extensibility of the vocabulary, is the ability to add or modify the content models with a minimal impact on the applications, and is, in fact, the openness of the schema.

13.1. Extensible Schemas

The extensibility of a schema is essentially determined by its style, the choice of which components (elements and attributes, element and attribute groups, and simple and complex types) have been made global, the use of the final and fixed attributes, and the optional division of these components over different schema documents. We need to have a look at these three factors.

13.1.1. Global Components

A simple example is often better than a long explanation, so to illustrate the differences between the different schema styles, we will take some examples out of our library and study complex and simple type elements and attributes.

13.1.1.1. Elements

Let's consider the definition of the book element in the context of our library. We have four different basic ways of defining this element, and they all will validate the same set of instance elements--but not the same set of instance documents, since exposing an element as global allows its use as a document element. We can use a Russian doll design and define the book element and its type locally within the library element (I have used the same Russian doll design for the book's child elements to keep the schema concise as we will focus on the definition of book for this example):

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="book" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="isbn"/>
            <xs:element ref="title"/>
            <xs:element ref="author" minOccurs="0"
              maxOccurs="unbounded"/>
            <xs:element ref="character" minOccurs="0"
              maxOccurs="unbounded"/>
          </xs:sequence>
          <xs:attribute ref="id"/>
          <xs:attribute ref="available"/>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:element>

We can also define a global book element and reference it in the content model of our library:

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="isbn"/>
      <xs:element ref="title"/>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
    <xs:attribute ref="available"/>
  </xs:complexType>
</xs:element>

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="book" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

The third classical way is to define a complex type for the content model of our bookType element (note that I could have called it book, but I feel bookType is less confusing):

<xs:complexType name="bookType">
  <xs:sequence>
    <xs:element ref="isbn"/>
    <xs:element ref="title"/>
    <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="character" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
  <xs:attribute ref="available"/>
</xs:complexType>

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="book" type="bookType" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Finally, we can define a group containing our book element:

<xs:group name="bookGroup">
  <xs:sequence>
    <xs:element name="book">
      <xs:complexType>
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute ref="id"/>
        <xs:attribute ref="available"/>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:group>

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="bookGroup" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

These four basic styles can, of course, be combined. The more extreme example is as follows:

<xs:complexType name="bookType">
  <xs:sequence>
    <xs:element ref="isbn"/>
    <xs:element ref="title"/>
    <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element ref="character" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute ref="id"/>
  <xs:attribute ref="available"/>
</xs:complexType>

<xs:element name="book" type="bookType"/>

<xs:group name="bookGroup">
  <xs:sequence>
    <xs:element ref="book"/>
  </xs:sequence>
</xs:group>

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="bookGroup" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Although this example may seem excessive, we must acknowledge that it is also the most extensible, since it lets you use all the "reuse and derive" methods of our three compositors! Now that we've seen these four basic styles, let's see how they compare for re-usability and derivation.

The Russian doll is obviously the style that is the least extensible: both the definition of the book element and of its content model are local. They cannot be referenced to be reused in another part of a schema, they cannot be used as a document element, they cannot be modified by derivation, through xs:redefine, or through substitution groups. Using a Russian doll style here is thus a more efficient "blocking" feature than any blocking attribute is. Changing or reusing the book element or content model requires attaching a totally different schema to the instance document or using a xsi:type attribute in the instance document.

The flat model, which uses global element definitions, gives a basic level of flexibility since the element can now be reused in any location within any schema, can be used as a document element in an instance document, and can be used as the head of a substitution group. When used with a local complex type definition like in our example, the flat model doesn't allow you to change the content model of the book element. Among these three features, the flat model can be used as the head of a substitution group, and is the only one that can be blocked (using a block attribute). It can be used without restriction as a document element in an instance document or be used anywhere in a schema. We also need to note that elements cannot be redefined and that the content model of our book element cannot be changed, except through a substitution by means of xsi:type in the instance document.

The definition of a global complex type to describe the content model of the book element opens two different doors. The content model of the book element can now be reused to derive extended or restricted content models that may be used elsewhere, and the complex type can be redefined through xs:redefine. As seen in the previous chapter, the derivation can be blocked through the final attribute, but the redefinition cannot be controlled.

Last but not least, embedding the definition of the book element in a group allows the group to be reused elsewhere-- for example, in our flat model--but can hide the definition of the book element, if needed, to avoid its usage as a document element in instance documents. (Incidentally, it also blocks its usage as the head of a substitution group.) Defining a group also opens the possibility to redefine it through xs:redefine to change the number of occurrences of the element, to add new elements, or even to change its content model if a global complex type has been used. Using an element group this way is very similar to the approach of RELAX NG and gives a bit of its flexibility. We need to note, though, that element groups cannot be recursive; this can be a limitation to using element groups to define recursive content models with element groups, since a global element still needs to be defined for use in a reference. This can be a problem when we can't, or don't want to, use a global element--for instance, when we have two different recursive content models using the same element name with different contents.

Which approach is appropriate? There is no single definite answer to this question, but we know that each of this style has a different set of extensibility features. The choice between them or a combination of them has a major impact on the reusability and derivability of the definitions present in a schema. Table 13-1 may help with visualizing the differences between these styles, but keep in mind that combinations of all of them are allowed!

Table 13-1. Complex type styles

Style | Element reference | Content model reference | Derivation | Substitution group | Document element | Redefine
Russian doll | No | No | No | No | No | No
Flat | Yes | No | No | Yes | Yes | No
Complex type | No | Yes | Yes | No | No | Yes
Group | Yes | No | No | No | No | Yes

Simple type elements behave much like complex types, except that the complex type definitions are, of course, replaced by simple type definitions similar to those for attributes, discussed in the next section.

13.1.1.2. Attributes

As seen in Chapter 10, "Controlling Namespaces", attributes behave differently from elements in that most of the time they are unqualified. This means then that they cannot be globally defined. Otherwise, we have a similar situation with attributes, simple types, and attribute groups as we had with elements and complex types (the other exception is there is no equivalent in attribute land to substitution groups or xsi:type). If we take the definition of a lang attribute restricted to en or fr in the title element, we can have a Russian doll design in which the attribute and its type will be locally defined:

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:token">
        <xs:attribute name="lang">
          <xs:simpleType>
            <xs:restriction base="xs:language">
              <xs:enumeration value="en"/>
              <xs:enumeration value="fr"/>
            </xs:restriction>
          </xs:simpleType>
        </xs:attribute>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

We can also take a flat design in which the attribute is globally defined:

<xs:attribute name="lang">
  <xs:simpleType>
    <xs:restriction base="xs:language">
      <xs:enumeration value="en"/>
      <xs:enumeration value="fr"/>
    </xs:restriction>
  </xs:simpleType>
</xs:attribute>

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:token">
        <xs:attribute ref="lang"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

A global simple type can also be defined:

<xs:simpleType name="langType">
  <xs:restriction base="xs:language">
    <xs:enumeration value="en"/>
    <xs:enumeration value="fr"/>
  </xs:restriction>
</xs:simpleType>

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:token">
        <xs:attribute name="lang" type="langType"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

The attribute may be "hidden" in an attribute group:

<xs:attributeGroup name="langGroup">
  <xs:attribute name="lang">
    <xs:simpleType>
      <xs:restriction base="xs:language">
        <xs:enumeration value="en"/>
        <xs:enumeration value="fr"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>
</xs:attributeGroup>

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:token">
        <xs:attributeGroup ref="langGroup"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

All can this can be used together:

<xs:simpleType name="langType">
  <xs:restriction base="xs:language">
    <xs:enumeration value="en"/>
    <xs:enumeration value="fr"/>
  </xs:restriction>
</xs:simpleType>

<xs:attribute name="lang" type="langType"/>

<xs:attributeGroup name="langGroup">
  <xs:attribute ref="lang"/>
</xs:attributeGroup>

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:token">
        <xs:attributeGroup ref="langGroup"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

The impact of these design decisions is pretty much the same as those we've seen in complex type elements, except, of course, for substitution groups and usability as a document element. Table 13-2 explains the options these varying approaches provide.

Table 13-2. Attribute styles

Style | Attribute reference | Datatype reference | Derivation | Redefine
Russian doll | No | No | No | No
Flat | Yes | No | No | No
Simple type | No | Yes | Yes | Yes
Attribute group | Yes | No | No | Yes

13.1.2. final and fixed Attributes

These attributes were already covered in Chapter 12, "Creating More Building Blocks Using Object-Oriented Features", and they have an obvious impact on the re-usability of simple and complex type definitions since they can block some or all the further derivations. This category of features affects the flexibility of the schema itself. Their friends block and abstract are features that impact the openness of the schema and have no impact on the set of instance documents.

13.1.3. Splitting Schema Components

The last factor that acts on the flexibility and re-usability of our schema (and schema libraries) is the split of the components among different documents. Some schema designers have gone as far as possible in this direction and advise the location of each class or component in its own schema document, and to include and import the components needed to create a full schema. This may seem excessive, but provides a very fine granularity and allows a workaround of the limitations of xs:redefine. (If a component needs to be redefined, just leave out the old definition and write a new one.)

The biggest issue with such a design is probably the management of a number of different documents that can rapidly grow, and the many dependencies between these documents. These dependencies must be considered when designing libraries of schemas since they can be tough to track because the links between the included and including documents are multidirectional. A component within an included schema can reference components defined in any other schema processed by the schema processor.

We need to reexamine how a schema processor will build a global schema using all the imported, included, and redefine instructions it will find. The schema processor initially builds a big consolidated schema with all the components defined in all the schema documents it has processed. It then resolves the references between components after building this consolidated schema. Although this simple and powerful mechanism applies to inclusions without restriction, we will see that things can get nastier with imports and redefinitions. Let's start with the simplest case and move on to the processing of xs:include.

The semantic of xs:include is slightly different from the semantic of the include statements used in languages such as C, and it should be considered a conditional include. A xs:include is actually a request to read a schema if it has not already been read, to add all the component declarations found in this schema to the consolidated schema if they have not already been defined, to ignore the components found in the new schema that are already defined in the global schema if they are identical, and to raise an error if they are different. This means it is perfectly legitimate to create loops and multiple inclusions, either directly (schema A includes schema B, which includes schema C) or indirectly (schema A includes schema B and schema C, which includes schema B) and we can create inclusion paths as complex as we wish.

The meaning of xs:redefine is similar, except that some components can be redefined. When used, this difference is enough to break the possibility of creating loops in which a schema A redefines components of a schema B, which redefines or includes schema A. This restriction actually means that while we can speak of inclusion graphs, the redefinitions would instead form a tree. The process of including or redefining is recursive, however, and when we include (or redefine) a schema, we include the consolidated schema resulting from the included document rather than the document itself. We can still create inclusion loops within the branches of the redefinition tree (schema A can redefine schema B, which includes schema C, which includes schema B).

Some designers rely on the fact that when a schema without target namespaces is included (or redefined) in a schema with a target namespace, the included schema "borrows" the target namespace of its "includer." This feature, already mentioned in Chapter 10, "Controlling Namespaces", can be used to build "neutral" components with no namespaces that can be included and used as building blocks. Since these components take the namespace of the including schema like a chameleon takes the color of its environment, these schemas are called "chameleon schemas." Although this technique is simple and may be convenient in some cases, it can be confusing to define similar components (and, therefore, similar types and content models) in different namespaces instead of creating a common namespace for them, which would immediately identify these types and content models as identical.

xs:import behaves somewhat like xs:include: no redefinitions occur, which means that loops can be created where schema A (for namespace A) imports schema B (for namespace B), which itself imports schema A. It is important to note that xs:import serves two different purposes: it is an instruction to import a schema and a declaration that components from a namespace can be referenced. If schema A for namespace A imports schema B for namespace B, and if schema B needs to reference components from the namespace A, an xs:import statement must be included in schema B to declare that namespace A can be used (the schemaLocation attribute is optional and can be omitted in such cases).

After working through the three mechanisms (include, redefine, and import), we can mix all of them together and note that chameleon schemas can be used together with imports. In this case, the same imported chameleon can contribute several times to a global schema under different namespaces. If schema A for namespace A includes schema B with no namespace, and imports schema C for namespace C with includes schema B, the two inclusions of schema B belong to different namespaces and are considered different.

We now have all the elements to find innovative ways to mix inclusion and import graphs with redefinition trees. Keep in mind that simple is beautiful, and if we don't restrict ourselves, we humans might get lost well before our favorite schema processor!

Previous | Home | Next
12.2. Controlling Derivations | Book Index | 13.2. The Need for Open Schemas
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Need for Open Schemas (XML Schema)

 XML SchemaSearch this book

13.2. The Need for Open Schemas

In this chapter, we have discussed the features that may make the components defined in a schema reusable and flexible. This is definitely something to look for, but the impact of this effort is limited to the conception and maintenance of schemas and has little or no impact on the documents themselves. Even though application designers often see XML instance documents as tightly coupled to their schema and to the application for which they were first created, the life span of documents often exceeds the life span of their applications and schemas. XML documents or fragments are also often reused in other contexts, or serve as a container in which other information is added to create combinations that their original author never imagined.

Opening XML vocabularies to facilitate and control such evolution is quite challenging and goes beyond designing open schemas. It requires a different perspective on what an XML document is and has an impact on the way to code the applications (which should, for instance, be as tolerant as possible of unexpected elements and attributes). Grammar-based schemas, such as W3C XML Schema, have a basic problem with open vocabulary: everything that has not been explicitly allowed is forbidden, and the schemas are effectively "closed by default."

If we define the openness of a schema as the ability to modify the content model explicitly defined by the original schema without defining a new schema (we already covered the modification of the content model through a modification of the schema in the earlier section Section 13.1, "Extensible Schemas"), the tools that we have at hand are the substitution of type through xsi:type and the wildcards.

13.2.1. xsi:type

This kind of openness is allowed by default. A number of applications that use complex type derivations by extension, simple type derivations by union, or are using building blocks that have not specified block or blockDefault attributes, could probably be quite surprised by a substitution of type in the instance document.

Beyond these cases of "unexpected" openness, defining extension of the complex types used to define our elements that can be substituted in the instance documents through the xsi:type attribute can be an initial approach to open the schema while keeping control over surprises. In this case, the xsi:type attribute can be processed by the applications to determine which kind of extension they are facing.

13.2.2. Wildcards

Namespaces and wildcards are the most powerful tools we can use to create open schemas. Many vocabularies (such as W3C XML Schema) allow attributes that have a namespace other than the target namespace in all their elements, while keeping tight control over both unqualified attributes and attributes from their namespace. The values of the processContents attribute (lax, skip, and strict) can be adjusted to provide the level of openness you find best adapts to your schema. skip is completely open, strict requires supplying schemas for the namespaces that are found during the validation, and lax is a medium-level constraint.

Elements from other namespaces are often considered more intrusive than attributes and can be kept within containers. This is also what W3C XML Schema does for its own vocabulary, allowing any element from any namespace within xs:appinfo. Other vocabularies, such as Relax NG or RSS 1.0, simply accept any element that has a namespace other than its target namespace.

Although this practice allows a great deal of flexibility for embedding unexpected information in instance documents, note that the fact that there is no special value to specify "any namespace except those which are defined in the consolidated schema" limits its applicability when several namespaces are defined together.

13.2.3. And Substitution Groups?

If we mention xsi:type here, shouldn't we mention substitution groups also? Yes and no. Yes, because using them is a good way to define several alternative content models. However, no, because they can be used only if they have been defined in a schema. However, xsi:type can be used even if the schema makes no special provision for them. Despite similarities, substitution groups truly belong to flexible schemas much more than to open schemas.

Previous | Home | Next
13. Creating Extensible Schemas | Book Index | 14. Documenting Schemas
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Documenting Schemas (XML Schema)

 XML SchemaSearch this book

Chapter 14. Documenting Schemas

Contents:

Style Matters
The W3C XML Schema Annotation Element
Foreign Attributes
XML 1.0 Comments
Which One and What For?

The issue of documenting schemas--or any machine readable language--goes beyond simple additions of comments. The real challenge is to create schemas that are readable both directly by looking at their source code and by documentation extraction tools.

14.1. Style Matters

Writing schemas is much like writing programs. Two pieces of code may both work, but one is more readable and maintainable than the other. Readability is good.

14.1.1. Keep It Simple

Although W3C XML Schema has been carefully specified so that schema processors can find their way through the most complex and intricate combinations of its many features, the same can't be expected of the average human reader. I must confess that I, for one, am getting rapidly lost in the meanders of medium complexity schemas, such as the famous schema for schema.

"Keep it simple" is a useful principle. Although W3C XML Schema gives you a huge number of features, you don't need to use all of them in every schema. Each of them incurs a price in terms of readability of your schema.

Some of the rules for simplicity that we have used for some time with programming languages apply here, such as the conflicting rules for brevity ("If a function is more than one page long, split it"), and directness ("A function should be called more than once"). There are, of course, others such as "Put the code and the documentation in the same place," "If you can't say it in English, you can't say it in C/C++ (or Java, C#, Perl, Python, etc.)," and "Don't solve problems that don't exist."

Translated for the XML design world, these four could read as "If a declaration is more than one page long, split it," "A declaration should be referred to at least one time" (we will see next that there might be exceptions), "If you can't say it in English, you can't say it in XML," and, of course, "Don't solve problems that don't exist."

14.1.2. Think Globally

When I started working with W3C XML Schema, I used to think that the Russian doll design was the simplest, since it's so close to the structure of the instance documents. Having written the W3C XML Schema reference manual, I am convinced that flat structures in which all the elements are global are much simpler to document and just as simple to write!

The Russian doll design relies on an analogy between object-oriented programming and markup. This is somewhat misleading: there is no such thing as a private or local object in an XML document (except maybe if you encode or encrypt some fragments); when you open an XML document, its whole content is exposed, and everything is public and needs to be documented with the same level of accuracy. To describe a concept, give it a name. W3C XML Schema enforces the attribution of unique names only for global elements. Although different content models are often presented as an advantage over the DTDs, defining them under the same element name is very confusing when reading an instance document. The most convenient way to create a reference manual for an XML vocabulary is through a dictionary of elements. Reusing the same element name for different purposes creates multiple entries that are confusing and difficult to read (like the entries for common words, such as "place" in an English dictionary); the example of W3C XML Schema and its very different meanings for xs:extension is enlightening. Therefore, the second piece of advice is to define the elements as global when possible. Note that this advice doesn't apply to unqualified attributes, which cannot be defined as global.

14.1.3. When It's Similar, Show It

The third and last piece of advice contradicts the first one, and a trade-off needs to be found between these two. The first two bits of advice lead to what I call flat schemas. These are similar to our very first example in Chapter 1, "Schema Uses and Development", in which all the elements and attributes are global with local type definitions. This style is easy to read but doesn't highlight the similarities between elements such as the fact that authors and characters can be considered persons and share some properties. When strong similarities exist between different elements, using one of the techniques already discussed (either a complex type derivation or elements and attributes group composition) can enhance the readability of the schema.

The third bit of advice states you should use W3C XML Schema features to highlight the strong similarities when they are present.

Previous | Home | Next
13.2. The Need for Open Schemas | Book Index | 14.2. The W3C XML Schema Annotation Element
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The W3C XML Schema Annotation Element (XML Schema)

 XML SchemaSearch this book

14.2. The W3C XML Schema Annotation Element

The recommended way to add comments and documentation in a W3C XML Schema is through the xs:annotation element. This element can be added within pretty much all the W3C XML Schema elements (in fact, it can be added within all the schema elements except xs:annotation and its child elements, xs:documentation and xs:appinfo). It generally appears as the first child element (except for the xs:schema element in which xs:annotation can appear anywhere).

The xs:annotation element is a container for the xs:documentation and xs:appinfo elements that contain additional information. These two elements are dedicated to holding human readable documentation (xs:documentation) and machine-processable information (xs:appinfo). They accept any text and child elements. (These are the only W3C XML Schema elements that have a mixed content model.) Note, though, that the schema for schema specifies that the processing to apply to the content of these elements is lax. Concretely, this means that although W3C XML Schema elements can be included within these elements, they must be valid per the schema for schema. This mixed content model allows the inclusion of almost any content, such as text:

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:documentation xml:lang="en">
      The author of a book.
    </xs:documentation>
    <xs:documentation xml:lang="fr">
      Designe l'auteur d'un livre.
    </xs:documentation>
  </xs:annotation>
</xs:element>

It also allows rich content, such as XHTML, which can be assembled to create more user friendly and readable documentation:

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:documentation xml:lang="en">
      <p id="author" xmlns="http://www.w3.org/1999/xhtml">
        This element describes the
        <em>
          author
        </em>
        of a
        <a href="#book">
          book
        </a>
      </p>
      .
    </xs:documentation>
  </xs:annotation>
</xs:element>

It even allows SVG, such as the following, which provides a picture of what an author may look like:

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:documentation>
      <svg xmlns="http://www.w3.org/2000/svg">
        <title>
          An author
        </title>
        <ellipse style="stroke:#000000; fill:#e3e000;
          stroke-width:2pt;" id="head" cx="280" cy="250" rx="110"
          ry="130"/>
        <ellipse style="stroke:none; fill:#7f7f7f; " id="leftEye"
          cx="240"cy="225" rx="18" ry="18"/>
        <ellipse style="stroke:none; fill:#7f7f7f; " id="rightEye"
          cx="320"cy="225" rx="18" ry="18"/>
        <path style="fill:none;stroke:#7F7F7F; stroke-width:5pt;"
          id="mouth"d="M 222 280 A 58 48 0 0 0 338 280"/>
      </svg>
    </xs:documentation>
  </xs:annotation>
</xs:element>

Dublin Core elements are a set of elements widely used on the Web to qualify web pages and supported by a large range of applications. They may be used as general purpose metadata embedded within annotations:

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:appinfo xmlns:dc="http://purl.org/dc/elements/1.1/">
      <dc:creator>
        Eric van der Vlist (mailto:vdv@dyomedea.com)
      </dc:creator>
      <dc:date>
        2002-02-01
      </dc:date>
      <dc:subject>
        author,person,book
      </dc:subject>
      <dc:description>
        This element describes the author of a book.
      </dc:description>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

Annotations are also a good container for application-specific metadata, such as those used by the schema for schema to describe the list of facets and properties of its primitive datatypes:

<xs:simpleType name="string" id="string">
  <xs:annotation>
    <xs:appinfo>
      <hfp:hasFacet name="length"/>
      <hfp:hasFacet name="minLength"/>
      <hfp:hasFacet name="maxLength"/>
      <hfp:hasFacet name="pattern"/>
      <hfp:hasFacet name="enumeration"/>
      <hfp:hasFacet name="whiteSpace"/>
      <hfp:hasProperty name="ordered" value="false"/>
      <hfp:hasProperty name="bounded" value="false"/>
      <hfp:hasProperty name="cardinality" value="countably
        infinite"/>
      <hfp:hasProperty name="numeric" value="false"/>
    </xs:appinfo>
    <xs:documentation
      source="http://www.w3.org/TR/xmlschema-2/#string"/>
  </xs:annotation>
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="preserve" id="string.preserve"/>
  </xs:restriction>
</xs:simpleType>

The Schema Adjunct Framework (SAF) is a proposal to complement schemas with the information needed to generate applications. It is found at http://www.extensibility.com/saf/. One of its syntaxes, " schema adornments," also uses xs:appinfo; although this syntax hasn't been adapted to the W3C XML Schema Recommendation yet, it could be something like this (note that this is my own unofficial adaptation given here just as an example):

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:appinfo source="saf:meta-data-item"
      xmlns:sql="http://www.extensibility.com/saf/spec/safsample/sql-map.saf"
      >
      <sql:select>
        select
        <sql:elem>
          name
        </sql:elem>
        ,
        <sql:elem>
          birthdate
        </sql:elem>
        ,
        <sql:attr>
          deathdate
        </sql:attr>
        from tbl_author
      </sql:select>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

SAF also defines a syntax to embed rules written as XPath expressions, which is also the domain of Schematron (which is discussed in Appendix A). Schematron rules can be embedded in xs:appinfo elements and used to test things that W3C XML Schema cannot--like ensuring that the birth of a person took place before their death.

How can this work, given that the XPath 1.0 on which Schematron is built can't interpret dates? This is a fairly dangerous practice, but assuming that the dates use four digits for the years, as well as the same time zone (which we saw how to impose through patterns in Chapter 6, "Using Regular Expressions to Specify Simple Datatypes"), this works because ISO 8601 dates are then following the alphabetical sort order!

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:appinfo xmlns:sch="http://www.ascc.net/xml/schematron">
      <sch:pattern name="Born before dead">
        <sch:rule context="author">
          <sch:assert test="not(dead) or (dead > born)"
            diagnostics="bornAfterDead">
            An author should die after her or his death.
          </sch:assert>
          <sch:diagnostics>
            <sch:diagnostic id="bornAfterDead">
              Error, this author is born after her or his birth!
              Author=
              <sch:value-of select="name"/>
              Birth =
              <sch:value-of select="born"/>
              Death =
              <sch:value-of select="dead"/>
            </sch:diagnostic>
          </sch:diagnostics>
        </sch:rule>
      </sch:pattern>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

Although not a common practice, it is also possible to embed code snippets, such as this XSLT template:

<xs:element name="book" type="book">
  <xs:annotation>
    <xs:appinfo xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:template match="book">
        <xsl:apply-templates select="title"/>
        <xsl:apply-templates select="isbn"/>
        <p>
          Authors:
        </p>
        <ul>
          <xsl:apply-templates select="author"/>
        </ul>
        <p>
          Characters:
        </p>
        <ul>
          <xsl:apply-templates select="character"/>
        </ul>
      </xsl:template>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

Instead of embedding resources, you can also link them using XLink. RDDL, a vocabulary aimed at describing namespaces may be diverted from its original goal to provide the glue for expressing these links:

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:appinfo xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:rddl="http://www.rddl.org/">
      <rddl:resource id="author-transform"
        xlink:arcrole="http://www.w3.org/1999/xhtml"
        xlink:role="http://www.w3.org/1999/XSL/Transform"
        xlink:title="Author template"
        xlink:href="library.xslt#author">
        <div class="resource">
          <h4>
            XSLT Transformation
          </h4>
          <p>
            This
            <a href="library.xslt#author">
              template
            </a>
            displays the description of an author.
          </p>
        </div>
      </rddl:resource>
      <rddl:resource id="CSS" xlink:title="CSS Stylesheet"
        xlink:role="http://www.isi.edu/in-notes/iana/assignments/media-types/text/css"
        xlink:href="author.css">
        <div class="resource">
          <h4>
            CSS Stylesheet
          </h4>
          <p>
            A
            <a href="author.css">
              CSS stylesheet
            </a>
            defining the styles which may be used to display an author.
          </p>
        </div>
      </rddl:resource>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

RDDL should be pronounced "riddle," and its specification is available at http://rddl.org.

TIP: xs:documentation and xs:appinfo both accept an optional source attribute, which is a URI, and can identify the source or the nature of the included information. Since it's designed for human consumption, xs:documentation also accepts an optional xml:lang attribute.

Previous | Home | Next
14. Documenting Schemas | Book Index | 14.3. Foreign Attributes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Foreign Attributes (XML Schema)

 XML SchemaSearch this book

14.3. Foreign Attributes

Although xs:annotation is certainly the most flexible way to embed any information within a schema, W3C XML Schema defines a second opening in its vocabulary that may be used either as an alternative or in conjunction with annotations. All the W3C XML Schema elements (except xs:documentation and xs:appinfo) accept any attribute that has a namespace other than the W3C XML Schema namespace (unprefixed attributes are forbidden). Such attributes may be used to document a schema:

<xs:element name="author" type="author" doc:doc="This element
  describes the author of a book."
  xmlns:doc="http://dyomedea.com/ns/doc"/>

This approach is also used by SAF adornments, whose simple form can be embedded in attributes:

<xs:element name="author" type="author" sql:table="TBL_AUTHOR"
  xmlns:sql="http://www.extensibility.com/saf/spec/safsample/sql-map.saf"
  />

The huge opening given to attributes is especially interesting with attribute-only vocabularies such as XLink, and simple XLinks can be directly embedded into W3C XML Schema. The following example links the definition of our author element directly to a XSLT template:

<xs:element name="author" type="author"
  xlink:arcrole="http://www.w3.org/1999/xhtml"
  xlink:role="http://www.w3.org/1999/XSL/Transform"
  xlink:title="Author template" xlink:href="library.xslt#author"
  xmlns:xlink="http://www.w3.org/1999/xlink"/>

Unfortunately, because of the exception for xs:appinfo and xs:documentation, which do not accept foreign attributes, metadata cannot be added through attributes in these elements, so the following example is invalid:

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:appinfo
      xlink:arcrole="http://www.w3.org/1999/XSL/Transform"
      xlink:role="http://www.w3.org/1999/XSL/Transform"
      xlink:title="Author template" xlink:href="library.xslt#author"
      xmlns:xlink="http://www.w3.org/1999/xlink">
      <div class="resource">
        <h4>
          XSLT Transformation
        </h4>
        <p>
          This
          <a href="library.xslt#author">
            template
          </a>
          displays the description of an author.
        </p>
      </div>
    </xs:appinfo>
    <xs:appinfo title="CSS Stylesheet"
      role="http://www.isi.edu/in-notes/iana/assignments/media-types/text/css"
      href="author.css">
      <div class="resource">
        <h4>
          CSS Stylesheet
        </h4>
        <p>
          A
          <a href="author.css">
            CSS stylesheet
          </a>
          defining the styles which may be used to display an
          author.
        </p>
      </div>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

Of course, the usual limitations of attributes apply here: attributes are less extensible than elements and they cannot include structured content.

Previous | Home | Next
14.2. The W3C XML Schema Annotation Element | Book Index | 14.4. XML 1.0 Comments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML 1.0 Comments (XML Schema)

 XML SchemaSearch this book

14.4. XML 1.0 Comments

There is a general tendency among recent XML vocabularies to use special elements to add documentation or meta-information rather than general purpose XML 1.0 comments or processing instructions. W3C XML Schema follow this tendency with its xs:documentation and xs:appinfo elements. The arguments often used to justify this choice are the ease of use by applications and the fact that parsers are not obliged to transmit comments to applications. In practice, most parsers do transmit comments; comments are available to the applications and can be manipulated quite easily, even through XPath expressions. A more solid argument in favor of using elements is their ability to include structured content.

On the other hand, XML 1.0 comments (and processing instructions) are lighter weight and can be included at any location within any element--not only as the first child element of XML Schema components. With an XML 1.0 comment, our example becomes:

<xs:element name="author" type="author">
  <!-- This element describes the author of a book. -->
</xs:element>
Previous | Home | Next
14.3. Foreign Attributes | Book Index | 14.5. Which One and What For?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Which One and What For? (XML Schema)

 XML SchemaSearch this book

14.5. Which One and What For?

If we recap the three different forms we have seen to include simple comments, we may use xs:documentation:

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:documentation xml:lang="en">
      The author of a book.
    </xs:documentation>
    <xs:documentation xml:lang="fr">
      Designe l'auteur d'un livre.
    </xs:documentation>
  </xs:annotation>
</xs:element>

Or foreign attributes:

<xs:element name="author" type="author" doc:doc="This element
  describes the author of a book."
  xmlns:doc="http://dyomedea.com/ns/doc"/>

Or XML 1.0 comments:

<xs:element name="author" type="author">
  <!-- This element describes the author of a book. -->
</xs:element>

Which one is more appropriate? There is no simple answer, since it depends on which form is supported by the set of tools you choose, and on what you want to do out of these comments. That being said, does that really matter? The hard work is to include relevant comments in your schemas and to keep them up to date. The format itself is not really that important, and each approach is only one XSLT transformation away from the two other alternatives!

The same applies to all the meta-information that we included in our schemas: they do not break the schema and can be processed by any schema processor but will be ignored by most of them! Adding them is useful only if one has an application in mind, and the choice of the format will be determined by the constraints of this application.

Finally, we must note that we have only presented schema-centric options, in which information is added within W3C XML Schema that conforms to the Recommendation. Other options exist, which may be object-oriented design-centric (generating schemas from UML specifications) or adjunct-centric (describing the schema adjunct outside of the W3C XML Schema document). Also, some new inventive packaging could be invented, such as formats that would group different resources (schemas, transformations, stylesheets, etc.) for an information item under a single umbrella, and perhaps recompose the traditional "per function" packaging, depending on the composition done by the application.

Previous | Home | Next
14.4. XML 1.0 Comments | Book Index | 15. Elements Reference Guide
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Elements Reference Guide (XML Schema)

 XML SchemaSearch this book

Chapter 15. Elements Reference Guide

Contents:

Datatypes

This chapter provides a quick reference to all of the elements W3C XML Schema uses to define components of XML Schemas. Some elements can be defined simply, while others require multiple entries reflecting usage in different contexts. If an element name is followed by a parenthetical phrase, you may want to examine following entries to determine which context is most appropriate for your use.

15.1. Datatypes

xs:all(outside a group) Compositor describing an unordered group of elements.
<xs:all
           id                 = xs:ID
           maxOccurs          = "1" : "1"
           minOccurs          = ( "0" | "1" ) : "1"
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, xs:element*)
</xs:all>
May be included in: xs:complexType (local definition), xs:complexType (global definition), xs:extension (complex content), xs:restriction (complex content)

Description

xs:all is used to describe an unordered group of elements whose number of occurences may be zero or one.

Restrictions

xs:all is the only compositor that cannot be used as a particle and needs to be used by itself to describe a complete content model. Unlike xs:choice and xs:sequence, xs:all cannot be embedded within another compositor. It can, thus, only be embedded directly into an xs:complexType, xs:restriction, or xs:extension; furthermore, its number of occurrences may be only zero or one.

The particles included in xs:all are also limited: they can only be xs:element and their number of occurrences can only be zero or one.

Combining these two restrictions means xs:all may only be used to describe content models in which a group of unordered elements (mandatory or optional, but with a number of occurrences not greater than one) are the only child elements.

Situations describing unordered groups of optional elements that have the same number of maximum occurrences can be described using xs:choice or substitution groups, but other content models (such as groups of unordered elements with arbitrary and different maximum numbers of occurrences) cannot be described with W3C XML Schema.

Complex types defined using an xs:all compositor cannot be derived by extension.

Example

<xs:element name="author">
  <xs:complexType>
    <xs:all>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:all>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

<xs:element name="full-name" substitutionGroup="name">
  <xs:complexType>
    <xs:all>
      <xs:element name="first" type="string32" minOccurs="0"/>
      <xs:element name="middle" type="string32" minOccurs="0"/>
      <xs:element name="last" type="string32"/>
    </xs:all>
  </xs:complexType>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

maxOccurs
Maximum number of occurrences. Note that this value is fixed to one.

minOccurs
Minimum number of occurrences. Note that this value can be only zero or one.

xs:all(within a group) Compositor describing an unordered group of elements. The number of occurrences cannot be defined when xs:all is used within a group.
<xs:all
           id          = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, xs:element*)
</xs:all>
May be included in: xs:group (definition)

Description

Used within a group, xs:all has the same meaning as when it is used directly under xs:complexType, except that the minOccurs and maxOccurs attributes have completely disappeared (i.e., it cannot be marked as optional).

Restrictions

The restrictions that apply to xs:all apply to the group embedding the xs:all compositor. This group cannot have a number of occurrences greater than one and cannot be used as a particle. It must be included directly under xs:complexType, xs:restriction, or xs:extension.

All other restrictions of xs:all apply here.

Example

<xs:group name="authorSubElements">
  <xs:all>
    <xs:element ref="name"/>
    <xs:element ref="born"/>
    <xs:element ref="dead" minOccurs="0"/>
  </xs:all>
</xs:group>

Attributes

id
W3C XML Schema's element ID.

xs:annotation Informative data for human or electronic agents.
<xs:annotation
           id          = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:appinfo | xs:documentation)*
</xs:annotation>
May be included in: xs:all (outside a group), xs:all (within a group), xs:any, xs:anyAttribute, xs:attribute (reference or local definition), xs:attribute (global definition), xs:attributeGroup (reference), xs:attributeGroup (global definition), xs:choice (outside a group), xs:choice (within a group), xs:complexContent, xs:complexType (local definition), xs:complexType (global definition), xs:element (within xs:all), xs:element (reference or local definition), xs:element (global definition), xs:enumeration, xs:extension (complex content), xs:extension (simple content), xs:field, xs:fractionDigits, xs:group (reference), xs:group (definition), xs:import, xs:include, xs:key, xs:keyref, xs:length, xs:list, xs:maxExclusive, xs:maxInclusive, xs:maxLength, xs:minExclusive, xs:minInclusive, xs:minLength, xs:notation, xs:pattern, xs:redefine, xs:restriction (complex content), xs:restriction (simple type), xs:restriction (simple content), xs:schema, xs:selector, xs:sequence (within a group), xs:sequence (outside a group), xs:simpleContent, xs:simpleType (local definition), xs:simpleType (global definition), xs:totalDigits, xs:union, xs:unique, xs:whiteSpace

Description

xs:annotation is a container in which additional information can be embedded, either for human consumption (with xs:documentation) or for programs (xs:appinfo). xs:annotation can be added as a first element in almost any W3C XML Schema element. It can also be included anywhere as a top-level element (directly under xs:schema).

Restrictions

xs:annotation cannot be included within itself.

Example

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:documentation xml:lang="en">
      The author of a book.
    </xs:documentation>
    <xs:documentation xml:lang="fr">
      Designe l'auteur d'un livre.
    </xs:documentation>
    <xs:appinfo xmlns:sch="http://www.ascc.net/xml/schematron">
      <sch:pattern name="Born before dead">
        <sch:rule context="author">
          <sch:assert test="not(dead) or (dead > born)"
            diagnostics="bornAfterDead">
            An author should die after her or his death.
          </sch:assert>
          <sch:diagnostics>
            <sch:diagnostic id="bornAfterDead">
              Error, this author is born after her or his birth!
              Author=
              <sch:value-of select="name"/>
              Birth =
              <sch:value-of select="born"/>
              Death =
              <sch:value-of select="dead"/>
            </sch:diagnostic>
          </sch:diagnostics>
        </sch:rule>
      </sch:pattern>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

xs:any Wildcard to replace any element.
<xs:any
           id                 = xs:ID
           maxOccurs          = ( xs:nonNegativeInteger | "unbounded" ) : "1"
           minOccurs          = xs:nonNegativeInteger : "1"
           namespace          = ( ( "##any" | "##other" ) | list of ( xs:anyURI | (
                                   "##targetNamespace" | "##local" ) ) ) : "##any"
           processContents    = ( "skip" | "lax" | "strict" ) : "strict"
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:any>
May be included in: xs:choice (outside a group), xs:choice (within a group), xs:sequence (within a group), xs:sequence (outside a group)

Description

xs:any is a wildcard that allows the insertion of any element belonging to a list of namespaces. This particle can be used like xs:element within choices (xs:choice) and sequences (xs:sequence), and the number of occurrences of the elements that are allowed can be controlled by its minOccurs and maxOccurs attributes.

The list of permitted namespaces is specified though the namespace attribute. The namespace attribute expects a list of namespace URIs. In this list, two values have a specific meaning: ##targetNamespace stands for the target namespace, and ##local stands for local elements (without namespaces). These values can be mixed in the list with regular namespaces URIs. The whole list may also be replaced by two other special values: ##any stands for any namespace at all and is the default value for the namespace attribute, and ##other stands for any namespace other than the target namespace. When ##other is used in a schema without a target namespace, all the namespaces are allowed and only elements without namespaces are forbidden.

The target namespace used to evaluate the special values ##targetNamespace and ##other is the target namespace (or lack of target namespace) of the schema in which the xs:any wildcard is found. This doesn't change when one schema is imported into another.

The behavior of the validator regarding the elements that will be allowed and their children can be specified using the processContent attribute. When processContent is set to strict (i.e., the default values), the schema processors must validate these elements against their schemas and report an error if they are not valid or if it hasn't been able to fetch the schemas for their namespaces. When set to skip, the processors do not attempt to validate these elements. When set to lax, the validator validates the elements if it can find a definition for them, and skips them if it can't.

When the processContent is skip (or when it is lax and the schema processor hasn't been able to locate a schema for an element, which is equivalent), the processor skips any further validation of the elements accepted that may have any content type. The processor eventually includes attributes and subelements of any namespace regardless of the value of the namespace attribute.

When the processContent is strict (or when it is lax and the schema validator has found a schema for them, which is equivalent), the elements that are accepted for the wildcard need to be defined as global elements in the schema for this namespace or have a valid datatype associated in the instance document through xsi:type attribute.

Restrictions

Some combinations of specifications of namespaces that are useful to define multi-namespaces open vocabularies are missing. These are the cases of "any namespace known in the current validation context" and its complementary "any namespace not defined in the current validation context."

The usual restrictions for nondeterministic content models apply to wildcards. While trying to track the risks of or reasons for a nondeterministic content model, pay attention to the value of the processContent attribute. Possible conflict may occur with any element declared as global in one of the permitted namespaces if processContent is strict, and it may occur with any element (global or local) if processContent is skip.

The behavior of the special value ##other on unqualified elements in schemas with target namespaces is contradictory in the W3C XML Schema Recommentation and should be clarified in a future addendum. The most likely interpretation is unqualified elements are not allowed when ##other is specified in these schemas, even though a passage of the Recommendation states the contrary.

Attributes

id
W3C XML Schema's element ID.

maxOccurs
Maximum number of elements permitted for this wildcard.

minOccurs
Minimum number of elements permitted for this wildcard.

namespace
Permitted namespaces.

processContents
Type of validation required on the elements permitted for this wildcard.

xs:anyAttribute Wildcard to replace any attribute.
<xs:anyAttribute
           id                 = xs:ID
           namespace          = ( ( "##any" | "##other" ) | list of ( xs:anyURI | (
                                   "##targetNamespace" | "##local" ) ) ) : "##any"
           processContents    = ( "skip" | "lax" | "strict" ) : "strict"
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:anyAttribute>
May be included in: xs:attributeGroup (global definition), xs:complexType (local definition), xs:complexType (global definition), xs:extension (complex content), xs:extension (simple content), xs:restriction (complex content), xs:restriction (simple content)

Description

xs:anyAttribute is a wildcard that allows the insertion of any attribute belonging to a list of namespaces. This particle must be used wherever an attribute local declaration of reference can be used (i.e., within complexType or attributeGroup definitions).

The list of permitted namespaces is specified though the namespace attribute. The namespace attribute expects a list of namespace URIs. In this list, two values have a specific meaning: ##targetNamespace stands for the target namespace, and ##local stands for local attributes (without namespaces). These values can be mixed in the list with regular namespaces URIs. The whole list may also be replaced by two other special values: ##any stands for any namespace at all and is the default value for the namespace attribute and ##other stands for any namespace other than the target namespace. When ##other is used in a schema without a target namespace, all the namespaces are allowed and only attributes without namespaces are forbidden.

The target namespace used to evaluate the special values ##targetNamespace and ##other is the target namespace (or lack of target namespace) of the schema in which the xs:anyAttribute wildcard is found. This doesn't change when one schema is imported into another.

The behavior of the validator regarding the attributes that will be allowed can be specified using the processContent attribute. When processContent is set to strict (i.e., the default values), the schema processors must validate these attributes against their schemas and report an error if they are not valid or if it hasn't been able to fetch the schemas for their namespaces. When set to skip, the processors do not attempt to validate these attributes. When set to lax, the validator validates the attributes if it can find a definition for them, and skips them if it can't.

When the processContent is skip (or when it is lax and the schema processor hasn't been able to locate a schema for an attribute, which is equivalent), the processor skips any further validation of the accepted attributes that may belong to any datatype.

When the processContent is strict (or when it is lax and the schema validator has found a schema for them, which is equivalent), the accepted attributes for the wildcard need to be defined as global attributes in the schema for this namespace.

Restrictions

Some combinations of specifications of namespaces used to define multi-namespace open vocabularies are missing. These include the cases of "any namespace known in the current validation context" and its complementary "any namespace not defined in the current validation context."

The behavior of the special value ##other on unqualified attributes in schemas with target namespaces is contradictory in the W3C XML Schema Recommentation and should be clarified in a future addendum. The most likely interpretation is that unqualified attributes are not allowed when ##other is specified in these schemas, even though a passage of the Recommendation states the contrary.

Attributes

id
W3C XML Schema's element ID.

namespace
Permitted namespaces.

processContents
Type of validation required on the elements permitted for this wildcard.

xs:appinfo Information for applications.
<xs:appinfo
           source          = xs:anyURI
           >
           Content: ({any})*
</xs:appinfo>
May be included in: xs:annotation

Description

xs:appinfo is a container that embeds structured information that can be used by applications. Its content model is open and can accept any element from any namespace (with a lax validation; W3C XML Schema elements included here must be valid). xs:appinfo can be used to include any kind of information, such as metadata, processing directives, or even code snippets.

Its content is similar to xs:documentation (which is reserved for human-readable information), except for an xml:lang attribute (which is allowed in xs:documentation but forbidden for xs:appinfo).

Restrictions

Foreign attributes cannot be included in xs:appinfo.

The source attribute is underspecified in the Recommendation, which could lead to interoperability issues between applications relying on xs:appinfo if they use relative or shared URIs. For instance, one application may decide to use the XSLT namespace in the source attribute to indicate an XSLT snippet while another application could use the same URI to indicate a set of parameters to pass to an XSLT transformation.

Example

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:appinfo xmlns:dc="http://purl.org/dc/elements/1.1/">
      <dc:creator>
        Eric van der Vlist (mailto:vdv@dyomedea.com)
      </dc:creator>
      <dc:date>
        2002-02-01
      </dc:date>
      <dc:subject>
        author,person,book
      </dc:subject>
      <dc:description>
        This element describes the author of a book.
      </dc:description>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:appinfo source="saf:meta-data-item"
      xmlns:sql="http://www.extensibility.com/saf/spec/safsample/sql-map.saf"
      >
      <sql:select>
        select
        <sql:elem>
          name
        </sql:elem>
        ,
        <sql:elem>
          birthdate
        </sql:elem>
        ,
        <sql:attr>
          deathdate
        </sql:attr>
        from tbl_author
      </sql:select>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

<xs:element name="book" type="book">
  <xs:annotation>
    <xs:appinfo xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:template match="book">
        <xsl:apply-templates select="title"/>
        <xsl:apply-templates select="isbn"/>
        <p>
          Authors:
        </p>
        <ul>
          <xsl:apply-templates select="author"/>
        </ul>
        <p>
          Characters:
        </p>
        <ul>
          <xsl:apply-templates select="character"/>
        </ul>
      </xsl:template>
    </xs:appinfo>
  </xs:annotation>
</xs:element>

Attributes

source
Can be used to provide a link to the source of the information when a snippet is included, or as a semantic attribute to qualify the type of information that is included.

xs:attribute(global definition) Global attribute definition that can be referenced within the same schema by other schemas.
<xs:attribute
           default        = xs:string
           fixed          = xs:string
           id             = xs:ID
           name           = xs:NCName
           type           = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, xs:simpleType?)
</xs:attribute>
May be included in: xs:schema

Description

All the attributes defined at the top level of a schema (i.e., xs:attribute, which is included directly under the xs:schema document element) are considered globally defined.

Globally defined attributes have a global scope: they can be referenced through their qualified name everywhere in the schema in which they are defined as well as in any schema that imports or includes this schema.

Attributes are identified by their qualified name, but the local name is expected in the name attribute.

The definition is done by assigning a simple datatype to the element. This assignment can be done either by reference, using the type attribute to refer to a simple datatype by its qualified name, or "inline," by embedding the definition of the simple type (xs:simpleType element) within the xs:attribute element.

Restrictions

When a target namespace is defined, global attributes must be defined as qualified, i.e., they must be prefixed in the instance documents. Since most of the XML vocabularies do not prefix attributes except when they are "foreign attributes" immersed in elements from other namespaces, this means that global attributes are seldom used when a target namespace is defined. To work around this restriction, attribute groups (xs:attributeGroup) can be used to embed definitions of unqualified attributes that need to be included within multiple complex type definitions.

The identification of the global attributes by their qualified names makes it impossible to globally define multiple attributes having the same qualified name. When a schema must include multiple definitions of attributes that have the same qualified names (and different datatypes), all the definitions (except one) must be locally declared. In this case, it is possible to use simple types or attribute groups instead of global attributes to define reusable content models.

All the globally defined attributes must be qualified if a target namespace is defined for the schema--they must all belong to the target namespace. When no target namespace is defined, all the attributes that are globally or locally defined must be unqualified.

Example

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
  .../...
</xs:schema>

Attributes

default
Default value. When specified, an attribute is added by the schema processor (if it is missing from the instance document) and it is given this value. The default and fixed attributes are mutually exclusive.

fixed
When specified, the value of the attribute is fixed and must be equal to the value specified here. The default and fixed attributes are mutually exclusive.

id
W3C XML Schema's element ID.

name
Local name of the attribute (without namespace prefix).

type
Qualified name of a simple type of the attribute (must be omitted when a simple type definition is embedded).

xs:attribute(reference or local definition) Reference to a global attribute definition or local definition (local definitions cannot be referenced).
<xs:attribute
           default        = xs:string
           fixed          = xs:string
           form           = ( "qualified" | "unqualified" )
           id             = xs:ID
           name           = xs:NCName
           ref            = xs:QName
           type           = xs:QName
           use            = ( "prohibited" | "optional" | "required" ) : "optional"
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:simpleType?))
</xs:attribute>
May be included in: xs:attributeGroup (global definition), xs:complexType (local definition), xs:complexType (global definition), xs:extension (complex content), xs:extension (simple content), xs:restriction (complex content), xs:restriction (simple content)

Description

This element serves two different purposes and has two different content models for these two purposes: it can either be a reference to a globally defined attribute or it can be a local attribute definition. These options are mutually exclusive.

When used as a reference to an attribute, the ref attribute must contain the qualified name of the attribute (with its namespace prefix).

When used as a local definition, the definition is done by assigning a simple datatype to the attribute. This assignment can be done either by reference, using the type attribute to refer to a simple datatype by its qualified name, or inline, by embedding the definition of the simple type (xs:simpleType element) within the xs:attribute element.

Restrictions

Locally defined attributes cannot be referenced.

Example

<xs:complexType>
  <xs:simpleContent>
    <xs:extension base="xs:string">
      <xs:attribute ref="lang"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>
              .../...

<xs:simpleContent>
  <xs:extension base="xs:string">
    <xs:attribute name="lang" type="xs:language"/>
  </xs:extension>
</xs:simpleContent>

Attributes

default
Default value. When specified, an attribute is added by the schema processor, if missing from the instance document, and is given this value. The default and fixed attributes are mutually exclusive.

fixed
When specified, the value of the attribute is fixed and must be equal to the value specified here. The default and fixed attributes are mutually exclusive.

form
Specifies if the attribute is qualified (i.e., must have a namespace prefix in the instance document) or not. The default value for this attribute is specified by the attributeFormDefault attribute of the xs:schema document element--local definition only.

id
W3C XML Schema's element ID.

name
Local name (without namespace prefix)--local definition only.

ref
Qualified name of a globally defined attribute--reference only.

type
Qualified name of a simple datatype--definition only.

use
Possible usage of the attribute. Marking an attribute "prohibited" is useful to exclude attributes during derivations by restriction.

xs:attributeGroup(global definition) Global attributes group declaration that can be referenced within the same schema by other schemas.
<xs:attributeGroup
           id            = xs:ID
           name          = xs:NCName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, ((xs:attribute | xs:attributeGroup)*,
                      xs:anyAttribute?))
</xs:attributeGroup>
May be included in: xs:redefine, xs:schema

Description

Attribute groups are global containers that embed groups of attributes. They can be used to manipulate groups of several attributes often used together, but also to provide global access to attributes that cannot be globally defined because they are unqualified or because several definitions cohabit in a single schema (see the example).

Attribute references or local definitions, attribute wildcards, and attribute group references may be included within attribute group definitions.

Attribute groups may be redefined through xs:redefine elements.

Restrictions

None.

Example

<xs:attributeGroup name="bookAttributes">
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
</xs:attributeGroup>

Attributes

id
W3C XML Schema's element ID.

name
Name of the attribute group.

xs:attributeGroup(reference) Reference to a global attributes group declaration.
<xs:attributeGroup
           id           = xs:ID
           ref          = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:attributeGroup>
May be included in: xs:attributeGroup (global definition), xs:complexType (local definition), xs:complexType (global definition), xs:extension (complex content), xs:extension (simple content), xs:restriction (complex content), xs:restriction (simple content)

Description

Any non-top-level occurrence of xs:attribute is a reference to an attribute group that acts like a replacement of the group by the attributes (or attribute group references) embedded in the group.

Restrictions

None.

Example

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="isbn"/>
      <xs:element ref="title"/>
      <xs:element ref="author" minOccurs="0" maxOccurs="unbounded"/>
      <xs:element ref="character" minOccurs="0"
        maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attributeGroup ref="bookAttributes"/>
  </xs:complexType>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

ref
Qualified name of the attribute group to reference.

xs:choice(outside a group) Compositor to define group of mutually exclusive elements or compositors.
<xs:choice
           id                 = xs:ID
           maxOccurs          = ( xs:nonNegativeInteger | "unbounded" ) : "1"
           minOccurs          = xs:nonNegativeInteger : "1"
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:element | xs:group | xs:choice |
                     xs:sequence | xs:any)
          )*)
</xs:choice>
May be included in: xs:choice (outside a group), xs:choice (within a group), xs:complexType (local definition), xs:complexType (global definition), xs:extension (complex content), xs:restriction (complex content), xs:sequence (within a group), xs:sequence (outside a group)

Description

xs:choice is a compositor that defines a group of mutually exclusive particles. Only one can be found in the instance document per occurrence of the xs:choice compositor. The number of occurrences of the compositor itself is controlled by its minOccurs and maxOccurs attributes, while the number of occurrences of each particle within a single occurrence of xs:choice can be controlled by the minOccurs and maxOccurs attributes of the particles.

In addition to situations where simple choices are expressed (element "a" or "b" can be accepted here), xs:choice is often used to work around the limitations of xs:all and to define content models where an unlimited number of elements can be found in any order (see the example).

When the particle used in a xs:choice compositor is an element, a similar effect may be achieved using substitution groups.

Restrictions

The xs:choice compositor may cause violations of the Unique Particle Attribution and Consistent Declaration rules.

Example

<xs:choice>
  <!-- Allows either "name" or the sequence "first-name",
  "middle-name"and"last-name". -->
  <xs:element ref="name"/>
  <xs:sequence>
    <xs:element ref="first-name"/>
    <xs:element ref="middle-name" minOccurs="0"/>
    <xs:element ref="last-name"/>
  </xs:sequence>
</xs:choice>

Attributes

id
W3C XML Schema's element ID.

maxOccurs
Maximum number of occurrences of the choice compositor.

minOccurs
Minimum number of occurrences of the choice compositor.

xs:choice(within a group) Compositor to define group of mutually exclusive elements or compositors. The number of occurrences cannot be defined when xs:choice is used within a group.
<xs:choice
           id          = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:element | xs:group | xs:choice |
                     xs:sequence | xs:any)
          )*)
</xs:choice>
May be included in: xs:group (definition)

Description

When embedded in a group definition, the xs:choice compositor has the same function as other contexts, except that the number of its occurrences is defined in the xs:group reference instead of by the minOccurs and maxOccurs attributes of the xs:choice compositor.

Restrictions

The minOccurs and maxOccurs attributes that are allowed in other contexts are forbidden.

Example

<xs:group name="author-or-character">
  <xs:choice>
    <xs:element ref="author"/>
    <xs:element ref="character"/>
  </xs:choice>
</xs:group>

Attributes

id
W3C XML Schema's element ID.

xs:complexContent Definition of a complex content by derivation of a complex type.
<xs:complexContent
           id             = xs:ID
           mixed          = xs:boolean
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:restriction | xs:extension))
</xs:complexContent>
May be included in: xs:complexType (local definition), xs:complexType (global definition)

Description

This element allows you to define a complex content model by derivation of a complex type. It is not a component by itself (complex contents are not named), but rather the declaration of an intention to define a complex content model by derivation.

The derivation method is not defined by the xs:complexContent element itself, but by the choice of its child element (xs:restriction for a derivation by restriction, or xs:extension for a derivation by extension).

This element can be used to define if the content model is mixed or not--but this information can also be defined in the xs:complexType parent element, which has also a mixed attribute.

The mixed nature of the content model defined by xs:complexContent attribute is dependent on the derivation method and on the base type. If the base type is mixed, it can be restricted to become elements only; otherwise the mixed nature of the base type cannot be changed.

Restrictions

A base type whose elements cannot be extended to be mixed.

Example

<xs:element name="author">
  <xs:complexType>
    <xs:complexContent>
      <xs:extension base="basePerson">
        <xs:sequence>
          <xs:element ref="dead" minOccurs="0"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

mixed
When set to true, the content model is mixed; when set to false, the content model is "element only"; when not set, the content model is determined by the mixed attribute of the parent xs:complexType element.

xs:complexType(global definition) Global definition of a complex type that can be referenced within the same schema by other schemas.
<xs:complexType
           abstract       = xs:boolean : "false"
           block          = ( "#all" | list of ( "extension" | "restriction" ) )
           final          = ( "#all" | list of ( "extension" | "restriction" ) )
           id             = xs:ID
           mixed          = xs:boolean : "false"
           name           = xs:NCName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:simpleContent | xs:complexContent | (,
                      (xs:group | xs:all | xs:choice | xs:sequence)?,
                      ((xs:attribute | xs:attributeGroup)*, xs:anyAttribute?
          ?))))
</xs:complexType>
May be included in: xs:redefine, xs:schema

Description

This component is used to create global complex types as direct descriptions of their content model, or by derivation from simple types or other complex types. The creation of a new complex type is done by a compositor (xs:sequence, xs:choice, or xs:all) that describes the child elements, followed by a list of attributes (or attribute groups or attribute wildcards). The derivation of a complex type through existing simple or complex types is done by including an xs:simpleContent (for simple contents) or an xs:complexContent (for complex contents) element. In both cases, when the content is not simple, the mixed attribute defines if the content model is mixed (i.e., allows both text and element nodes) or not.

Several controls are provided through attributes. Complex types can be declared "abstract," their substitution can be "blocked," and they can be "final" for derivation.

Restrictions

Constraints about keys (xs:key, xs:unique, and xs:keyref) cannot be defined at this level but need to be defined in the xs:element element. This means that, while in many cases referencing a global element definition or a global complex type definition can be considered as equivalent to define modular content models, complex types are no longer an option when these constraints need to be included in the content model.

Example

<xs:complexType name="title">
  <xs:simpleContent>
    <xs:extension base="tokenWithLang">
      <xs:attribute name="note" type="xs:token"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>

Attributes

abstract
When set to true, this complex type cannot be used directly in the instance documents and needs to be substituted using a xsi:type attribute.

block
Controls whether a substitution (either through a xsi:type or substitution groups) can be performed to a complex type, which is an extension or a restriction of the current complex type. This attribute can only block such substitutions (it cannot "unblock" them), which can also be blocked in the element definition. The default value is defined by the blockDefault attribute of xs:schema.

final
Controls whether the complex type can be further derived by extension or restriction to create new complex types.

id
W3C XML Schema's element ID.

mixed
Defines if the content model will be mixed.

name
Name of the complex type.

xs:complexType(local definition) Complex type local definition (local definitions cannot be referenced).
<xs:complexType
           id             = xs:ID
           mixed          = xs:boolean : "false"
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:simpleContent | xs:complexContent | (,
                      (xs:group | xs:all | xs:choice | xs:sequence)?,
                      ((xs:attribute | xs:attributeGroup)*, xs:anyAttribute?
          ?))))
</xs:complexType>
May be included in: xs:element (within xs:all), xs:element (reference or local definition), xs:element (global definition)

Description

Complex type definition can be created several ways. It can be done by derivation using an xs:simpleContent (for simple content models) or an xs:complexContent (for simple content model) element, or it can be described, using xs:sequence (for an ordered sequence of elements), an xs:choice (for an alternative beyond several elements), xs:all (for a unordered list of elements) or xs:group (to reference a group of elements), and xs:attribute, xs:attributeGroup, and xs:anyAttribute to define the list of its attributes.

Complex types describe all the constraints on the element, character, and attribute nodes that may be included within an element except those described by xs:unique, xs:key, and xs:keyref, which are defined directly under the xs:element element definitions.

Restrictions

Constraints defined by xs:unique, xs:key, and xs:keyref are not defined within complex types but directly under the xs:element element definitions.

Local definitions can neither be derived (by restriction or extension) nor substituted. In addition, the derivation by restriction of complex types and the redefinition by restriction of element groups, including elements defined with local complex type definitions, is limited since derivations by restriction need to redefine the elements that are kept using explicit derivations.

Unlike other components (xs:element, xs:attribute, xs:group, and xs:attributeGroup), local xs:complexType components cannot be used to reference global complex types. Reference to complex types is done through xs:element type attributes and xs:restriction and xs:extension base attributes.

Example

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute ref="lang"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

mixed
Defines if the content type will be mixed.

xs:documentation Human-targeted documentation.
<xs:documentation
           source            = xs:anyURI
           xml:lang          = xml:lang
           >
           Content: ({any})*
</xs:documentation>
May be included in: xs:annotation

Description

xs:documentation is a container for human-readable documentation in plain text or structured formats. Its content model is open and can accept any element from any namespace (with a lax validation only--W3C XML Schema elements included here must be valid). xs:documentation can be used to include any kind of information.

Its content is similar to xs:appinfo (which is reserved for application processable information), except that it has xml:lang attribute; this is allowed in xs:documentation, but forbidden for xs:appinfo).

Restrictions

Foreign attributes cannot be included in xs:documentation. The source attribute is also underspecified in the Recommendation. This could lead to interoperability issues between applications that rely on xs:documentation.

Example

<xs:element name="author" type="author">
  <xs:annotation>
    <xs:documentation xml:lang="en">
      The author of a book.
    </xs:documentation>
    <xs:documentation xml:lang="fr">
      Designe l'auteur d'un livre.
    </xs:documentation>
  </xs:annotation>
</xs:element>

Attributes

source
Can be used to provide a link to the source of the information when a snippet is included, or it can be used as a semantic attribute to qualify the type of information included.

xml:lang
Language used for the documentation.

xs:element(global definition) Global element definition that can be referenced within the same schema by other schemas.
<xs:element
           abstract          = xs:boolean : "false"
           block             = ( "#all" | list of ( "extension" | "restriction" |
                                  "substitution" ) )
           default           = xs:string
           final             = ( "#all" | list of ( "extension" | "restriction" ) )
           fixed             = xs:string
           id                = xs:ID
           name              = xs:NCName
           nillable          = xs:boolean : "false"
           substitutionGroup = xs:QName
           type              = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:simpleType | xs:complexType)?, (xs:unique |
                     xs:key | xs:keyref)*)
</xs:element>
May be included in: xs:schema

Description

All the elements defined at the top level of a schema (i.e., xs:element, which is included directly under the xs:schema document element) are considered globally defined.

Globally defined elements have a global scope: they can be referenced through their qualified name everywhere in the schema in which they are defined, as well as in any schema that imports or includes this schema. They can also be used as document elements in instance documents.

Elements are identified by their qualified name, but the local name is expected in the name attribute. The type definition is performed by assigning a simple or complex datatype to the element.

This assignment can be done either by reference, using the type attribute to refer to a simple or complex datatype by its qualified name, or inline, by embedding the definition of the simple (xs:simpleType) element or the complex (xs:complexType) element that is within the xs:element element.

A default value may be defined using the default attribute, but note that the semantics of default values for elements are different than those of the default values for attributes. An element is not created if it is absent from the instance document; the default value is applied only if the element is present and empty.

Several control attributes are included in element definitions (see the attributes abstract, blocked, final, fixed, and nillable) .

Element definitions are also the place in which substitution groups are defined using the substitutionGroup attribute to refer to the head of the substitution group. Any global element that is not final may be chosen as the head of a substitution group. Any reference to the head of a substitution group may be replaced in the instance documents by any of the members of the substitution group (assuming these substitutions have not been blocked through block attributes). The relation is transitive, and if the head of a substitution "A" is a member of another substitution group "B," the members of "A" are also considered members of "B."

Restrictions

There is no feature that allows definition of global elements that cannot be used as document elements. When this is needed, the workaround is to define all the elements, save one, and use simple and complex types or element groups instead of global elements to define reusable content models, and then reference these types from a single globally-defined element.

The identification of the global elements by their qualified names makes it impossible to globally define multiple elements that have the same qualified name. When a schema must include multiple definitions of elements that have the same qualified name (and different datatypes), all the definitions except one must be locally declared. It is possible to use simple and complex types or element groups instead of global elements to define reusable content models.

All globally defined elements must be qualified if a target namespace has been defined for the schema; they must all belong to the target namespace. When no target namespace is defined, all the elements that are globally or locally defined must be unqualified.

The default and fixed values are defined in attributes and, therefore, can only apply to simple type elements.

Elements cannot be directly redefined. To redefine an element, one can either include it in a group and redefine the group, or use a global complex type to define the element and redefine the complex type.

Example

<xs:element name="name" type="xs:string"/>

Attributes

abstract
Controls whether the element may be used directly in instance documents. When set to true, the element may still be used to define content models, but it must be substituted through a substitution group in the instance document.

block
Controls whether the element can be subject to a type or substitution group substitution. #all blocks any substitution, substitution blocks any substitution through substitution groups, and extension and restriction block any substitution (both through xsi:type and substitution groups) by elements or types, derived respectively by extension or restriction from the type of the element. Its default value is defined by the blockDefault attribute of the parent xs:schema.

default
Default value of the element. Defined in an attribute, element default values must be simple contents. Also note that default values apply only to elements that are present in the document and empty. The fixed and default attributes are mutually exclusive.

final
Controls whether the element can be used as the head of a substitution group for elements whose types are derived by extension or restriction from the type of the element. Its default value is defined by the finalDefault attribute of the parent xs:schema.

fixed
A simple content element may be fixed to a specific value using this attribute. This value is also used as a default value, and if the element is empty, it is supplied to the application. The fixed and default attributes are mutually exclusive.

id
W3C XML Schema's element ID.

name
Local name of the element (without namespace prefix).

nillable
When this attribute is set to true, the element can be declared as nil using an xsi:nil attribute in the instance documents.

substitutionGroup
Qualified name of the head of the substitution group to which this element belongs.

type
Qualified name of a simple or complex type (must be omitted when a simple or complex type definition is embedded).

xs:element(within xs:all) Reference to a global element declaration or local definition (local definitions cannot be referenced). The number of occurrences can only be zero or one when xs:element is used within xs:all..
<xs:element
           block         = ( "#all" | list of ( "extension" | "restriction" |
                              "substitution" ) )
           default       = xs:string
           fixed         = xs:string
           form          = ( "qualified" | "unqualified" )
           id            = xs:ID
           maxOccurs     = ( "0" | "1" ) : "1"
           minOccurs     = ( "0" | "1" ) : "1"
           name          = xs:NCName
           nillable      = xs:boolean : "false"
           ref           = xs:QName
           type          = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:simpleType | xs:complexType)?, (xs:unique |
                     xs:key | xs:keyref)*)
</xs:element>
May be included in: xs:all (outside a group), xs:all (within a group)

Description

This element serves two different purposes and has two different content models for these purposes: it can either be a reference to a globally defined element or to a local element definition. These options are mutually exclusive.

When used as a reference to an element, the ref attribute must contain the qualified name of the element (with its namespace prefix).

When used as a local definition, the definition is done by assigning a simple or complex datatype to the element. This assignment can be done either by reference, using the type attribute to refer to a simple or complex datatype by its qualified name, or inline, by embedding the definition of the simple (xs:simpleType) element or complex (xs:complexType) element within the xs:element element.

Restrictions

Local element definitions or references cannot have a number of occurrences greater than one.

The default and fixed values are defined in attributes and, therefore, can only apply to simple type elements.

The fact that W3C XML Schema cannot describe the exclusive combinations of the attributes and elements of xs:element, which can be summarized as:

ref attribute xor name attribute, type attribute xor xs:simpleType element, xor
xs:complexType element

is an example that shows some of the restrictions of the language.

Example

<xs:all>
  <xs:element name="first" type="string32" minOccurs="0"/>
  <xs:element name="middle" type="string32" minOccurs="0"/>
  <xs:element name="last" type="string32"/>
</xs:all>

Attributes

block
Controls whether the element can be subject to a type or substitution group substitution. #all blocks any substitution, substitution blocks any substitution through substitution groups, and extension and restriction block any substitution (both through xsi:type and substitution groups) by elements or types, derived respectively by extension or restriction from the type of the element. Its default value is defined by the blockDefault attribute of the parent xs:schema.

default
Default value of the element. Defined in an attribute, element default values must be simple contents. Also note that default values apply only to elements that are present in the document and empty. The fixed and default attributes are mutually exclusive.

fixed
A simple content element may be fixed to a specific value using this attribute. This value is also used as a default value, and if the element is empty, it is supplied to the application. The fixed and default attributes are mutually exclusive.

form
Specifies whether the element must be qualified (i.e., belong to a namespace) in the instance documents or not. The default value of this attribute is determined by the xs:schema elementFormDefault attribute.

id
W3C XML Schema's element ID.

maxOccurs
Maximum number of occurrences of the element. Can take only the values 0 or 1 within a xs:all compositor.

minOccurs
Minimum number of occurrences of the element. Can take only the values 0 or 1 within a xs:all compositor.

name
Name of the element (mutually exclusive with the ref attribute).

nillable
When this attribute is set to true, the element can be declared as nil using an xsi:nil attribute in the instance documents.

ref
Reference to a global element definition (mutually exclusive with the name, block, and type attributes and any embedded type definition.

type
Simple or complex type of the element (mutually exclusive with the ref attribute and any embedded type definition).

xs:element(reference or local definition) Reference to a global element declaration or local definition (local definitions cannot be referenced).
<xs:element
           block         = ( "#all" | list of ( "extension" | "restriction" |
                              "substitution" ) )
           default       = xs:string
           fixed         = xs:string
           form          = ( "qualified" | "unqualified" )
           id            = xs:ID
           maxOccurs     = ( xs:nonNegativeInteger | "unbounded" ) : "1"
           minOccurs     = xs:nonNegativeInteger : "1"
           name          = xs:NCName
           nillable      = xs:boolean : "false"
           ref           = xs:QName
           type          = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:simpleType | xs:complexType)?, (xs:unique |
                      xs:key | xs:keyref)*)
</xs:element>
May be included in: xs:choice (outside a group), xs:choice (within a group), xs:sequence (within a group), xs:sequence (outside a group)

Description

This element serves two different purposes and has two different content models for these purposes: it can either be a reference to a globally defined element or to a local element definition. These options are mutually exclusive.

When used as a reference to an element, the ref attribute must contain the qualified name of the element (with its namespace prefix).

When used as a local definition, the definition is done by assigning a simple or complex datatype to the element. This assignment can be done either by reference, using the type attribute to refer to a simple or complex datatype by its qualified name, or inline, by embedding the definition of the simple (xs:simpleType) element or complex (xs:complexType) element within the xs:element element.

In all cases, the number of occurrences can be constrained using the minOccurs and maxOccurs attributes, whose default value is 1. The maxOccurs attribute can take the value unbounded, to define that an element may appear as many times as the author of the document wishes.

Restrictions

The default and fixed values are defined in attributes and, therefore, can only apply to simple type elements.

The fact that W3C XML Schema cannot describe the exclusive combinations of the attributes and elements of xs:element which could be summarized as:

ref attribute xor name attribute, type attribute xor xs:simpleType element, xor
xs:complexType element

is an example that shows some of the restrictions of the language.

Example

<xs:sequence>
  <xs:element name="book" maxOccurs="unbounded">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="isbn"/>
        <xs:element ref="title"/>
        <xs:element ref="author" minOccurs="0"
          maxOccurs="unbounded"/>
        <xs:element ref="character" minOccurs="0"
          maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
      <xs:attribute ref="available"/>
    </xs:complexType>
  </xs:element>
</xs:sequence>

Attributes

block
Controls whether the element can be subject to a type or substitution group substitution. #all blocks any substitution, substitution blocks any substitution through substitution groups, and extension and restriction block any substitution (both through xsi:type and substitution groups) by elements or types, derived respectively by extension or restriction from the type of the element. Its default value is defined by the blockDefault attribute of the parent xs:schema.

default
Default value of the element. Defined in an attribute, element default values must be simple contents. Also note that default values apply only to elements that are present in the document and empty. The fixed and default attributes are mutually exclusive.

fixed
A simple content element may be fixed to a specific value using this attribute. This value is also used as a default value, and if the element is empty, it is supplied to the application. The fixed and default attributes are mutually exclusive.

form
Defines if the element is "qualified" (i.e., belongs to the target namespace) or "unqualified" (i.e., doesn't belong to any namespace)--to be used only for local element definitions.

id
W3C XML Schema's element ID.

maxOccurs
Maximum number of occurrences ("unbounded" means "unlimited").

minOccurs
Minimum number of occurrences.

name
Local name (without namespace prefix)--to be use only for local element definitions.

nillable
When this attribute is set to true, the element can be declared as nil using an xsi:nil attribute in the instance documents.

ref
Reference to a global element--to be used only for references.

type
Reference to a simple or complex type--to be used only for local element definitions.

xs:enumeration Facet to restrict a datatype to a finite set of values.
<xs:enumeration
           id             = xs:ID
           value          = anySimpleType
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:enumeration>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:anyURI, xs:base64Binary, xs:byte, xs:date, xs:dateTime, xs:decimal, xs:double, xs:duration, xs:ENTITIES, xs:ENTITY, xs:float, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:gYearMonth, xs:hexBinary, xs:ID, xs:IDREF, xs:IDREFS, xs:int, xs:integer, xs:language, xs:long, xs:Name, xs:NCName, xs:negativeInteger, xs:NMTOKEN, xs:NMTOKENS, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:normalizedString, xs:NOTATION, xs:positiveInteger, xs:QName, xs:short, xs:string, xs:time, xs:token, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, xs:unsignedShort

Description

xs:enumeration is a facet that allows definition of a list of possible values for the value space of a datatype by enumerating all these values in separated xs:enumeration elements in a single restriction step (i.e., under a single rs:restriction parent element). This is one of the two facets (with xs:pattern) that are available for all the datatypes (atomic, lists, or unions), but unlike xs:pattern, xs:enumeration is not available for xs:boolean.

Restrictions

The values of the xs:enumeration facets must belong to the value space of the base datatype. When the base datatype is already restricted by enumeration, the new enumeration values must be a subset of the base datatype enumerations (even though they may have different lexical representations that correspond to the same logical values).

This facet cannot be fixed (i.e., it remains available in all the derived types).

This facet is not available for xs:boolean.

Example

<xs:simpleType name="schemaRecommendations">
  <xs:restriction base="xs:anyURI">
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-0/"/>
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-1/"/>
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-2/"/>
  </xs:restriction>
</xs:simpleType>

Attributes

id
W3C XML Schema's element ID.

value
Value to be added to the list of possible values for this datatype.

xs:extension(simple content) Extension of a simple content model.
<xs:extension
           base          = xs:QName
           id          = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, ((xs:attribute | xs:attributeGroup)*,                      xs:anyAttribute?))
</xs:extension>
May be included in: xs:simpleContent

Description

This element is used to extend either a simple type or a simple content complex type into a simple content complex type (i.e., the content model of an element with a text nodes and some attributes). The type to extend is specified in the base attribute, and the list of the attributes to add to the content model is given as xs:attributes, xs:attributeGroup, and xs:anyAttribute embedded in the xs:extension element.

Such a derivation by extension of a simple type is the only way to create a complex type simple content content model.

Restrictions

The definition of the base type cannot be embedded in the xs:extension element, and the base type needs to be a global type.

The same element name (xs:extension) is used by W3C XML Schema for the extension of simple content and complex content complex types with a different content model.

Example

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="string255">
        <xs:attribute ref="lang"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

Attributes

base
Qualified name of the base type (simple type or simple content complex type).

id
W3C XML Schema's element ID.

xs:extension(complex content) Extension of a complex content model.
<xs:extension
           base          = xs:QName
           id            = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (, (xs:group | xs:all | xs:choice |
                      xs:sequence)?, ((xs:attribute | xs:attributeGroup)*,
                      xs:anyAttribute?)))
</xs:extension>
May be included in: xs:complexContent

Description

Extension of a complex (or mixed) content complex type. This derivation method can be applied to another complex content complex base type, and allows the addition of elements and attributes to this base type. The additional elements are added after the compositor used in the base type.

When new elements are added during a derivation by extension, the resulting content model is equivalent to creating a sequence that contains the compositor used to define the base type and the one included in the xs:extension element.

Restrictions

Complex types that use a xs:all compositor cannot be used as base types for derivations by extension, except when those extensions add only attributes.

New elements added to base types using a xs:choice compositor are added in sequence after the xs:choice, rather than as new choices.

Example

<xs:element name="character">
  <xs:complexType>
    <xs:complexContent>
      <xs:extension base="basePerson">
        <xs:sequence>
          <xs:element ref="qualification"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

Attributes

base
Qualified name of the base type.

id
W3C XML Schema's element ID.

xs:field Definition of the field to use for a uniqueness constraint.
<xs:field
           id             = xs:ID
           xpath          = xs:token
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:field>
May be included in: xs:key, xs:keyref, xs:unique

Description

xs:field is used to define the location of the fields on which a uniqueness constraint or reference will be checked.

The fields are elements or attributes that are identified by relative XPath expressions (i.e., xpath attributes) evaluated against the nodes selected by the xs:selector element.

Concatenated keys can be expressed defining multiple fields under a xs:unique, xs:key, or xs:keyref element.

Restrictions

The xpath attribute uses a simple subset of XPath 1.0. The motivation of the W3C XML Schema Working Group for defining this subset is to simplify the work of the implementers of schema processors, and also to define a subset that constraints the path to stay within the scope of the current element.

This subset is restricted to using only the child, attribute, self, and descendant or self XPath axes through their abbreviated syntaxes without including any test in any of the XPath location steps, and without using any XPath functions. It is identical to the subset defined for xs:selector, except that attributes are allowed in xs:field and forbidden in xs:selector.

The formal extended BNF given in the W3C Recommendation is as follows:

Field ::= Path ( '|' Path )* Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
Step ::= '.' | NameTest NameTest ::= QName | '*' | NCName ':' '*'

When concatenated keys are defined, the node sets identified by each field must have a single occurrence per iteration of locator. This means that on structures such as:

<sect num="1">
  <sub-sect num="1"> Sub section 1.1 </sub-sect>
  <sub-sect num="2"> Sub section 1.2 </sub-sect>
</sect>
<sect num="1">
  <sub-sect num="3"> Sub section 1.3 </sub-sect>
  <sub-sect num="4"> Sub section 1.4 </sub-sect>
</sect>

multilevel concatenated keys over the num attributes of sect and sub-sect cannot be defined with W3C XML Schema. This is because the locator would need to iterate on the sect element, and the num attribute of the sub-sect element then takes several values for each iteration.

Example

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:unique name="book">
    <xs:selector xpath="book"/>
    <xs:field xpath="isbn"/>
  </xs:unique>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

xpath
Relative XPath expression identifying the field(s) composing the key, key reference, or unique constraint.

xs:fractionDigits Facet to define the number of fractional digits of a numerical datatype.
<xs:fractionDigits
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = xs:nonNegativeInteger
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:fractionDigits>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:decimal

Description

xs:fractionDigits defines the maximum number of fractional digits (i.e., digits that are after the decimal point) of an xs:decimal datatype.

This facet constrains the value space, which means that the number of fractional digits is checked after the value is transformed to its canonical form, and the trailing zeros are removed.

Restrictions

Within a restriction step, xs:fractionDigits is dependent on xs:totalDigits, since using inconsistent values leads to datatypes with empty value spaces.

xs:fractionDigits must restrict the value space of its base type, and its value must be smaller than the value of xs:fractionDigits of its base type if defined.

It is possible to use xs:pattern to constrain the number of fractional digits in the lexical space.

Example

<xs:simpleType name="fractionDigits">
  <xs:restriction base="xs:decimal">
    <xs:fractionDigits value="2"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to true, the value of the facet cannot be modified during further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet.

xs:group(definition) Global elements group declaration that can be referenced within the same schema by other schemas.
<xs:group
           name               = xs:NCName
           >
           Content: (xs:annotation?, (xs:all | xs:choice | xs:sequence))
</xs:group>
May be included in: xs:redefine, xs:schema

Description

Groups of elements can be globally defined and used as containers, which can be referenced elsewhere in this schema or in other schemas. These containers, which are almost free of semantics, are very flexible and may be used as building blocks for the definition of local or global complex types.

Being global components, they can also be used to encapsulate element definitions that cannot be made global, such as unqualified elements in a schema with target namespaces, multiple elements with the same name in a schema, elements that should not be used as document elements, or elements that should be redefined through xs:redefine in other schemas.

Element groups can be redefined through xs:redefine; the redefinition can be either a restriction (similar to a complex type derivation by restriction) or an extension. (This is more flexible than a complex type extension since the location where the base group is included in the new group can be chosen, while the new elements are always located after the base type during a derivation by extension.)

Any of the three compositors (xs:all, xs:sequence, or xs:choice) may be used to create an element group.

Restrictions

When a group is created using a xs:all compositor, this group inherits all the restrictions of xs:all. In other words, including a xs:all compositor in a group is not a workaround to avoid the fundamental restrictions of xs:all!

Group definition cannot be recursive.

Example

<xs:group name="name">
  <xs:choice>
    <xs:element ref="name"/>
    <xs:sequence>
      <xs:element ref="first-name"/>
      <xs:element ref="middle-name" minOccurs="0"/>
      <xs:element ref="last-name"/>
    </xs:sequence>
  </xs:choice>
</xs:group>

Attributes

name
Name of the group (unqualified).

xs:group(reference) Reference to a global elements group declaration or local definition (local definitions cannot be referenced).
<xs:group
           id          = xs:ID
           maxOccurs          = ( xs:nonNegativeInteger | "unbounded" ) : "1"
           minOccurs          = xs:nonNegativeInteger : "1"
           ref          = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:group>
May be included in: xs:choice (outside a group), xs:choice (within a group), xs:complexType (local definition), xs:complexType (global definition), xs:extension (complex content), xs:restriction (complex content), xs:sequence (within a group), xs:sequence (outside a group)

Description

Used in a local context, xs:group is a reference to an element group. The effect is a logical replacement of the group reference by the group's content.

When a group is being redefined (through xs:redefine), a self reference (i.e., a reference to the group being redefined) specifically means "include the original content of the group at this location."

Restrictions

Groups cannot be recursive (i.e., a group cannot be referred to either directly or indirectly within its definition).

Example

<xs:element name="author">
  <xs:complexType>
    <xs:sequence>
      <xs:group ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute ref="id"/>
  </xs:complexType>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

maxOccurs
Maximum number of occurrences of the group.

minOccurs
Minimum number of occurrences of the group.

ref
Qualified name of the group to include.

xs:import Import of a W3C XML Schema for another namespace.
<xs:import
           id               = xs:ID
           namespace        = xs:anyURI
           schemaLocation   = xs:anyURI
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:import>
May be included in: xs:schema

Description

xs:import identifies the location at which a W3C XML Schema validator may find the definition corresponding to namespaces other than the target namespace of the current schema.

All the global definitions (elements, attributes, element and attribute groups, simple and complex types) and unique and key constraints of the imported schemas can be referenced using a namespace prefix defined for the corresponding namespace URI.

The schema locations indicated in xs:import elements are only hints provided to the schema validators and may be omitted. In this case, the Recommendation states that "the schema author is leaving the identification of that schema to the instance, application or user, via the mechanisms described in Layer 3: Schema Document Access and Web-interoperability."

xs:import may also be used to import components with no target namespaces into schemas with target namespaces.

Restrictions

xs:import must not be used to import component definitions from the target namespace since two other elements are available for this purpose (see xs:include and xs:redefine).

Only global component definitions of the imported schemas can be referenced (local definitions can never be referenced).

The rules of scoping described for the xs:keyref element also apply to references between constraints for elements that belong to different namespaces (the root element of the keyref constraint must be an ancestor or self element of the root element for the unique or key constraint).

Example

<xs:schema targetNamespace="http://dyomedea.com/ns/library"
  elementFormDefault="qualified" attributeFormDefault="unqualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:ppl="http://dyomedea.com/ns/people"
  xmlns:lib="http://dyomedea.com/ns/library">
  <xs:import namespace="http://dyomedea.com/ns/people"
    schemaLocation="simple-2-ns-ppl.xsd"/>
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" type="lib:bookType"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="bookType">
    <xs:sequence>
      <xs:element name="isbn" type="xs:NMTOKEN"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="authors">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="ppl:person"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="characters">
        <xs:complexType>
          <xs:sequence>
            <xs:element ref="ppl:person" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
    <xs:attribute name="available" type="xs:string" use="required"/>
  </xs:complexType>
</xs:schema>

Attributes

id
W3C XML Schema's element ID.

namespace
Namespace URI of the components to import. If this attribute is missing, the imported components are expected to have no namespace. When present, its value must be different than the target namespace of the importing schema.

schemaLocation
Location of the schema to import. If this attribute is missing, the validator might expect to get the information from the application, or try to find it on the Internet.

xs:include Inclusion of a W3C XML Schema for the same target namespace.
<xs:include
           id             = xs:ID
           schemaLocation = xs:anyURI
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:include>
May be included in: xs:schema

Description

xs:include performs a straight inclusion of a schema within another schema that describes the same target namespace. It is possible, though, to include a schema that doesn't specify a target namespace. The included schema will be treated as adopting the target namespace of its includer in a schema with a target namespace. This possibility, often called "chameleon design," allows reuse of "transparent" schemas in the context of different namespaces.

The effect of a schema inclusion is a merge between the two schemas that are consolidated into a global schema. The schema that is included doesn't need to be a complete schema by itself.

Restrictions

It is not possible to use xs:include to include a schema describing another namespace. (xs:import should be used in this case.)

It is not possible to change the definitions of the included schemas. (xs:redefine should be used in this case.)

It is not possible to include a fragment of a schema. (General purpose inclusion mechanisms such as XInclude should be used in this case.)

Example

<xs:include schemaLocation="simple-types.xsd"/>

Attributes

id
W3C XML Schema's element ID.

schemaLocation
Location of the schema to include.

xs:key Definition of a key.
<xs:key
           id            = xs:ID
           name          = xs:NCName
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:selector, xs:field+))
</xs:key>
May be included in: xs:element (within xs:all), xs:element (reference or local definition), xs:element (global definition)

Description

xs:key is used to define simple or compound keys by unambiguously identifying each element of a selected list of subelements within the scope of a root element.

xs:key is very similar to xs:unique. Like the constraints defined with xs:unique, keys defined with xs:key are unique in the scope of their root element and may be referenced by xs:keyref. The only difference between xs:key and xs:unique is the keys must be defined for each of the elements in the selection list, while unique identifiers may be undefined for elements in the selection list.

The root element for the key is the element in which the key is defined. The location of the root element must be carefully chosen since the unicity of the key is checked only within the node elements that are the key's children. Defining a root element that has multiple occurrences within a document leaves the possibility to define local keys that are unique only in the scope of each occurrence of a document. When a key is global to a document, defining it using the document element as a root may cause it to be less error-prone.

The contribution of xs:key to the PSVI is a node table in each occurrence of the root key element that contains the list of the key sequences for this root element. The scope of these node tables is limited to the root element in which they are contained and its ancestors. This scope is used to determine the behavior of the key references and impacts the choice of the key root element when the key is referenced (see xs:keyref).

Restrictions

These XPath-based features do not follow the object-oriented, namespace-aware general philosophy of W3C XML Schema. The XPath expressions are specified independently of the element's content model and the constraints for the elements of all the possible namespaces must be defined in the schema for the namespace of the constraint root element.

Example

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:key name="book">
    <xs:selector xpath="book"/>
    <xs:field xpath="isbn"/>
  </xs:key>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

name
The name of the key.

xs:keyref Definition of a key reference.
<xs:keyref
           id            = xs:ID
           name          = xs:NCName
           refer         = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:selector, xs:field+))
</xs:keyref>
May be included in: xs:element (within xs:all), xs:element (reference or local definition), xs:element (global definition)

Description

xs:keyref is used to define a reference to a simple or compound key or to a unique constraint.

The syntax of xs:keyref is very similar to the syntax of xs:key or xs:unique. The only difference is the refer attribute, which must contain the name of the referred key or unique constraint.

The root element for the keyref is the element in which the key is defined. The key reference is often defined with the same root element as its referred key or unique constraint, in which case the matching is simple and straightforward.

The root element of the keyref may also be defined in any element for which it is in the scope of a matching constraint node table, i.e., in all the ancestors of the root element of the referred key or unique constraint. In this case, an additional check is performed; for each matching key reference, the validator should test that the key or unique value is defined only once in all the matching key or unique root elements.

Restrictions

Key references defined in an ancestor of their referenced key or unique constraint actually add additional constraints on the referenced key or the unique key, which may be unexpected. A set of identifiers that match a key or unique constraint may become invalid when a key reference is defined.

If, for instance, local keys are defined to identify characters within the scope of a book, these keys would allow the reuse of the same characters' identifiers in distinct books, such as in the following:

<book>
  <isbn> 0836217462 </isbn>
  <title> Being a Dog Is a Full-Time Job </title>
  <character id="PP">
    <name> Peppermint Patty </name>
    <qualification> bold, brash and tomboyish </qualification>
  </character>
  <character id="Snoopy">
    <name> Snoopy </name>
    <qualification> hero of the book </qualification>
  </character>
  <character id="Schroeder">
    <name> Schroeder </name>
    <qualification> brought classical music to the Peanuts strip</qualification>
  </character>
  <character id="Lucy">
    <name> Lucy </name>
    <qualification> bossy, crabby and selfish </qualification>
  </character>
</book>
<book>
  <isbn> 0805033106 </isbn>
  <title> Peanuts Every Sunday </title>
  <character id="Sally">
    <name> Sally Brown </name>
    <qualification> always looks for the easy way out </qualification>
  </character>
  <character id="Snoopy">
    <name> Snoopy </name>
    <qualification> extroverted beagle </qualification>
  </character>
</book>

If we add a key reference to reference these keys from the books, using the same identifier for characters in different books will be allowed if, and only if, these keys are not referenced.

This example is then valid:

<library>
  <book>
    <isbn> 0836217462 </isbn>
    <title> Being a Dog Is a Full-Time Job </title>
    <character id="PP">
      <name> Peppermint Patty </name>
      <qualification> bold, brash and tomboyish </qualification>
    </character>
    <character id="Snoopy">
      <name> Snoopy </name>
      <qualification> hero of the book </qualification>
    </character>
    <character id="Schroeder">
      <name> Schroeder </name>
      <qualification> brought classical music to the Peanuts strip </qualification>
    </character>
    <character id="Lucy">
      <name> Lucy </name>
      <qualification> bossy, crabby and selfish </qualification>
    </character>
  </book>
  <book>
    <isbn> 0805033106 </isbn>
    <title> Peanuts Every Sunday </title>
    <character id="Sally">
      <name> Sally Brown </name>
      <qualification> always looks for the easy way out </qualification>
    </character>
    <character id="Snoopy">
      <name> Snoopy </name>
      <qualification> extroverted beagle </qualification>
    </character>
  </book>
  <comment about="Sally"> Sally is cute </comment>
</library>

Because "Sally" is a unique key, it becomes invalid if we add a comment element referring to "Snoopy," which is mentioned twice.

A complete schema for this document might be the following (note how the xs:keyref refers to a xs:unique constraint, which is defined for a child of the current node):

<xs:schema elementFormDefault="qualified" xmlns:xs=
  "http://www.w3.org/2001/XMLSchema">
<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="book" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="isbn" type="xs:NMTOKEN"/>
            <xs:element name="title" type="xs:string"/>
            <xs:element name="character" maxOccurs="unbounded">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="name" type="xs:string"/>
                  <xs:element name="qualification" type="xs:string"/>
                </xs:sequence>
                <xs:attribute name="id" type="xs:string"/>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
        <xs:key name="character">
        <xs:selector xpath="character"/>
        <xs:field xpath="@id"/>
        </xs:key>
      </xs:element>
      <xs:element name="comment" minOccurs="0" maxOccurs="unbounded">
        <xs:complexType>
          <xs:simpleContent>
            <xs:extension base="xs:string">
            <xs:attribute name="about" type="xs:string"/>
            </xs:extension>
          </xs:simpleContent>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:unique name="book">
  <xs:selector xpath="book"/>
  <xs:field xpath="isbn"/>
  </xs:unique>
  <xs:keyref name="comment" refer="character">
  <xs:selector xpath="comment"/>
  <xs:field xpath="@about"/>
  </xs:keyref>
</xs:element>
</xs:schema>

Example

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:key name="ID">
    <xs:selector xpath="book|author|character"/>
    <xs:field xpath="@id"/>
  </xs:key>
  <xs:keyref name="IDREF" refer="ID">
    <xs:selector xpath="book/author-ref|book/character-ref"/>
    <xs:field xpath="@ref"/>
  </xs:keyref>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

name
Name of the key reference.

refer
Name of the key or unique constraint referred by the key reference.

xs:length Facet to define the length of a value.
<xs:length
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = xs:nonNegativeInteger
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:length>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:anyURI, xs:base64Binary, xs:ENTITIES, xs:ENTITY, xs:hexBinary, xs:ID, xs:IDREF, xs:IDREFS, xs:language, xs:Name, xs:NCName, xs:NMTOKEN, xs:NMTOKENS, xs:normalizedString, xs:NOTATION, xs:QName, xs:string, xs:token

Description

xs:length is a facet that allows the definition of the length expressed in a unit that depends on the datatype. For most of the datatypes, the unit is the character as defined in the XML 1.0 Recommendation (i.e., Unicode characters defined by ISO/IEC 10646 that may be represented on more than 8 bits). The exceptions are the binary datatypes (xs:hexBinary and xs:base64Binary), for which lengths are expressed in number of bytes (8 bits) of binary data, and all the list datatypes, for which lengths are expressed in number of list items.

xs:length constrains the value space. In practice, this means that it is checked after whitespace replacement and collapsing, as defined by the xs:whiteSpace facet.

Restrictions

This is a logical length that has often no direct relation to the size of storage needed for the value.

The value of this facet cannot be modified during further restrictions, and the value of the fixed attribute is meaningless (the behavior is always as if the facet were fixed).

Although also not specified in the Recommendation, xs:length interacts with the xs:minLength and xs:maxLength and shouldn't set the length to a value not in the range between xs:minLength and xs:maxLength of its base type.

Fixing the xs:length facet doesn't fix the xs:maxLength and xs:minLength facets. To fix the three facets, define two restriction steps, since it is forbidden to apply these facets in the same restriction step.

Example

<xs:simpleType name="standardNotations">
  <xs:restriction base="xs:NOTATION">
    <xs:length value="8"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to true, the value of the length cannot be modified during further restrictions (meaningless for this facet whose value can never be modified in further restrictions).

id
W3C XML Schema's element ID.

value
Value of the facet.

xs:list Derivation by list.
<xs:list
           id               = xs:ID
           itemType         = xs:QName
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:simpleType?))
</xs:list>
May be included in: xs:simpleType (local definition), xs:simpleType (global definition)

Description

Derivation by list is the process of transforming a simple datatype (named the item type) into a whitespace-separated list of values from this datatype.

The item type can be defined inline by adding a simpleType definition as a child element of the list element, or by reference, using the itemType attribute (it is an error to use both).

The semantic and list of facets applicable on the item type are lost and the new datatype inherits a list of facets, which is common to all the list datatypes: xs:enumeration, xs:length, xs:maxLength, xs:minLength, and xs:pattern and its whitespaces are always collapsed.

After a derivation by list, the type of the resulting datatype is a list of whatever the item type was (atomic or union).

It is possible to define lists of atomic datatypes that allow whitespaces such as xs:string. In this case, the whitespaces are always considered separators.

Restrictions

The facets that can be applied to a list datatype are common to all the list datatypes, and add constraints to the list as a whole (rather than on the items composing the list). The constraints on the item composing the lists, therefore, need to be applied before the derivation by list.

The list separators cannot be chosen and are always whitespaces.

It is explicitly forbidden by the Recommendation to define lists of lists. Lists of unions and unions of lists are both allowed, so it should be possible to work around this limitation by defining a dummy union of a list and using this union as a list item if needed. However, lists of lists are effectively pointless, since all the list types have the same set of separators.

Example

<xs:simpleType name="integerList">
  <xs:list itemType="xs:integer"/>
</xs:simpleType>

Attributes

id
W3C XML Schema's element ID.

itemType
Reference to the item type when not defined inline by a xs:simpleType element.

xs:maxExclusive Facet to define a maximum (exclusive) value.
<xs:maxExclusive
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = anySimpleType
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:maxExclusive>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for:xs:byte, xs:date, xs:dateTime, xs:decimal, xs:double, xs:duration, xs:float, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:gYearMonth, xs:int, xs:integer, xs:long, xs:negativeInteger, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:positiveInteger, xs:short, xs:time, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, xs:unsignedShort

Description

xs:maxExclusive defines an exclusive maximum value. To be valid, a value must be strictly less than the value of xs:maxExclusive. This facet constrains the value space.

Restrictions

It is forbidden to define both xs:maxExclusive and xs:maxInclusive in the same restriction step. Although not explicitly specified in the Recommendation, it doesn't make sense to define several xs:maxExclusive facets in a single restriction step also.

Within a restriction step, xs:maxExclusive is also dependent on xs:minExclusive and xs:minInclusive, since using inconsistent values leads to datatypes with empty value spaces.

xs:maxExclusive must restrict the value space of its base type, and its value must be in the value space of the base type.

Fixing the maxExclusive facet doesn't fix the xs:maxInclusive facet. To fix both facets, define two restriction steps since it is forbidden to apply these two facets in the same restriction step.

Example

<xs:simpleType name="myInteger">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="-2"/>
    <xs:maxExclusive value="5"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to fixed, the value of this facet cannot be changed in further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet (must be in the value space of the base type).

xs:maxInclusive Facet to define a maximum (inclusive) value.
<xs:maxInclusive
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = anySimpleType
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:maxInclusive>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:byte, xs:date, xs:dateTime, xs:decimal, xs:double, xs:duration, xs:float, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:gYearMonth, xs:int, xs:integer, xs:long, xs:negativeInteger, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:positiveInteger, xs:short, xs:time, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, xs:unsignedShort

Description

xs:maxInclusive defines an inclusive maximum value. To be valid, a value must be less than or equal to the value of xs:maxInclusive.

This facet constrains the value space.

Restrictions

It is forbidden to define both xs:maxExclusive and xs:maxInclusive in the same restriction step. Although not explicitly specified in the Recommendation, it doesn't make sense to define several xs:maxInclusive facets in a single restriction step either.

Within a restriction step, xs:maxInclusive is also dependent on xs:minExclusive and xs:minInclusive, since using inconsistent values leads to datatypes with empty value spaces.

xs:maxInclusive must restrict the value space of its base type, and its value must be in the value space of the base type.

Fixing the maxInclusive facet doesn't fix the xs:maxExclusive facet. To fix both facets, define two restriction steps since it is forbidden to apply these two facets in the same restriction step.

Example

<xs:simpleType name="thousands">
  <xs:restriction base="xs:double">
    <xs:maxInclusive value="1e3"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to fixed, the value of this facet cannot be changed in further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet (must be in the value space of the base type).

xs:maxLength Facet to define a maximum length.
<xs:maxLength
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = xs:nonNegativeInteger
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:maxLength>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for:xs:anyURI, xs:base64Binary, xs:ENTITIES, xs:ENTITY, xs:hexBinary, xs:ID, xs:IDREF, xs:IDREFS, xs:language, xs:Name, xs:NCName, xs:NMTOKEN, xs:NMTOKENS, xs:normalizedString, xs:NOTATION, xs:QName, xs:string, xs:token

Description

xs:maxLength is a facet that allows the definition of the maximum length expressed in a unit that depends on the datatype. For most of the datatypes, the unit is a character as defined in the XML 1.0 Recommendation (i.e., Unicode characters defined by ISO/IEC 10646 that may be represented on more than 8 bits). The exceptions are the binary datatypes (xs:hexBinary and xs:base64Binary), for which lengths are expressed in number of bytes (8 bits) of binary data, and all the list datatypes, for which lengths are expressed in number of list items.

xs:maxLength constrains the value space. In practice, this means that it is checked after whitespace replacement and collapsing, as defined by the xs:whiteSpace facet.

Restrictions

This is a logical length, which often has no direct relation to the size of storage needed for the value.

It is forbidden to define both xs:maxLength and xs:length in the same restriction step. Although not explicitly specified in the Recommendation, it doesn't make sense to define several xs:maxLength facets in a single restriction step either.

Within a restriction step, xs:maxLength is also dependent on xs:minLength, since using inconsistent values leads to datatypes with empty value spaces.

xs:maxLength must restrict the value space of its base type, and its value must be smaller than the value of xs:maxLength of its base type, if defined.

Although not specified in the Recommendation, xs:maxLength interacts with the xs:length and shouldn't be used if xs:length is defined for its base type.

Fixing the xs:maxLength facet doesn't fix the xs:length facet. To fix both facets, define two restriction steps since it is forbidden to apply these two facets in the same restriction step.

Example

<xs:simpleType name="binaryImage">
  <xs:restriction base="xs:hexBinary">
    <xs:maxLength value="1024"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to true, the value of the facet cannot be modified during further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet.

xs:minExclusive Facet to define a minimum (exclusive) value.
<xs:minExclusive
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = anySimpleType
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:minExclusive>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:byte, xs:date, xs:dateTime, xs:decimal, xs:double, xs:duration, xs:float, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:gYearMonth, xs:int, xs:integer, xs:long, xs:negativeInteger, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:positiveInteger, xs:short, xs:time, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, xs:unsignedShort

Description

xs:minExclusive defines an exclusive minimum value. To be valid, a value must be strictly greater than the value of xs:minExclusive.

This facet constrains the value space.

Restrictions

It is forbidden to define both xs:minExclusive and xs:minInclusive in the same restriction step. Although not explicitly specified in the Recommendation, it doesn't make sense to define several xs:minExclusive facets in a single restriction step either.

Within a restriction step, xs:minExclusive is also dependent on xs:maxExclusive and xs:maxInclusive, since using inconsistent values leads to datatypes with empty value spaces.

xs:minExclusive must restrict the value space of its base type, and its value must be in the value space of the base type.

Fixing the xs:minExclusive facet doesn't fix the xs:minInclusive facet. To fix both facets, define two restriction steps since it is forbidden to apply these two facets in the same restriction step.

Example

<xs:simpleType name="afterTeaTimeInParisInSummer">
  <xs:restriction base="xs:time">
    <xs:minExclusive value="17:00:00+02:00"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to fixed, the value of this facet cannot be changed in further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet (must be in the value space of the base type).

xs:minInclusive Facet to define a minimum (inclusive) value.
<xs:minInclusive
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = anySimpleType
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:minInclusive>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:byte, xs:date, xs:dateTime, xs:decimal, xs:double, xs:duration, xs:float, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:gYearMonth, xs:int, xs:integer, xs:long, xs:negativeInteger, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:positiveInteger, xs:short, xs:time, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, xs:unsignedShort

Description

xs:minInclusive defines an inclusive minimum value. To be valid, a value must be greater than or equal to the value of xs:minInclusive.

This facet constrains the value space.

Restrictions

It is forbidden to define both xs:minInclusive and xs:minExclusive in the same restriction step. Although not explicitly specified in the Recommendation, it doesn't make sense to define several xs:minInclusive facets in a single restriction step either.

Within a restriction step, xs:minInclusive is also dependent on xs:maxExclusive and xs:maxInclusive, since using inconsistent values leads to datatypes with empty value spaces.

xs:minInclusive must restrict the value space of its base type, and its value must be in the value space of the base type.

Fixing the xs:minInclusive facet doesn't fix the xs:minExclusive facet. To fix both facets, define two restriction steps since it is forbidden to apply these two facets in the same restriction step.

Example

<xs:simpleType name="positive">
  <xs:restriction base="xs:double">
    <xs:minInclusive value="0"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to fixed, the value of this facet cannot be changed in further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet (must be in the value space of the base type).

xs:minLength Facet to define a minimum length.
<xs:minLength
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = xs:nonNegativeInteger
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:minLength>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:anyURI, xs:base64Binary, xs:ENTITIES, xs:ENTITY, xs:hexBinary, xs:ID, xs:IDREF, xs:IDREFS, xs:language, xs:Name, xs:NCName, xs:NMTOKEN, xs:NMTOKENS, xs:normalizedString, xs:NOTATION, xs:QName, xs:string, xs:token

Description

xs:minLength is a facet that allows definition of the minimum length expressed in a unit that depends on the datatype. For most of the datatypes, the unit is the character as defined in the XML 1.0 Recommendation (i.e., Unicode characters defined by ISO/IEC 10646 that may be represented on more than 8 bits). The exceptions are the binary datatypes (xs:hexBinary and xs:base64Binary), for which lengths are expressed in number of bytes (8 bits) of binary data, and all the list datatypes, for which lengths are expressed in number of list items.

xs:minLength constrains the value space. In practice, this means that it is checked after whitespace replacement and collapsing, as defined by the xs:whiteSpace facet.

Restrictions

This is a logical length, which often has no direct relation on the size of storage needed for the value.

It is forbidden to define both xs:minLength and xs:length in the same restriction step. Although not explicitly specified in the Recommendation, it doesn't make sense to define several xs:minLength facets in a single restriction step either.

Within a restriction step, xs:minLength is also dependent on xs:maxLength, since using inconsistent values leads to datatypes with empty value spaces.

xs:minLength must restrict the value space of its base type, and its value must be greater than the value of xs:minLength of its base type if defined.

Although not specified in the Recommendation, xs:minLength interacts with xs:length and shouldn't be used if xs:length is defined for its base type.

Fixing the xs:minLength facet doesn't fix the xs:length facet. To fix both facets, define two restriction steps since it is forbidden to apply these two facets in the same restriction step.

Example

<xs:simpleType name="longName">
  <xs:restriction base="xs:NCName">
    <xs:minLength value="6"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to true, the value of the facet cannot be modified during further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet.

xs:notation Declaration of a notation.
<xs:notation
           id            = xs:ID
           name          = xs:NCName
           public        = xs:token
           system        = xs:anyURI
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:notation>
May be included in: xs:schema

Description

xs:notation is used to declare a notation just like the NOTATION declarations in DTDs. The main difference is that W3C XML Schema notations are namespace-aware and can be imported between schemas. When these declarations are used, the notations are used in xs:enumeration facets to create simple types.

Restrictions

Notations are very seldom used in real world applications.

Example

<xs:notation name="jpeg" public="image/jpeg"
  system="file:///usr/bin/xv"/>

<xs:notation name="gif" public="image/gif"
  system="file:///usr/bin/xv"/>

<xs:notation name="png" public="image/png"
  system="file:///usr/bin/xv"/>

<xs:notation name="svg" public="image/svg"
  system="file:///usr/bin/xsmiles"/>

<xs:notation name="pdf" public="application/pdf"
  system="file:///usr/bin/acroread"/>

<xs:simpleType name="graphicalFormat">
  <xs:restriction base="xs:NOTATION">
    <xs:enumeration value="jpeg"/>
    <xs:enumeration value="gif"/>
    <xs:enumeration value="png"/>
    <xs:enumeration value="svg"/>
    <xs:enumeration value="pdf"/>
  </xs:restriction>
</xs:simpleType>

Attributes

id
W3C XML Schema's element ID.

name
Name of the notation (unqualified).

public
Public identifier (usually its content type).

system
System identifier (typically the location of a resource that might be used to process the content type associated with the notation).

xs:pattern Facet to define a regular expression pattern constraint.
<xs:pattern
           id             = xs:ID
           value          = anySimpleType
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:pattern>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for:xs:anyURI, xs:base64Binary, xs:boolean, xs:byte, xs:date, xs:dateTime, xs:decimal, xs:double, xs:duration, xs:ENTITY, xs:float, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:gYearMonth, xs:hexBinary, xs:ID, xs:IDREF, xs:int, xs:integer, xs:language, xs:long, xs:Name, xs:NCName, xs:negativeInteger, xs:NMTOKEN, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:normalizedString, xs:NOTATION, xs:positiveInteger, xs:QName, xs:short, xs:string, xs:time, xs:token, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, xs:unsignedShort

Description

xs:pattern allows the definition of regular expression patterns over the lexical space. The syntax used for these patterns has been borrowed from Perl regular expressions, and has been enhanced to support the Unicode character blocks.

When multiple xs:pattern facets are defined in a single derivation step, a value is considered valid if it matches at least one of the patterns, meaning that a logical or is performed on all the patterns defined in a same derivation step.

Patterns are applied to the lexical space of the base datatype, meaning that a logical and is performed on patterns defined in different derivation steps.

The syntax of these patterns is explained in more detail in Chapter 6, "Using Regular Expressions to Specify Simple Datatypes".

Restrictions

xs:pattern is a common workaround to define datatypes that look like datatypes missing from the list of predefined datatypes. It is possible, for instance, to simulate decimals using a separator other than the decimal point with a pattern. Such datatypes need to be based on xs:token, and will still not have the semantic of xs:decimal and its list of facets.

xs:pattern cannot be fixed.

Example

<xs:simpleType name="nonScientific">
  <xs:restriction base="xs:float">
    <xs:pattern value="[^eE]*"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="noLeading0">
  <xs:restriction base="xs:float">
    <xs:pattern value="[^0].*"/>
  </xs:restriction>
</xs:simpleType>

Attributes

id
W3C XML Schema's element ID.

value
Value of the facet.

xs:redefine Inclusion of a W3C XML Schema for the same namespace with possible override.
<xs:redefine
           id             = xs:ID
           schemaLocation = xs:anyURI
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation | (xs:simpleType | xs:complexType | xs:group |
                      xs:attributeGroup))*
</xs:redefine>
May be included in: xs:schema

Description

xs:redefine is similar to xs:include with one exception: the definition of simple and complex types and attribute and element groups can be changed. Changes performed on these components must lead to components that are valid restrictions or extensions of the original ones.

Simple and complex types are redefined using the usual mechanism of derivation: the new types must be defined as derivations by restriction or extension of their previous definition, which is considered to be the base type.

Element and attribute groups are redefined by giving a new definition, and may refer to themselves to indicate that the content of their original definition must be included. In this case, this self-reference is not considered a recursive definition (which is forbidden in element and attribute groups), but an extension of the original group.

When xs:redefine is used and no redefinitions are specified, its effect is similar to xs:include.

Restrictions

xs:redefine can be used only to include schemas with either the same target namespace or no target namespace. There is no equivalent to import and redefine schema definitions in a single operation. The schema must be redefined and imported in two different operations.

Arbitrary redefinitions are not allowed. After redefinition, the components must be valid derivations (by extension or restriction) of their definitions before redefinition.

xs:element, xs:attribute, and xs:notation cannot be redefined.

Example

<xs:redefine schemaLocation="complex-types2.xsd">
  <xs:complexType name="book">
    <xs:complexContent>
      <xs:restriction base="book">
        <xs:sequence>
          <xs:element ref="isbn"/>
          <xs:element ref="title"/>
          <xs:element ref="author" minOccurs="0"
            maxOccurs="unbounded"/>
          <xs:element ref="character" minOccurs="0"
            maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="id" type="bookID"/>
        <xs:attribute ref="available"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:redefine>

Attributes

id
W3C XML Schema's element ID.

schemaLocation
Location of the schema to redefine.

xs:restriction(simple type) Derivation of a simple datatype by restriction.
<xs:restriction
           base          = xs:QName
           id            = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:simpleType?, (xs:minExclusive |
                      xs:minInclusive | xs:maxExclusive | xs:maxInclusive |
                      xs:totalDigits | xs:fractionDigits | xs:length |
                      xs:minLength | xs:maxLength | xs:enumeration |
                      xs:whiteSpace | xs:pattern)*))
</xs:restriction>
May be included in: xs:simpleType (local definition), xs:simpleType (global definition)

Description

Deriving a simple datatype by restriction is the action of defining a new datatype by adding constraints (called facets) on a base datatype to restrict its lexical and value spaces.

The base datatype can be defined by reference (using the base attribute) or inline (through a xs:simpleType element defining a local datatype).

Each of the facets is defined as a child element after the optional embedded xs:simpleType definition.

Each facet has its own meaning and depending on the facet, acts on the lexical space, on the value space, or on the whitespace transformation performed between the lexical and value spaces. Depending on the base datatype, a facet may also have a different behavior. See the detailed description of each facet in this chapter for more information.

Derivations by restrictions can be applied to any type of datatype (atomic, list, or union), and don't change the type of the datatype (atomic, list, and union datatypes stay atomic, list, or union, after a derivation by restriction).

Restrictions

The same element name (xs:restriction) is used in three different contexts with highly different meanings. Be careful to distinguish restriction in the context of a simple datatype, a simple content model, and a complex content model.

The base attribute and the xs:simpleType embedded definition must not be used together (the base type is defined either by reference or embedded).

Example

<xs:simpleType name="myInteger">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="-2"/>
    <xs:maxExclusive value="5"/>
  </xs:restriction>
</xs:simpleType>

Attributes

base
Qualified name of the base datatype when defined by reference.

id
W3C XML Schema's element ID.

xs:restriction(simple content) Derivation of a simple content model by restriction.
<xs:restriction
           base          = xs:QName
           id            = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:simpleType?, (xs:minExclusive |
                      xs:minInclusive | xs:maxExclusive | xs:maxInclusive |
                      xs:totalDigits | xs:fractionDigits | xs:length |
                      xs:minLength | xs:maxLength | xs:enumeration | xs:whiteSpace |
                      xs:pattern)*)?, ((xs:attribute | xs:attributeGroup)*,
                      xs:anyAttribute?))
</xs:restriction>
May be included in: xs:simpleContent

Description

The restriction of simple content complex types is an operation that allows the addition of new constraints to both the attributes and the text node allowed in simple content elements (this reduces the set of valid instances structures). The restriction of the text node is done using the same facets that are available for restricting simple types; the restriction of the attributes is done by providing a list of modified attribute definitions or references. The attribute types used in this list must be identical or explicit derivations by restriction of the types used during the definition of the base type. The controls applied to the attributes must result in a restriction of their definition (for instance, a value may be fixed, an attribute that was optional may become either required or prohibited, etc.).

Restrictions

The same element name (xs:restriction) is used in three different contexts with highly different meanings. Be careful to distinguish restriction in the context of a simple datatype, a simple content model, and a complex content model.

Example

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:restriction base="tokenWithLangAndNote">
        <xs:maxLength value="255"/>
        <xs:attribute name="lang">
          <xs:simpleType>
            <xs:restriction base="xs:language">
              <xs:enumeration value="en"/>
              <xs:enumeration value="es"/>
            </xs:restriction>
          </xs:simpleType>
        </xs:attribute>
      </xs:restriction>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

Attributes

base
Qualified name of the base type.

id
W3C XML Schema's element ID.

xs:restriction(complex content) Derivation of a complex content model by restriction.
<xs:restriction
           base          = xs:QName
           id            = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:group | xs:all | xs:choice | xs:sequence)?,
                      ((xs:attribute | xs:attributeGroup)*, xs:anyAttribute?))
</xs:restriction>
May be included in: xs:complexContent

Description

The restriction of a complex content complex type is an operation that allows the addition of new constraints to both the attributes and child elements in complex content elements (this reduces the set of valid instances structures). It can be seen as a declaration of intention that a new content model is a subset of a base content model (the declaration by intention is checked by the schema processors). The new content model is entirely described under xs:restriction and must represent a restriction to what was allowed by the base content model (i.e., any content valid per the restricted type must also be valid per the base type). The only exception is attributes whose description may be skipped under xs:restriction, if not changed.

Restrictions

To facilitate the checking done by schema processors, the new derived content model must follow the definition of the base type particle per particle. Each of them must be explicit derivations by restriction.

The same element name (xs:restriction) is used in three different contexts with highly different meanings. Be careful to be able to distinguish restrictions in the context of a simple datatype, a simple content model, and a complex content model.

Example

<xs:element name="author">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="person">
        <xs:sequence>
          <xs:element ref="name"/>
          <xs:element ref="born"/>
          <xs:element ref="dead" minOccurs="0"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

Attributes

base
Qualified name of the base type.

id
W3C XML Schema's element ID.

xs:schema Document element of a W3C XML Schema.
<xs:schema
           attributeFormDefault = ( "qualified" | "unqualified" ) : "unqualified"
           blockDefault         = ( "#all" | list of ( "extension" | "restriction" |
                                    "substitution" ) ) : ""
           elementFormDefault   = ( "qualified" | "unqualified" ) : "unqualified"
           finalDefault         = ( "#all" | list of ( "extension" | "restriction" )
                                    ) : ""
           id                   = xs:ID
           targetNamespace      = xs:anyURI
           version              = xs:token
           xml:lang             = xml:lang
           {any attributes with non-schema namespace}
           >
           Content: ((xs:include | xs:import | xs:redefine | xs:annotation)*, (,
                      ( | (xs:simpleType | xs:complexType | xs:group |
                      xs:attributeGroup) | xs:element | xs:attribute |
                      xs:notation), xs:annotation*)*)
</xs:schema>
May be included in: (must be root element)

Description

xs:schema is the document (root) element of any W3C XML Schema. It's both a container for all the declarations and definitions of the schema and a place holder for a number of default values expressed as attributes. The compositors embedded directly under xs:schema have a special role, since they are considered global definitions that can be referenced elsewhere.

Restrictions

A xs:schema element (and therefore a schema) is associated with a maximum of one namespace and must import the definitions of compositors for other namespaces if needed.

Example

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="name" type="xs:string"/>
  <xs:element name="qualification" type="xs:string"/>
  <xs:element name="born" type="xs:date"/>
  <xs:element name="dead" type="xs:date"/>
  <xs:element name="isbn" type="xs:NMTOKEN"/>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="available" type="xs:boolean"/>
  <xs:attribute name="lang" type="xs:language"/>
  .../...
</xs:schema>

Attributes

attributeFormDefault
Default value for the form attributes of xs:attribute, determining whether attributes will be namespace-qualified by default.

blockDefault
Default value of the block attribute of xs:element and xs:complexType.

elementFormDefault
Default value for the form attributes of xs:element, determining whether attributes will be namespace-qualified by default

finalDefault
Default value of the final attribute of xs:element and xs:complexType.

id
W3C XML Schema's element ID.

targetNamespace
Namespace attached to this schema. All the qualified elements and attributes defined in this schema will belong to this namespace. This namespace will also be attached to all the global components.

version
Version of the schema (for user convenience).

xml
Language of the schema.

xs:selector Definition of the the path selecting an element for a uniqueness constraint.
<xs:selector
           id             = xs:ID
           xpath          = xs:token
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:selector>
May be included in: xs:key, xs:keyref, xs:unique

Description

xs:selector is used to define the element on which a uniqueness constraint or reference is checked (it cannot be an attribute, since attributes are already unique per XML 1.0). It is identified by a relative XPath expression.

The constraint is checked while looping on the XPath expression is resolved relative to the root element of the constraint. It serves as the location from which xs:field XPath expressions are resolved.

Restrictions

The xpath attribute uses a simple subset of XPath 1.0. The motivation of the W3C XML Schema Working Group for defining this subset is to simplify the work of the implementers of schema processors, and also to define a subset that constrains the path to stay within the scope of the current element.

This subset is restricted to use the child only; self and descendant, and self XPath axes through their abbreviated syntaxes without including any test in any of the XPath location steps and without using any XPath functions. It is identical to the subset defined for xs:field, except that attributes are allowed in xs:field and forbidden in xs:selector.

The formal extended BNF given in the W3C Recommendation is as follow:

Selector ::= Path ( '|' Path )* Path ::= ('.//')? Step ( '/' Step )* Step ::= '.' |
NameTest NameTest ::= QName | '*' | NCName ':' '*'

Example

<xs:element name="book" maxOccurs="unbounded">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:unique name="book">
    <xs:selector xpath="book"/>
    .../...
  </xs:unique>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

xpath
Relative XPath expression identifying the element on which the constraint applies.

xs:sequence(outside a group) Compositor to define an ordered group of elements.
<xs:sequence
           id                 = xs:ID
           maxOccurs          = ( xs:nonNegativeInteger | "unbounded" ) : "1"
           minOccurs          = xs:nonNegativeInteger : "1"
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:element | xs:group | xs:choice |
                      xs:sequence | xs:any) )*)
</xs:sequence>
May be included in: xs:choice (outside a group), xs:choice (within a group), xs:complexType (local definition), xs:complexType (global definition), xs:extension (complex content), xs:restriction (complex content), xs:sequence (within a group), xs:sequence (outside a group)

Description

This compositor is used to define an ordered list or set of elements. It can hold its own minOccurs and maxOccurs attributes to define a number of occurrences of the whole sequence.

Restrictions

xs:sequence may not be included in an xs:all compositor, and xs:sequence may not include xs:all compositors.

When xs:sequence is included in an xs:group compositor, its content model is slightly different (see xs:sequence(within a group) (within a group)).

Example

<xs:element name="library">
  <xs:complexType>
    <xs:sequence>
      <xs:element ref="book" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

maxOccurs
Maximum number of occurrences.

minOccurs
Minimum number of occurrences.

xs:sequence(within a group) Compositor to define an ordered group of elements. The number of occurrences cannot be defined when xs:all is used within a group.
<xs:sequence
           id          = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:element | xs:group | xs:choice |
                      xs:sequence | xs:any) )*)
</xs:sequence>
May be included in: xs:group (definition)

Description

This compositor is used to define ordered list of elements.

Restrictions

xs:sequence may not include xs:all compositors.

When xs:sequence is used outside an xs:group compositor, its content model is slightly different (see xs:sequence(outside a group) (outside a group)).

Example

<xs:group name="name">
  <xs:sequence>
    <xs:element ref="name"/>
    <xs:sequence minOccurs="0">
      <xs:element ref="middle-name" minOccurs="0"/>
      <xs:element ref="last-name"/>
    </xs:sequence>
  </xs:sequence>
</xs:group>

Attributes

id
W3C XML Schema's element ID.

xs:simpleContent Simple content model declaration.
<xs:simpleContent
           id          = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:restriction | xs:extension))
</xs:simpleContent>
May be included in: xs:complexType (local definition), xs:complexType (global definition)

Description

This element allows the definition of a simple content model by derivation of a simple type or of another simple content complex type. It is not a component by itself (simple contents are not named) but rather declaration of the intention to define a simple content model by derivation.

The derivation method is not defined by the xs:simpleContent element, but by the choice of its child element (which is xs:restriction for a derivation by restriction, or xs:extension for a derivation by extension).

Restrictions

None.

Example

<xs:element name="title">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute ref="lang"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

xs:simpleType(global definition) Global simple type declaration that can be referenced within the same schema by other schemas.
<xs:simpleType
           final         = ( "#all" | ( "list" | "union" | "restriction" ) )
           id            = xs:ID
           name          = xs:NCName
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:restriction | xs:list | xs:union))
</xs:simpleType>
May be included in: xs:redefine, xs:schema

Description

Global user-defined simple datatypes are defined directly under the xs:schema document element (or redefined using xs:redefine) outside of the scope of any specific content model. They can be referenced through their qualified name in the same schema or by any other schema that has included or imported this schema.

Like local simple datatypes, these are defined by derivation from other simple datatypes (either predefined by W3C XML Schema or other user-defined datatypes) using one of the three derivation methods available for simple types: xs:restriction to add new constraints to a datatype, xs:list to define lists of values, and xs:union to perform the union of the lexical spaces of several datatypes).

The name attribute is unqualified since the datatype is considered to belong to the target namespace of the schema in which it is defined. During a reference, the prefix that has been defined for this namespace needs to be added.

Although simple datatypes are considered to belong to the target namespace of schema in which they are defined, they can be referenced to create new datatypes, attributes, or elements for any namespace.

Further derivations (by list, union, restriction, or all) can be blocked using the final attribute.

Restrictions

W3C XML Schema doesn't provide any mechanism to add new primary datatypes, new facets, or to act on the transformation performed between the lexical and value spaces (for instance, it is not possible to change the representation of the decimal point).

Example

<xs:simpleType name="totalDigits">
  <xs:restriction base="xs:integer">
    <xs:totalDigits value="5"/>
  </xs:restriction>
</xs:simpleType>

Attributes

final
When set, this attribute blocks any further derivations of this datatype (by list, union, derivation, or all).

id
W3C XML Schema's element ID.

name
Unqualified name of this datatype.

xs:simpleType(local definition) Local simple type definition (local definitions cannot be referenced).
<xs:simpleType
           id          = xs:ID
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?, (xs:restriction | xs:list | xs:union))
</xs:simpleType>
May be included in: xs:attribute (reference or local definition), xs:attribute (global definition), xs:element (within xs:all), xs:element (reference or local definition), xs:element (global definition), xs:list, xs:restriction (simple type), xs:restriction (simple content), xs:union

Description

Local simple datatypes are defined directly where needed (during element, attribute, or other simple type definitions) for local anonymous usage, and cannot be referenced as such in other places of a schema.

Like global simple datatypes, these are defined by derivation from other simple datatypes (either predefined by W3C XML Schema or other user-defined datatypes) using one of the three derivation methods available for simple types: xs:restriction to add new constraints to a datatype, xs:list to define lists of values, and xs:union to perform the union of the lexical spaces of several datatypes).

Restrictions

W3C XML Schema doesn't provide any mechanism for creating new primary datatypes, new facets, or to act on the transformation performed between the lexical and value spaces (for instance, it is not possible to change the representation of the decimal point).

Example

<xs:simpleType name="myInteger">
  <xs:restriction>
    <xs:simpleType>
      <xs:restriction base="xs:integer">
        <xs:maxExclusive value="5"/>
      </xs:restriction>
    </xs:simpleType>
    <xs:minInclusive value="-2"/>
  </xs:restriction>
</xs:simpleType>

Attributes

id
W3C XML Schema's element ID.

xs:totalDigits Facet to define the total number of digits of a numeric datatype.
<xs:totalDigits
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = xs:positiveInteger
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:totalDigits>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for: xs:byte, xs:decimal, xs:int, xs:integer, xs:long, xs:negativeInteger, xs:nonNegativeInteger, xs:nonPositiveInteger, xs:positiveInteger, xs:short, xs:unsignedByte, xs:unsignedInt, xs:unsignedLong, xs:unsignedShort

Description

xs:totalDigits defines the maximum number of digits of decimal and derived datatypes (both after and before the decimal point, not counting the decimal point itself).

This facet constrains the value space, which means that the number of digits is checked after the value is transformed to its canonical form and the leading and trailing zeros are removed.

Restrictions

It is also possible to use xs:pattern to constrain the number of total digits in the lexical space.

Within a restriction step, xs:totalDigits is dependent on xs:fractionDigits since using inconsistent values leads to datatypes with empty value spaces.

xs:totalDigits must restrict the value space of its base type. Its value must be smaller than the value of the xs:totalDigits of its base type if defined.

Example

<xs:simpleType name="totalDigits">
  <xs:restriction base="xs:integer">
    <xs:totalDigits value="5"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to true, the value of the facet cannot be modified during further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet.

xs:union Derivation of simple datatypes by union.
<xs:union
           id          = xs:ID
           memberTypes = list of xs:QName
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:simpleType*))
</xs:union>
May be included in: xs:simpleType (local definition), xs:simpleType (global definition)

Description

Deriving a simple datatype by union merges the lexical spaces of several simple datatypes (called member types) to create a new simple datatype.

Whatever the type (atomic, list, or union) of the member types, the resulting datatype has a type union.

The member types can be defined either by reference (through the memberTypes attribute) or embedded as simple datatype local definitions in the xs:union element. Both styles can be mixed.

The semantic of the member datatypes is lost at least as far as validation is concerned, and the only facets that can be further applied to the resulting datatype are xs:pattern and xs:enumeration.

However, the semantic of the member datatypes isn't completely lost for an application built on a PSVI, since the validator needs to perform canonicalization according to the first member type that matches the instance value. This implies that order matters in the list of member types. When both styles are used, the datatypes referenced in the memberTypes are tested before the locally defined datatypes.

Restrictions

None.

Example

<xs:simpleType name="integerOrData">
  <xs:union memberTypes="xs:integer xs:date"/>
</xs:simpleType>

Attributes

id
W3C XML Schema's element ID.

memberTypes
List of member types (member types can also be embedded as xs:simpleType in the xs:union element).

xs:unique Definition of a uniqueness constraint.
<xs:unique
           id            = xs:ID
           name          = xs:NCName
           {any attributes with non-schema namespace}
           >
           Content: ((xs:annotation?), (xs:selector, xs:field+))
</xs:unique>
May be included in: xs:element (within xs:all), xs:element (reference or local definition), xs:element (global definition)

Description

xs:unique is used to define simple or compound constraints, which unambiguously identify each element in which they are present, from a selected list of subelements within the scope of a root element.

xs:unique is very similar to xs:key. Like the constraints defined with xs:key, constraints defined with xs:unique are unique in the scope of their root element and may be referenced by xs:keyref. The only difference between xs:unique and xs:key is that the xs:unique keys may be undefined in any of the elements in the selection list, while xs:key identifiers must be defined for all the elements in the selection list.

The root element for the constraint is the element in which the constraint is defined. The location of the root element must be carefully chosen since the unity of the constraint is checked only within the node elements that are its children. Defining a root element that has multiple occurrences within a document leaves the possibility of defining local constraints that are unique only in the scope of each occurrence of a document. When a constraint is global to a document, defining it using the document element as a root may cause it to be less error-prone.

The contribution of xs:unique to the PSVI is a node table in each occurrence of the root key element that contains the list of the key sequences for this root element. The scope of these node tables is limited to the root element in which they are contained and all its ancestors. This scope is used to determine the behavior of the key references and impacts the choice of the constraint root element when the constraint is referenced (see the section xs:keyref).

Restrictions

These XPath-based features do not follow the object-oriented, namespace-aware general philosophy of W3C XML Schema. The XPath expressions are specified independently of the element's content model, and the constraints for the elements of all the possible namespaces must be defined in the schema for the namespace of the constraint root element.

Example

<xs:element name="library">
  <xs:complexType>
    .../...
  </xs:complexType>
  <xs:unique name="book">
    <xs:selector xpath="book"/>
    <xs:field xpath="isbn"/>
  </xs:unique>
</xs:element>

Attributes

id
W3C XML Schema's element ID.

name
Name of the unique constraint.

xs:whiteSpace Facet to define whitespace behavior.
<xs:whiteSpace
           fixed          = xs:boolean : "false"
           id             = xs:ID
           value          = ( "preserve" | "replace" | "collapse" )
           {any attributes with non-schema namespace}
           >
           Content: (xs:annotation?)
</xs:whiteSpace>
May be included in: xs:restriction (simple type), xs:restriction (simple content)

May be used as facet for:xs:ENTITIES, xs:ENTITY, xs:ID, xs:IDREF, xs:IDREFS, xs:language, xs:Name, xs:NCName, xs:NMTOKEN, xs:NMTOKENS, xs:normalizedString, xs:string, xs:token

Description

This facet defines the treatment to perform on whitespace--i.e., #x20 (space), #x9 (tab), #xA (linefeed), and #xD (carriage return)--during the transformation between the lexical and value spaces.

Its values are preserve (whitespace characters are kept unchanged), replace (all instances of whitespace are replaced with a space), and collapse (leading and trailing whitespace is removed and all the other sequences of contiguous whitespace are replaced by a single space).

Restrictions

This is the only facet (or feature of W3C XML Schema) that interacts with the canonicalization transformation.

It is not possible to "relax" the whitespace behavior during a restriction: if a datatype has a whitespace set as preserve, its derived datatypes can have any whitespace behavior, but if its whitespace is set as replace, its derived datatypes can only have whitespace equal to replace or collapse. If its whitespace is equal to collapse, all its derived datatypes must have the same behavior.

This limitation is somewhat paradoxical since it allows the actual expansion of the lexical space of a datatype during a derivation by restriction.

Example

<xs:simpleType name="CapitalizedNameWS">
  <xs:restriction base="xs:string">
    <xs:whiteSpace value="collapse"/>
    <xs:pattern value="([A-Z]([a-z]*) ?)+"/>
  </xs:restriction>
</xs:simpleType>

Attributes

fixed
When set to fixed, the value of this facet cannot be changed in further restrictions.

id
W3C XML Schema's element ID.

value
Value of the facet .

Previous | Home | Next
14.5. Which One and What For? | Book Index | 16. Datatype Reference Guide
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Datatype Reference Guide (XML Schema)

 XML SchemaSearch this book

Chapter 16. Datatype Reference Guide

Contents:

Datatypes

This chapter provides a quick reference to all of the datatypes W3C XML Schema defines. Each datatype is listed with its constraining facets, as well as information about what it represents and how.

16.1. Datatypes

xs:anyURI URI (Uniform Resource Identifier).
<xs:simpleType name="anyURI" id="anyURI">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction></xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:anyURI

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

This datatype corresponds normatively to the XLink href attribute. Its value space includes the URIs defined by the RFCs 2396 and 2732, but its lexical space doesn't require the character escapes needed to include non-ASCII characters in URIs.

Restrictions

Relative URIs are not "absolutized" by W3C XML Schema. A datatype defined as:

<xs:simpleType name="schemaRecommendations">
  <xs:restriction base="xs:anyURI">
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-0/"/>
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-1/"/>
    <xs:enumeration value="http://www.w3.org/TR/xmlschema-2/"/>
  </xs:restriction>
</xs:simpleType>

should not validate the href attribute in this instance element:

<a xml:base="http://www.w3.org/TR/" href="xmlschema-1/">
  XML Schema Part 2: Datatypes
</a>

The Recommendation states that "it is impractical for processors to check that a value is a context-appropriate URI reference," freeing schema processors from having to validate the correctness of the URI.

Example

<xs:simpleType name="httpURI">
  <xs:restriction base="xs:anyURI">
    <xs:pattern value="http://.*"/>
  </xs:restriction>
</xs:simpleType>
xs:base64Binary Binary content coded as "base64".
<xs:simpleType name="base64Binary" id="base64Binary">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:base64Binary

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The value space of xs:base64Binary is the set of arbitrary binary contents. Its lexical space is the same set after base64 coding. This coding is described in Section 6.8 of RFC 2045.

Restrictions

RFC 2045 has been defined to transfer binary contents over text-based mail systems. It imposes a line break at least every 76 characters to avoid the inclusion of arbitrary line breaks by the mail systems. Sending base64 content without line breaks is nevertheless a common usage for applications such as SOAP and the W3C XML Schema Working Group. After a request from other W3C Working Groups, the W3C XML Schema Working Group decided to remove the obligation to include these line breaks from the constraints on the lexical space. (This decision was made after the publication of the W3C XML Schema Recommendation and should be included in a release of the errata.)

Example

<xs:element name="picture">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:base64Binary">
        <xs:attribute name="type" type="graphicalFormat"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>
xs:boolean Boolean (true or false).
<xs:simpleType name="boolean" id="boolean">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:boolean

Known subtypes:

none

Facets:

xs:pattern, xs:whiteSpace

Description

The value space of xs:boolean is "true" and "false," and its lexical space accepts true, false, and also "1" (for true) and "0" (for false).

Restrictions

This datatype cannot be localized--for instance, to accept "vrai" and "faux" instead of "true" and "false".

Example

<book id="b0836217462" available="true"/>
xs:byte Signed value of 8 bits.
<xs:simpleType name="byte" id="byte">
  <xs:restriction base="xs:short">
    <xs:minInclusive value="-128"/>
    <xs:maxInclusive value="127"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:short

Primary:

xs:decimal

Known subtypes:

none

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:byte is the integers between -128 and 127, i.e., the signed values that can fit in a word of 8 bits. Its lexical space allows an optional sign and leading zeros before the significant digits.

Restrictions

The lexical space does not allow values expressed in other numeration bases (such as hexadecimal, octal, or binary).

Example

Valid values for byte include 27, -34, +105, and 0.

Invalid values include 0A, 1524, and INF.

xs:date Gregorian calendar date.
<xs:simpleType name="date" id="date">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:date

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

This datatype is modeled after the calendar dates defined in Chapter 5.2.1 of ISO 8601. Its value space is the set of Gregorian calendar dates as defined by this standard; i.e., a one-day-long period of time. Its lexical space is the ISO 8601 extended format "[-]CCYY-MM-DD[Z|(+|-)hh:mm]" with an optional timezone. Timezones that are not specified are considered "undetermined."

Restrictions

The basic format of ISO 8601 calendar dates "CCYYMMDD" is not supported.

The other forms of dates available in ISO 8601--ordinal dates defined by the year and the number of the day in the year and dates identified by calendar week and day numbers--are not supported.

As the value space is defined by reference to ISO 8601, there is no support for any calendar system other than Gregorian.

As the lexical space is also defined as reference to ISO 8601, there is no support for any localization such as different orders for date parts or named months.

The order relation between dates with and without timezone is partial: they can be compared only outside of a +/- 14 hours interval.

There is a dissension between ISO 8601 which defines a day as a period of time of 24 hours, and W3C XML Schema, which indicates that a date is a "one-day long, non-periodic instance . . . independent of how many hours this day has." Even though technically right (some days do not last exactly 24 hours because of leap seconds), this definition is not coherent with the definition of xs:duration for which a day is always exactly 24 hours long.

Example

Valid values include: "2001-10-26", "2001-10-26+02:00", "2001-10-26Z", "2001-10-26+00:00", "-2001-10-26", or "-20000-04-01".

The following values would be invalid: "2001-10" (all the parts must be specified), "2001-10-32" (the days part (32) is out of range), "2001-13-26+02:00" (the month part (13) is out of range), or "01-10-26" (the century part is missing).

xs:dateTime Instant of time (Gregorian calendar).
<xs:simpleType name="dateTime" id="dateTime">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:dateTime

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

This datatype describes instants identified by the combination of a date and a time. Its value space is described as a "combination of date and time of day" in Chapter 5.4 of ISO 8601. Its lexical space is the extended format "[-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]". The timezone may be specified as "Z" (UTC) or "(+|-)hh:mm." Timezones that are not specified are considered "undetermined."

Restrictions

The basic format of ISO 8601 calendar datetimes "CCYYMMDDThhmmss" is not supported.

The other forms of datetimes available in ISO 8601--ordinal dates defined by the year and the number of the day in the year and dates identified by calendar week and day numbers--are not supported.

As the value space is defined by reference to ISO 8601, there is no support for any calendar system other than Gregorian.

As the lexical space is also defined as reference to ISO 8601, there is no support for any localization such as different orders for date parts or named months.

The order relation between datetimes with and without timezone is partial: they can be compared only outside of a +/- 14 hours interval.

Example

Valid values for xs:dateTime include: "2001-10-26T21:32:52", "2001-10-26T21:32:52+02:00", "2001-10-26T19:32:52Z", "2001-10-26T19:32:52+00:00", "-2001-10-26T21:32:52", or "2001-10-26T21:32:52.12679".

The following values would be invalid: "2001-10-26" (all the parts must be specified), "2001-10-26T21:32" (all the parts must be specified), "2001-10-26T25:32:52+02:00" (the hours part (25) is out of range), or "01-10-26T21:32" (all the parts must be specified).

xs:decimal Decimal numbers.
<xs:simpleType name="decimal" id="decimal">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:decimal

Known subtypes:

xs:integer

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

xs:decimal is the datatype that represents the set of all the decimal numbers with arbitrary lengths. Its lexical space allows any number of insignificant leading and trailing zeros (after the decimal point).

Restrictions

The decimal separator is always a point (".") and no thousand separator may be added. There is no support for scientific notations.

Example

Valid values include: "123.456", "+1234.456", "-1234.456", "-.456", or "-456".

The following values would be invalid: "1 234.456" (spaces are forbidden), "1234.456E+2" (scientific notation ("E+2") is forbidden), "+ 1234.456" (spaces are forbidden), or "+1,234.456" (delimiters between thousands are forbidden).

xs:double IEEE 64 bit floating point.
<xs:simpleType name="double" id="double">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:double

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The value space of xs:double is "double" (64 bits) floating-point numbers as defined by the IEEE. The lexical space uses a decimal format with optional scientific notation. The match between lexical (powers of 10) and value (powers of 2) spaces is approximate and done on the closest value.

This datatype differentiates positive (0) and negative (-0) zeros, and includes the special values "-INF" (negative infinity), "INF" (positive infinity) and "NaN" (Not a Number).

Note that the lexical spaces of xs:float and xs:double are exactly the same; the only difference is the precision used to convert the values in the value space.

Restrictions

The decimal separator is always a point (".") and no thousands separator may be used.

Examples

Valid values include: "123.456", "+1234.456", "-1.2344e56", "-.45E-6", "INF", "-INF", or "NaN".

The following values would be invalid: "1234.4E 56" (spaces are forbidden), "1E+2.5" (the power of 10 must be an integer), "+INF" (positive infinity doesn't expect a sign), or "NAN" (capitalization matters in special values).

xs:duration Time durations.
<xs:simpleType name="duration" id="duration">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:duration

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

Durations may be expressed using all the parts of a datetime (from year to fractions of second) and are, therefore, defined as a "six-dimensional space." Note that because the relation between some of these date parts (such as the number of days in a month) is not fixed, the order relationship between durations is only partial and the result of a comparison between two durations may be undetermined.

The lexical space of xs:duration is the format defined by ISO 8601 under the form "PnYnMnDTnHnMnS," in which the capital letters are delimiters and can be omitted when the corresponding member is not used.

Although some durations are undetermined, this is fixed as soon as a starting point is fixed for the duration. W3C XML Schema relies on this feature to define the algorithm to use to compare two durations. Four datetimes have been chosen, which produce the greatest deviations when durations are added. A duration will be considered bigger than another when the result of its addition to these four dates is consistently bigger than the result of the addition of the other duration to these same four datetimes. These datetimes are: "1696-09-01T00:00:00Z", "1697-02-01T00:00:00Z," "1903-03-01T00:00:00Z," and "1903-07-01T00:00:00Z."

Restrictions

The lexical space cannot be customized.

Example

Valid values include "PT1004199059S", "PT130S", "PT2M10S", "P1DT2S", "-P1Y", or "P1Y2M3DT5H20M30.123S".

The following values would be invalid: "1Y" (leading "P" is missing), "P1S" ("T" separator is missing), "P-1Y" (all parts must be positive), "P1M2Y" (parts order is significant and Y must precede M), or "P1Y-1M" (all parts must be positive).

xs:ENTITIES Whitespace separated list of unparsed entity references.
<xs:simpleType name="ENTITIES" id="ENTITIES">
  <xs:restriction>
    <xs:simpleType>
      <xs:list>
        <xs:simpleType>
          <xs:restriction base="xs:ENTITY"/>
        </xs:simpleType>
      </xs:list>
    </xs:simpleType>
    <xs:minLength value="1"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:ENTITY

Primary:

none

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:whiteSpace

Description

xs:ENTITIES is derived by a list from xs:ENTITY. It represents lists of unparsed entity references. Each part of this entity reference is a nonqualified name (xs:NCName) and must be declared as an unparsed entity in an internal or external DTD.

Restrictions

Unparsed entities have been defined in XML 1.0 as a way to include non-XML content in a XML document, but most of the applications prefer to define links (such as those defined in (X)HTML to include images or other multimedia objects).

W3C XML Schema does not provide alternative ways to declare unparsed entities. A DTD is needed to do so.

xs:ENTITY Reference to an unparsed entity.
<xs:simpleType name="ENTITY" id="ENTITY">
  <xs:restriction base="xs:NCName"/>
</xs:simpleType>

Derived from:

xs:NCName

Primary:

xs:string

Known subtypes:

xs:ENTITIES

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

xs:ENTITY is an entity reference, i.e., a nonqualified name (xs:NCName) that has been declared as an unparsed entity in an internal or external DTD.

Restrictions

Unparsed entities are defined in XML 1.0 as a way to include non-XML content in XML document, but most of the applications prefer to define links (such as those defined in (X)HTML to include images or other multimedia objects).

W3C XML Schema does not provide alternative ways to declare unparsed entities. A DTD is needed to do so.

xs:float IEEE 32 bit floating point.
<xs:simpleType name="float" id="float">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:float

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The value space of xs:float is "float" (32 bits) floating-point numbers as defined by the IEEE. The lexical space uses a decimal format with optional scientific notation. The match between lexical (powers of 10) and value (powers of 2) spaces is approximate and is done on the closest value.

This datatype differentiates positive (0) and negative (-0) zeros, and includes the special values "-INF" (negative infinity), "INF" (positive infinity), and "NaN" (Not a Number).

Note that the lexical spaces of xs:float and xs:double are exactly the same; the only difference is the precision used to convert the values in the value space.

Restrictions

The decimal separator is always a point (".") and no thousands separator may be added.

Example

Valid values include: "123.456", "+1234.456", "-1.2344e56", "-.45E-6", "INF", "-INF", or "NaN".

The following values would be invalid: "1234.4E 56" (spaces are forbidden), "1E+2.5" (the power of 10 must be an integer), "+INF" (positive infinity doesn't expect a sign), or "NAN" (capitalization matters in special values).

xs:gDay Recurring period of time: monthly day.
<xs:simpleType name="gDay" id="gDay">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:gDay

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The value space of xs:gDay is the periods of one calendar day recurring each calendar month (such as the third of the month); its lexical space follows the ISO 8601 syntax for such periods (i.e., " -- -DD") with an optional timezone.

When needed, days are reduced to fit in the length of the months, so ---31 would occur on the the 28th of February of nonleap years.

Restrictions

The period (one month) and the duration (one day) are fixed, and no calendars other than the Gregorian are supported.

Example

Valid values include "---01", "---01Z", "---01+02:00", "---01-04:00", "---15", or "---31".

The following values would be invalid: "--30-" (the format must be "---DD"), "---35" (the day is out of range), "---5" (all the digits must be supplied), or "15" (missing leading "---").

xs:gMonth Recurring period of time: yearly month.
<xs:simpleType name="gMonth" id="gMonth">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:gMonth

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The value space of xs:gMonth is the period of one calendar month recurring each calendar year (such as the month of April); its lexical space should follow the ISO 8601 syntax for such periods (i.e., " -- MM") with an optional timezone. However, there is a typo in the W3C XML Schema Recommendation where the format is defined as " -- MM -- -- . Even though an erratum should be published to bring the W3C XML Schema inline with ISO 8601, most of the current schema processors will expect the (bogus) format " -- MM -- -- . In the example, we follow the correct ISO 8601 format.

Restrictions

The period (one year) and the duration (one month) are fixed, and no calendars other than the Gregorian are supported.

Because of the typo in the W3C XML Schema Specification, users must choose between a bogus format, which works on the current version of the tools, or a correct format, which conforms to ISO 8601.

Example

Valid values include "--05", "--11Z", "--11+02:00", "--11-04:00", or "--02".

The following values would be invalid: "-01-" (the format must be "--MM"), "--13" (the month is out of range), "--1" (both digits must be provided), or "01" (leading " -- " are missing).

xs:gMonthDay Recurring period of time: yearly day.
<xs:simpleType name="gMonthDay" id="gMonthDay">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:gMonthDay

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The value space of xs:gMonthDay is the period of one calendar day recurring each calendar year (such as the third of April); its lexical space follows the ISO 8601 syntax for such periods (i.e., " -- MM-DD") with an optional timezone.

When needed, days are reduced to fit in the length of the months, so --02-29 would occur on the the 28th of February of nonleap years.

Restrictions

The period (one year) and the duration (one day) are fixed, and no calendars other than the Gregorian are supported.

Example

Valid values include "--05-01", "--11-01Z", "--11-01+02:00", "--11-01-04:00", "--11-15", or "--02-29".

The following values would be invalid: "-01-30-" (the format must be "--MM-DD"), "--01-35" (the day part is out of range), "--1-5" (the leading zeros are missing), or "01-15" (the leading "--" are missing).

xs:gYear Period of one year.
<xs:simpleType name="gYear" id="gYear">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:gYear

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The value space of xs:gYear is the period of one calendar year (such as the year 2002); its lexical space follows the ISO 8601 syntax for such periods (i.e., "YYYY") with an optional timezone.

Restrictions

The duration (one year) is fixed, and no calendars other than the Gregorian are supported.

Example

Valid values include "2001", "2001+02:00", "2001Z", "2001+00:00", "-2001", or "-20000".

The following values would be invalid: "01" (the century part is missing) or "2001-12" (month parts are forbidden).

xs:gYearMonth Period of one month.
<xs:simpleType name="gYearMonth" id="gYearMonth">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:gYearMonth

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The value space of xs:gYearMonth is the period of one calendar month (such as the month of February 2002); its lexical space follows the ISO 8601 syntax for such periods (i.e., "YYYY-MM") with an optional timezone.

Restrictions

The duration (one month) is fixed, and no calendars other than the Gregorian are supported.

Example

Valid values include "2001-10", "2001-10+02:00", "2001-10Z", "2001-10+00:00", "-2001-10", or "-20000-04".

The following values would be invalid: "2001" (the month part is missing), "2001-13" (the month part is out of range), "2001-13-26+02:00" (the month part is out of range), or "01-10" (the century part is missing).

xs:hexBinary Binary contents coded in hexadecimal.
<xs:simpleType name="hexBinary" id="hexBinary">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:hexBinary

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The value space of xs:hexBinary is the set of all binary contents; its lexical space is a simple coding of each octet as its hexadecimal value.

Restrictions

This datatype should not be confused with another encoding called BinHex that is not supported by W3C XML Schema. Other popular binary text encodings (such as uuXXcode, Quote Printable, BinHex, aencode, or base85, to name few) are not supported by the W3C XML Schema.

The expansion factor is high since each binary octet is coded as two characters (i.e., four octets if the document is encoded with UTF-16).

Example

A UTF-8 XML header such as:

"<?xml version="1.0" encoding="UTF-8"?>"

encoded would be:

"3f3c6d78206c657673726f693d6e3122302e20226e656f636964676e223d54552d4622383e3f"
xs:ID Definition of unique identifiers.
<xs:simpleType name="ID" id="ID">
  <xs:restriction base="xs:NCName"/>
</xs:simpleType>

Derived from:

xs:NCName

Primary:

xs:string

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The purpose of the xs:ID datatype is to define unique identifiers that are global to a document and emulate the ID attribute type available in the XML DTDs.

Unlike their DTD counterparts, W3C XML Schema ID datatypes can be used to define not only attributes, but also simple element content.

For both attributes and simple element content, the lexical domain of these datatypes is the lexical domain of XML nonqualified names (xs:NCName).

Identifiers defined using this datatype are global to a document and provide a way to uniquely identify their containing element, whatever its type and name is.

The constraint added by this datatype beyond the xs:NCName datatype from which it is derived is that the values of all the attributes and elements that have an ID datatype in a document must be unique.

Restrictions

Applications that need to maintain a level of compatibility with DTDs should not use this datatype for elements but should reserve it for attributes.

The lexical domain (xs:NCName) of this datatype doesn't allow the definition of numerical identifiers or identifiers containing whitespaces.

W3C XML Schema provides another mechanism to define unique and key constraints using the xs:unique or xs:key elements when more flexibility is needed.

Example

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="isbn" type="xs:int"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="author-ref">
        <xs:complexType>
          <xs:attribute name="ref" type="xs:IDREF" use="required"/>
        </xs:complexType>
      </xs:element>
      <xs:element name="character-refs" type="xs:IDREFS"/>
    </xs:sequence>
    <xs:attribute name="identifier" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:element>
xs:IDREF Definition of references to unique identifiers.
<xs:simpleType name="IDREF" id="IDREF">
  <xs:restriction base="xs:NCName"/>
</xs:simpleType>

Derived from:

xs:NCName

Primary:

xs:string

Known subtypes:

xs:IDREFS

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The xs:IDREF datatype defines references to the identifiers defined by the ID datatype and, therefore, emulates the IDREF attribute type of the XML DTDs, even though it can be used for simple content elements as well as for attributes.

The lexical space of xs:IDREF is, like the lexical space of xs:ID, nonqualified XML names (NCName).

The constraint added by this datatype beyond the xs:NCName datatype from which it is derived is the values of all the attributes and elements that have a xs:IDREF datatype must match an ID defined within the same document.

Restrictions

Applications that need to maintain a level of compatibility with DTDs should not use this datatype for elements but should reserve it for attributes.

The lexical domain (NCName) of this datatype doesn't allow definition of numerical key references or references containing whitespaces.

W3C XML Schema provides another mechanism to define key reference constraints using the xs:keyref elements when more flexibility is needed.

Example

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="isbn" type="xs:int"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="author-ref">
        <xs:complexType>
          <xs:attribute name="ref" type="xs:IDREF" use="required"/>
        </xs:complexType>
      </xs:element>
      <xs:element name="character-refs" type="xs:IDREFS"/>
    </xs:sequence>
    <xs:attribute name="identifier" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:element>
xs:IDREFS Definition of lists of references to unique identifiers.
<xs:simpleType name="IDREFS" id="IDREFS">
  <xs:restriction>
    <xs:simpleType>
      <xs:list>
        <xs:simpleType>
          <xs:restriction base="xs:IDREF"/>
        </xs:simpleType>
      </xs:list>
    </xs:simpleType>
    <xs:minLength value="1"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:IDREF

Primary:

none

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:whiteSpace

Description

xs:IDREFS is derived as a list from xs:IDREF and, thus, represents whitespace-separated lists of references to identifiers defined using the ID datatype.

The lexical space of xs:IDREFS is the lexical space of a list of xs:NCName values with a minimum length of one element (xs:IDREFS cannot be empty lists).

xs:IDREFS emulates the xs:IDREFS attribute type of the XML DTDs, even though it can be used to define simple content elements as well as attributes.

Restrictions

Applications that need to maintain a level of compatibility with DTDs should not use this datatype for elements but should reserve it for attributes.

The lexical domain (lists of xs:NCName) of this datatype doesn't allow definition of lists of numerical key references or references containing whitespaces.

Although the W3C XML Schema provides another mechanism to define unique, key, and key reference constraints (using the xs:unique, xs:key, and xs:keyref elements) when more flexibility is needed, this mechanism doesn't provide any alternative for lists of key references (similar to xs:IDREFS).

Example

<xs:element name="book">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="isbn" type="xs:int"/>
      <xs:element name="title" type="xs:string"/>
      <xs:element name="author-ref">
        <xs:complexType>
          <xs:attribute name="ref" type="xs:IDREF" use="required"/>
        </xs:complexType>
      </xs:element>
      <xs:element name="character-refs" type="xs:IDREFS"/>
    </xs:sequence>
    <xs:attribute name="identifier" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:element>
xs:int 32 bit signed integers.
<xs:simpleType name="int" id="int">
  <xs:restriction base="xs:long">
    <xs:minInclusive value="-2147483648"/>
    <xs:maxInclusive value="2147483647"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:long

Primary:

xs:decimal

Known subtypes:

xs:short

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:int is the set of common single size integers (32 bits), i.e., the integers between -2147483648 and 2147483647, its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

-0 and +0 are considered equal, which is different from the behavior of xs:float and xs:double.

Example

Valid values include "-2147483648", "0", "-0000000000000000000005", or "2147483647".

Invalid values include "-2147483649" and "1".

xs:integer Signed integers of arbitrary length.
<xs:simpleType name="integer" id="integer">
  <xs:restriction base="xs:decimal">
    <xs:fractionDigits value="0" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:decimal

Primary:

xs:decimal

Known subtypes:

xs:nonPositiveInteger, xs:long, xs:nonNegativeInteger

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:integer includes the set of all the signed integers, with no restriction on range. Its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

-0 and +0 are considered equal, which is different from the behavior of xs:float and xs:double.

Example

Valid values for xs:integer include "-123456789012345678901234567890", "2147483647", "0", or "-0000000000000000000005".

Invalid values include "1.", "2.6", and "A".

xs:language RFC 1766 language codes.
<xs:simpleType name="language" id="language">
  <xs:restriction base="xs:token">
     <xs:pattern
      value="([a-zA-Z]{2}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*"
      />
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:token

Primary:

xs:string

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical and value spaces of xs:language are the set of language codes defined by the RFC 1766.

Restrictions

Although the schema for schema defines a minimal test to perform expressed as patterns (see the Definition), the lexical space is the set of existing language codes.

Example

Some valid values for this datatype are: "en", "en-US", "fr", or "fr-FR".

xs:long 64 bit signed integers.
<xs:simpleType name="long" id="long">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="-9223372036854775808"/>
    <xs:maxInclusive value="9223372036854775807"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:integer

Primary:

xs:decimal

Known subtypes:

xs:int

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:long is the set of common double-size integers (64 bits), i.e., the integers between -9223372036854775808 and 9223372036854775807; its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values for xs:long include "-9223372036854775808", "0", "-0000000000000000000005", or "9223372036854775807".

Invalid values include "9223372036854775808" and "1.".

xs:Name XML 1.O names.
<xs:simpleType name="Name" id="Name">
  <xs:restriction base="xs:token">
    <xs:pattern value="\i\c*"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:token

Primary:

xs:string

Known subtypes:

xs:NCName

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical and value spaces of xs:Name are the tokens (NMTOKEN) that conform to the definition of a name in XML 1.0.

Restrictions

Following XML 1.0, those names may contain colons (":"), but no special meaning is attached to these characters. Another datatype (xs:QName) should be used for qualified names when they use namespaces prefixes.

Example

Valid values include "Snoopy", "CMS", or "_1950-10-04_10:00".

Invalid values include "0836217462" (a xs:Name cannot start with a number) or "bold,brash" (commas are forbidden).

xs:NCName Unqualified names.
<xs:simpleType name="NCName" id="NCName">
  <xs:restriction base="xs:Name">
    <xs:pattern value="[\i-[:]][\c-[:]]*"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:Name

Primary:

xs:string

Known subtypes:

xs:ID, xs:IDREF, xs:ENTITY

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical and value spaces of xs:NCName are the names (Name) that conform to the definition of a NCName in the Recommendation "Namespaces in XML 1.0"--i.e., all the XML 1.0 names that do not contain colons (":").

Restrictions

This datatype allows characters such as "-" and may need additional constraints to match the notion of name in your favorite programming language or database system.

Example

Valid values include "Snoopy", "CMS", "_1950-10-04_10-00", or "bold_brash".

Invalid values include "_1950-10-04:10-00" or "bold:brash" (colons are forbidden).

xs:negativeInteger Strictly negative integers of arbitrary length.
<xs:simpleType name="negativeInteger" id="negativeInteger">
  <xs:restriction base="xs:nonPositiveInteger">
    <xs:maxInclusive value="-1"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:nonPositiveInteger

Primary:

xs:decimal

Known subtypes:

none

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:negativeInteger includes the set of all the strictly negative integers (excluding zero), with no restriction of range. Its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values for xs:negativeInteger include "-123456789012345678901234567890", "-1", or "-0000000000000000000005".

Invalid values include "0" or "-1.".

xs:NMTOKEN XML 1.0 name token (NMTOKEN).
<xs:simpleType name="NMTOKEN" id="NMTOKEN">
  <xs:restriction base="xs:token">
    <xs:pattern value="\c+"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:token

Primary:

xs:string

Known subtypes:

xs:NMTOKENS

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical and value spaces of xs:NMTOKEN are the set of XML 1.0 "name tokens," i.e., tokens composed of characters, digits, ".", ":", "-", and the characters defined by Unicode, such as "combining" or "extender".

Restrictions

This type is usually called a "token."

Example

Valid values include "Snoopy", "CMS", "1950-10-04", or "0836217462".

Invalid values include "brought classical music to the Peanuts strip" (spaces are forbidden) or "bold,brash" (commas are forbidden).

xs:NMTOKENS List of XML 1.0 name token (NMTOKEN).
<xs:simpleType name="NMTOKENS" id="NMTOKENS">
  <xs:restriction>
    <xs:simpleType>
      <xs:list>
        <xs:simpleType>
          <xs:restriction base="xs:NMTOKEN"/>
        </xs:simpleType>
      </xs:list>
    </xs:simpleType>
    <xs:minLength value="1"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:NMTOKEN

Primary:

none

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:whiteSpace

Description

xs:NMTOKENS is derived by list from xs:NMTOKEN and represents whitespace-separated lists of XML 1.0 name tokens.

Restrictions

None.

Example

Valid values include "Snoopy", "CMS", "1950-10-04", "0836217462 0836217463", or "brought classical music to the Peanuts strip" (note that, in this case, the sentence is considered as a list of words).

Invalid values include "brought classical music to the "Peanuts" strip" (quotes are forbidden) or "bold,brash" (commas are forbidden).

xs:nonNegativeInteger Integers of arbitrary length positive or equal to zero.
<xs:simpleType name="nonNegativeInteger" id="nonNegativeInteger">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="0"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:integer

Primary:

xs:decimal

Known subtypes:

xs:unsignedLong, xs:positiveInteger

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:nonNegativeInteger includes the set of all the integers greater than or equal to zero, with no restriction of range. Its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "+123456789012345678901234567890", "0", "0000000000000000000005", or "2147483647".

Invalid values include "1." or "-1.".

xs:nonPositiveInteger Integers of arbitrary length negative or equal to zero.
<xs:simpleType name="nonPositiveInteger" id="nonPositiveInteger">
  <xs:restriction base="xs:integer">
    <xs:maxInclusive value="0"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:integer

Primary:

xs:decimal

Known subtypes:

xs:negativeInteger

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:nonPositiveInteger includes the set of all the integers less than or equal to zero, with no restriction of range. Its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "-123456789012345678901234567890", "0", "-0000000000000000000005", or "-2147483647".

Invalid values include "-1." or "1.".

xs:normalizedString Whitespace-replaced strings.
<xs:simpleType name="normalizedString" id="normalizedString">
  <xs:restriction base="xs:string">
    <xs:whiteSpace value="replace"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:string

Primary:

xs:string

Known subtypes:

xs:token

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical space of xs:normalizedString is unconstrained (any valid XML character may be used), and its value space is the set of strings after whitespace replacement (i.e., after any occurrence of #x9 (tab), #xA (linefeed), and #xD (carriage return) have been replaced by an occurrence of #x20 (space) without any whitespace collapsing).

Restrictions

This is the only datatype that performs whitespace replacement without collapsing. When whitespaces are not significant, xs:token is preferred.

This datatype corresponds to neither the XPath function normalize-space( ) (which performs whitespace trimming and collapsing) nor to the DOM "normalize" method (which is a merge of adjacent text objects).

Example

The value of the element:

<title lang="en">
  Being a Dog Is
  a Full-Time Job
</title>"

is the string: " Being a Dog Is a Full-Time Job ", where all the whitespaces have been replaced by spaces if the title element is a type xs:normalizedString.

xs:NOTATION Emulation of the XML 1.0 feature.
<xs:simpleType name="NOTATION" id="NOTATION">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:NOTATION

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The value and lexical spaces of xs:NOTATION are references to notations declared though the xs:notation element. The use of this element and datatype is, therefore, a namespace-aware emulation of the NOTATION feature of XML 1.0.

Restrictions

Notations are very seldom used in real-world applications.

One cannot use xs:notation directly, but must derive it as shown in the Example.

Example

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:notation name="jpeg" public="image/jpeg"
    system="file:///usr/bin/xv"/>
  <xs:notation name="gif" public="image/gif"
    system="file:///usr/bin/xv"/>
  <xs:notation name="png" public="image/png"
    system="file:///usr/bin/xv"/>
  <xs:notation name="svg" public="image/svg"
    system="file:///usr/bin/xsmiles"/>
  <xs:notation name="pdf" public="application/pdf"
    system="file:///usr/bin/acroread"/>
  <xs:simpleType name="graphicalFormat">
    <xs:restriction base="xs:NOTATION">
      <xs:enumeration value="jpeg"/>
      <xs:enumeration value="gif"/>
      <xs:enumeration value="png"/>
      <xs:enumeration value="svg"/>
      <xs:enumeration value="pdf"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="picture">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:base64Binary">
          <xs:attribute name="type" type="graphicalFormat"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
</xs:schema>
xs:positiveInteger Strictly positive integers of arbitrary length.
<xs:simpleType name="positiveInteger" id="positiveInteger">
  <xs:restriction base="xs:nonNegativeInteger">
    <xs:minInclusive value="1"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:nonNegativeInteger

Primary:

xs:decimal

Known subtypes:

none

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:positiveInteger includes the set of the strictly positive integers (excluding zero), with no restriction of range. Its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "123456789012345678901234567890", "1", or "0000000000000000000005".

Invalid values include "0" or "1.".

xs:QName Namespaces in XML qualified names.
<xs:simpleType name="QName" id="QName">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:QName

Known subtypes:

none

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical space of xs:QName is the qualified names per Namespace in XML, i.e., a local name (which is a xs:NCName) with an optional prefix (itself a xs:NCName), separated by a colon (":"), where the prefix is declared a namespace prefix in the scope of the element carrying the value. Its value space comprises the pairs (namespace URI, local name) in which the namespace URI is the URI associated to the prefix in the namespace declaration.

This dissociation between lexical and value spaces makes a lot of difference when using facets such as xs:pattern (which acts on the lexical space only and thus constrains the namespace prefix) and xs:enumeration (which acts on the value space and thus constrains the namespace URI).

Restrictions

It is impossible to apply a pattern on the namespace URI.

The usage of QNames in elements and attributes is controversial since it creates a dependency between the content of the document and its markup. However, the official position of the W3C doesn't discourage this practice.

Example

W3C XML Schema itself has already given us some examples of QNames. When we wrote "<xs:attribute name="lang" type="xs:language"/>", the type attribute was a xs:QName and its value was the tuple {"http://www.w3.org/2001/XMLSchema", "language"} because the URI "http://www.w3.org/2001/XMLSchema" had been assigned to the prefix "xs:". If there had been no namespace declaration for this prefix, the type attribute would have been considered invalid.

xs:short 32 bit signed integers.
<xs:simpleType name="short" id="short">
  <xs:restriction base="xs:int">
    <xs:minInclusive value="-32768"/>
    <xs:maxInclusive value="32767"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:int

Primary:

xs:decimal

Known subtypes:

xs:byte

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:short is the set of common short integers (16 bits), i.e., the integers between -32768 and 32767; its lexical space allows any number of insignificant leading zeros.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "-32768", "0", "-0000000000000000000005", or "32767".

Invalid values include "32768" and "1.".

xs:string Any string.
<xs:simpleType name="string" id="string">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="preserve"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:string

Known subtypes:

xs:normalizedString

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical and value spaces of xs:string are the set of all possible strings composed of any character allowed in a XML 1.0 document without any treatment done on whitespaces.

Restrictions

This is the only datatype that leaves all the whitespaces. When whitespaces are not significant, xs:token is preferred.

Example

The value of the following element:

<title lang="en">
  Being a Dog Is
  a Full-Time Job
</title>

is the full string "Being a Dog Is a Full-Time Job", with all its tabulations and CR/LF if the title element is a xs:string type .

xs:time Point in time recurring each day.
<xs:simpleType name="time" id="time">
  <xs:restriction base="xs:anySimpleType">
    <xs:whiteSpace value="collapse" fixed="true"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:anySimpleType

Primary:

xs:time

Known subtypes:

none

Facets:

xs:enumeration, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:whiteSpace

Description

The lexical space of xs:time is identical to the time part of xs:dateTime ("hh:mm:ss[Z|(+|-)hh:mm]"); and its value space is the set of points in time recurring daily.

Restrictions

The period (one day) is fixed and no calendars other than the Gregorian are supported.

Example

Valid values include "21:32:52", "21:32:52+02:00", "19:32:52Z", "19:32:52+00:00", or "21:32:52.12679".

Invalid values include "21:32" (all the parts must be specified), "25:25:10" (the hour part is out of range), "-10:00:00" (the hour part is out of range), or "1:20:10" (all the digits must be supplied).

xs:token Whitespace-replaced and collapsed strings.
<xs:simpleType name="token" id="token">
  <xs:restriction base="xs:normalizedString">
    <xs:whiteSpace value="collapse"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:normalizedString

Primary:

xs:string

Known subtypes:

xs:language, xs:NMTOKEN, xs:Name

Facets:

xs:enumeration, xs:length, xs:maxLength, xs:minLength, xs:pattern, xs:whiteSpace

Description

The lexical and value spaces of xs:token are the sets of all the strings after whitespace replacement--i.e., after any occurrence of #x9 (tab), #xA (linefeed), and #xD (carriage return) is replaced by an occurrence of #x20 (space) and collapsing (i.e., the contiguous occurrences of spaces are replaced by a single space, and leading and trailing spaces are removed).

More simply said, xs:token is the most appropriate datatype to use for strings that do not care about whitespaces.

Restrictions

The name xs:token is misleading since whitespaces are allowed within xs:token. xs:NMTOKEN is the type corresponding to what is usually called "tokens."

Example

The element:

<title lang="en">
  Being a Dog Is
  a Full-Time Job
</title>

is a valid xs:token and its value is the string "Being a Dog Is a Full-Time Job", where all the whitespaces have been replaced by spaces, leading and trailing spaces have been removed and contiguous sequences of spaces have been replaced by single spaces.

xs:unsignedByte Unsigned value of 8 bits.
<xs:simpleType name="unsignedByte" id="unsignedBtype">
  <xs:restriction base="xs:unsignedShort">
    <xs:maxInclusive value="255"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:unsignedShort

Primary:

xs:decimal

Known subtypes:

none

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:unsignedByte is the integers between 0 and 255, i.e., the unsigned values that can fit in a word of 8 bits. Its lexical space allows an optional "+" sign and leading zeros before the significant digits.

Restrictions

The lexical space does not allow values expressed in other numeration bases (such as hexadecimal, octal, or binary).

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "255", "0", "+0000000000000000000005", or "1".

Invalid values include "-1" and "1.".

xs:unsignedInt Unsigned integer of 32 bits.
<xs:simpleType name="unsignedInt" id="unsignedInt">
  <xs:restriction base="xs:unsignedLong">
    <xs:maxInclusive value="4294967295"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:unsignedLong

Primary:

xs:decimal

Known subtypes:

xs:unsignedShort

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:unsignedInt is the integers between 0 and 4294967295, i.e., the unsigned values that can fit in a word of 32 bits. Its lexical space allows an optional "+" sign and leading zeros before the significant digits.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "4294967295", "0", "+0000000000000000000005", or "1".

Invalid values include "-1" and "1.".

xs:unsignedLong Unsigned integer of 64 bits.
<xs:simpleType name="unsignedLong" id="unsignedLong">
  <xs:restriction base="xs:nonNegativeInteger">
    <xs:maxInclusive value="18446744073709551615"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:nonNegativeInteger

Primary:

xs:decimal

Known subtypes:

xs:unsignedInt

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:unsignedLong is the integers between 0 and 18446744073709551615, i.e., the unsigned values that can fit in a word of 64 bits. Its lexical space allows an optional "+" sign and leading zeros before the significant digits.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "18446744073709551615", "0", "+0000000000000000000005", or "1".

Invalid values include "-1" and "1.".

xs:unsignedShort Unsigned integer of 16 bits.
<xs:simpleType name="unsignedShort" id="unsignedShort">
  <xs:restriction base="xs:unsignedInt">
    <xs:maxInclusive value="65535"/>
  </xs:restriction>
</xs:simpleType>

Derived from:

xs:unsignedInt

Primary:

xs:decimal

Known subtypes:

xs:unsignedByte

Facets:

xs:enumeration, xs:fractionDigits, xs:maxExclusive, xs:maxInclusive, xs:minExclusive, xs:minInclusive, xs:pattern, xs:totalDigits, xs:whiteSpace

Description

The value space of xs:unsignedShort is the integers between 0 and 65535, i.e., the unsigned values that can fit in a word of 16 bits. Its lexical space allows an optional "+" sign and leading zeros before the significant digits.

Restrictions

The decimal point (even when followed only by insignificant zeros) is forbidden.

Example

Valid values include "65535", "0", "+0000000000000000000005", or "1".

Invalid values include "-1" and "1." .

Previous | Home | Next
15. Elements Reference Guide | Book Index | A. XML Schema Languages
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Schema Languages (XML Schema)

 XML SchemaSearch this book

Appendix A. XML Schema Languages

Contents:

What Is a XML Schema Language?
Classification of XML Schema Languages
A Short History of XML Schema Languages
Sample Application
XML DTDs
W3C XML Schema
RELAX NG
Schematron
Examplotron
Decisions

A.1. What Is a XML Schema Language?

Roughly speaking, XML schema languages describe XML documents. Different approaches to that task, however, provide a wide range of functionality.

A.1.1. XML Schema Languages Are Not Schemas

The first thing we can say about XML schema languages is they are not schemas. At least they do not match the definition of a schema as given by Webster's dictionary, which states: "an outline or image universally applicable to a general conception, under which it is likely to be presented to the mind; as, five dots in a line are a schema of the number five; a preceding and succeeding event are a schema of cause and effect."

This definition does not apply to the languages known as "XML schema languages"; most of these are more complex than the documents they describe and are too difficult to "be presented to the mind." They focus on defining validation rules more than on representing or modeling a class of documents. When they do model a class of documents, they often want to add information to the documents they model.

Looking past the formal label of schemas, how can we classify so-called "XML schema languages"? Looking at all XML schema languages (DTDs, W3C XML Schema, RELAX NG, and also languages such as Schematron), the one thing they have in common is being transformations, which take a "schema" and an instance document as an input and transform them into a validation report, and optionally, into a PSVI (Post Schema Validation Infoset), a set of information added to the XML infoset of the source document. This PSVI (when it exists) includes information such as default values, datatypes, etc.

Changing the category of XML schema languages not only alters our perception of what they are, but also opens the game, since general-purpose transformations or programming languages, such as XSLT, Prolog, Java, C# and friends, can be considered XML schema languages.

A.1.2. Firewalls Against Diversity

The "X" in XML stands for Extensibility. XML is, in fact, so extensible and diverse that few if any applications are able to support this diversity. XML schema languages were created as firewalls against diversity and protect applications from meeting unexpected information and formats.

This analogy provides us our first classification of schema languages. These, like firewalls, can be open and allow any construction that isn't forbidden (as in the case of Schematron), or they can be closed and forbid anything that has not been allowed. (This is the case for most of the other schema languages, including W3C XML Schema.)

Closed firewalls are certainly much safer than open ones, but also much less extensible, since any new traffic must be allowed by the administrator before it can pass through. This is one of the reasons for the failure of protocols such as CORBA, as well as one the reasons web services has chosen to use HTTP. Closed XML schema languages can represent the same kind of threat against the diversity of XML vocabularies as closed firewalls do against the diversity of IP protocols.

Therefore, there is a trade-off: being able to check whether a document we have received or are about to send is valid and won't blow up our applications is not only useful but even necessary. However, our schemas should stay open whenever possible to remain extensible. (Chapter 13, "Creating Extensible Schemas" presents the techniques available with the W3C XML Schema to limit the danger of "closedness.")

A.1.3. Intrusive Modeling Tools

Schematron is the only XML schema language that doesn't base its validation on a model of the class of the documents that are considered valid. All the other XML schema languages describe the structure of the valid documents (which is where the name "schema" comes from). The lack of expressiveness of their description languages can be another threat to the diversity of XML vocabularies since the structures, which cannot be described with one of the major XML schema languages, might become deprecated automatically. Unfortunately, this will likely be the case with the W3C XML Schema, in which expressiveness can be considered medium. Some existing vocabularies, such as RSS 1.0 and WebDAV, cannot be described with W3C XML Schema.

One may argue whether such and such a structure, which cannot be described by such and such a language, is good practice or not. However, I think that XML schema languages should be as neutral as possible and not add constraints to those defined by the XML 1.0 and Namespaces in XML Recommendations. XML is still a young technology, and many innovative ways of using it are still to be discovered. Some of them may be jeopardized by the lack of expressiveness of W3C XML Schema.

On the other hand, modeling activity is valuable by itself and its outcome, expressed as a XML schema, can be used to automate or enhance the processing of the XML document or the generation of applications and generic tools, such as Version 2 of XPath and XSLT. The initial version of XQuery will rely on the information provided by XML schemas for advanced features, but also for things as basic as knowing which sort order should be used for each node.

In modeling, we find the same basic differences that can be found between an API (such as the DOM, in which each node is manipulated individually and is highly differentiated) and a model such as XPath, which enables splitting XML documents into sets of nodes ("nodesets"). While RELAX NG (co-authored by James Clark, who was also the editor of XPath 1.0) is based on the definition of patterns (classes of undifferentiated nodesets or containers encapsulating elements, attributes, and text nodes) W3C XML Schema has defined differentiated and different constructions to define elements, attributes, their content (called simple or complex types), and groups of elements or attributes.

Although this differentiation will seem natural when we get used to the W3C XML Schema, it is often useful to remember that those different constructions can be seen as a difference of perspective. Also, elements, attributes, types, and groups are "patterns," as defined by RELAX NG with a different granularity, and can be embedded within each other.

There is also a second consequence to any modeling activity, which is to change our perception of what is modeled. As the outside world is seen differently after Aristotle, Newton, and Einstein, your perception of a given XML document will vary depending upon which XML schema language you use.

A.1.4. Early Binding Tools

Another often mentioned quality of XML is its ability to serve as a base for late binding and highly decoupled systems in which the sender and receiver applications are independent of each other. This late binding ability has two major advantages. The first, which is very practical, is a complete independence between the systems and applications that create the XML document on one side and those that use it on the other side. The second, which is more abstract, allows the receiver to apply its own treatment and project its own semantic to "understand" the document, leaving the possibility of adding some value to the sent message.

XML Schema languages may be a danger for late binding approaches, especially those like the W3C XML Schema, which produce a PSVI, since the association of information from the schema is a form of early binding that binds a document to a specific XML schema language (necessary to interpret the document). Here again, this danger seems to be the price of automating the writing of applications to process the documents.

Previous | Home | Next
16. Datatype Reference Guide | Book Index | A.2. Classification of XML Schema Languages
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Classification of XML Schema Languages (XML Schema)

 XML SchemaSearch this book

A.2. Classification of XML Schema Languages

In the previous section, we saw several classifications for XML schema languages (open or closed, with or without PSVI, etc.). Here, we will follow the classification done by the DSDL (Document Schema Definition Language) ISO project and define a more layered and structured classification.

A.2.1. Rule-Based XML Schema Languages

The XML schema languages that have the finest granularity for controlling how an instance document may look are the rule-based schema languages such as Schematron. Since any constraint may be expressed as a set of rules of some kind, rule-based schema languages may be considered as lower-level than the other XML schema languages that can be built on top of them. Though I am not aware of any implementation that does so, a W3C XML Schema processor could generate a set of Schematron rules to express its constraints. They can be used alone or to finish any work that cannot be finished with other XML schema languages that do not have their level of granularity. Embedding their rules in the W3C XML Schema seems very promising (as discussed in Chapter 14, "Documenting Schemas").

A.2.2. Grammar-Based XML Schema Languages

Grammar-based XML Schema languages act at another level and attempt to describe the structure of the instance documents. In a sense, they may be compared to a BNF (Backus-Naur Notation or Form, used to describe the syntax of programming languages) for XML. They describe the possible patterns of XML nodes as BNF describes possible patterns of characters. The most popular grammar-based XML schema languages are DTDs and RELAX NG. Focusing on the structure, those languages do not attach a lot of importance to the notions of datatype and PSVI. Those languages may also be seen as a concise way to define the rules that constrain the structure of a document, and may be used in conjunction with rule-based languages needed to express nonstructural rules.

A.2.3. Object-Oriented XML Schema Languages

It's clear that W3C XML Schema is something more than just a grammar-based XML schema language. It is also an attempt to describe instance documents in a way that is as close as possible to an object oriented design. One consequence is the importance of type. If elements and attributes are assimilated to objects, the types are the closest thing to object-oriented classes and are crucial to express the similarity between objects. The other object-oriented features of the W3C XML Schema are the derivation and substitution mechanisms defined to match the inheritance between object-oriented classes. And finally, since it is anticipated that object-oriented applications will benefit from this mapping between hierarchical XML and the object-oriented paradigm, the PSVI is key to communication between the schema processor and the applications.

Previous | Home | Next
A. XML Schema Languages | Book Index | A.3. A Short History of XML Schema Languages
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Short History of XML Schema Languages (XML Schema)

 XML SchemaSearch this book

A.3. A Short History of XML Schema Languages

The list of schema languages is long and needs to include languages developed for SGML (the language used before XML was born) to be complete. The list that I propose is far from exhaustive, and includes only the major proposals that have influenced the schema languages I see as the most promising.

A.3.1. The DTD Family

Mandatory for any SGML application, a simplified version of the SGML DTDs was introduced in the XML 1.0 Recommendation. Even though a DTD is not mandatory for an application to read and understand a XML document, many developers highly recommend writing DTDs for any XML application.

A.3.2. The W3C XML Schema Family

The W3C XML Schema Working Group received many proposals that were contributed as notes:

  • XML-Data, submitted as a note (http://www.w3.org/TR/1998/NOTE-XML-data) in January 1998 by Microsoft, DataChannel, Arbortext, Inso Corporation, and the University of Edinburgh, included most of the basic concepts later developed by W3C XML Schema. Although the details were not fully developed, the note covered a lot of ground that was kept out of W3C XML Schema, such as internal and external entity definitions and the mapping to RDF (Resource Description Framework) and OOP structures.

  • XML-Data-Reduced (XDR), submitted in July 1998 (http://www.ltg.ed.ac.uk/~ht/XMLData-Reduced.htm) by Microsoft and the University of Edinburgh was presented to "refine and subset those ideas down to a more manageable size in order to allow faster progress toward adopting a new schema language for XML" (mappings were left out). XDR was implemented by Microsoft and used by the BizTalk framework.

  • DCD (Document Content Description for XML), also submitted in July 1998 (http://www.w3.org/TR/NOTE-dcd) by Textuality, Microsoft, and IBM, was a "subset of the XML-Data Submission (XML-Data) and expressed it in a way which is consistent with the ongoing W3C RDF (Resource Description Framework) effort." Mapping considerations were left out, but the language took care to be consistent with RDF through features such as "Interchangeability of Elements and Attributes."

  • SOX (Schema for Object-Oriented XML) was developed by Veo Systems/Commerce One and submitted as a note in September 1998 (a second version was submitted in July 1999 (see http://www.w3.org/TR/NOTE-SOX) as "informed by the XML 1.0 specification as well as the XML-Data submission (XML-Data), the Document Content Description submission (DCD), and the EXPRESS language reference manual (ISO-10303-11)." SOX was very influenced by OOP language design and included concepts of interface and implementation, but it was also influenced by DTDs and included support for "parameters." SOX is widely used by Commerce One.

  • DDML (Document Definition Markup Language or XSchema) was the "result of contributions from a large number of people on the XML-Dev mailing list, coordinated by a smaller group of editors" (Ronald Bourret, John Cowan, Ingo Macherius, and Simon St. Laurent) and was submitted as a note in January 1999 (http://www.w3.org/TR/NOTE-ddml). Its purpose was to "encode the logical (as opposed to physical) content of DTDs in an XML document." Great attention is paid to the definition of the back and forward conversions between DTDs and DDML, and the document also includes an "experimental" chapter proposing "Inline DDML Elements." DDML made a clear distinction between structures and data and left datatypes out.

  • W3C XML Schema, published as a Recommendation in May 2001 (http://www.w3.org/TR/xmlschema-0, http://www.w3.org/TR/xmlschema-1 and http://www.w3.org/TR/xmlschema-2) acknowledges the influence of DCD, DDML, SOX, XML-Data, and XDR in its list of references. It appears to have picked pieces from each of these proposals but is also a compromise between them. The main sponsors of the two languages still actively used and developed (Microsoft for XDR and Commerce One for SOX) both announced that they would support the W3C XML Schema for their new developments. W3C XML Schema will most likely become the only surviving member of this family in the long-term.

A.3.3. The RELAX NG Family

The RELAX NG family is a more traditional marriage between grammar-based XML Schema languages that have chosen to unite their strengths.

  • First published in March 2000 as a Japanese ISO Standard Technical Report written by Murata Makoto, Regular Language description for XML Core (RELAX; see http://www.xml.gr.jp/relax) is both simple ("Tired of complicated specifications? You just RELAX !") and built on a solid mathematical foundation (the adaptation of the hedge automata theory to XML trees). It was approved as an ISO/IEC Technical Report in May 2001.

  • XDuce (http://xduce.sourceforge.net) was first announced in March 2000."XDuce ('transduce') is a typed programming language that is specifically designed for processing XML data. One can read an XML document as an XDuce value, extract information from it or convert it to another format, and write out the result value as an XML document." Although it is not meant to be a schema language, its typing system has influenced the schema languages.

  • Published by James Clark in January 2001, TREX (Tree Regular Expressions for XML; see http://thaiopensource.com/trex) is "basically the type system of XDuce with an XML syntax and with a bunch of additional features." The names and content models of the elements used to define the tree patterns of a TREX schema have been carefully chosen, and TREX schemas are usually as easy to read as a plain text description. The simplicity of the structure of the language also allows the resurrection of a consistent treatment between elements and attributes, a feature lost since DCD.

  • Announced in May 2001, RELAX NG (RELAX New Generation) is a merger of RELAX and TREX, developed by an OASIS TC (http://www.oasis-open.org/committees/relax-ng), coedited by James Clark and Murata Makoto. "The key features of RELAX NG are that it is simple, easy to learn, uses XML syntax, does not change the information set of an XML document, supports XML namespaces, treats attributes uniformly with elements so far as possible, has unrestricted support for unordered content, has unrestricted support for mixed content, has a solid theoretical basis, and can partner with a separate datatyping language (such W3C XML Schema Datatypes)." RELAX NG is now an official specification of the OASIS RELAX NG Technical Committee and will probably progress to become an ISO/IEC International Standard as part of DSDL.

A.3.4. Schematron

Schematron (http://www.ascc.net/xml/resource/schematron/schematron.html), which was first proposed in September 1999 by Rick Jelliffe of the Academia Sinica Computing Centre, is an unusual schema language. It defines validation rules using XPath expressions. Schematron is also described in the ISO DSDL project.

A.3.5. Examplotron

Starting from the observations that instance documents are usually much easier to understand than the schemas that describe them, and that schema languages often need to give examples of instance documents to help human readers to understand their syntax, I proposed Examplotron (http://examplotron.org) in March 2001, to define "schemas by example" using sample instance documents as actual schemas.

Previous | Home | Next
A.2. Classification of XML Schema Languages | Book Index | A.4. Sample Application
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Sample Application (XML Schema)

 XML SchemaSearch this book

A.4. Sample Application

In the remainder of this appendix, we use the following simple library application, adapted from our first example in Chapter 1, "Schema Uses and Development" to illustrate the use of the various schema languages:

<library>
  <book id="-0836217462">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <author-ref id="Charles-M.-Schulz"/>
    <character-ref id="Peppermint-Patty"/>
    <character-ref id="Snoopy"/>
    <character-ref id="Schroeder"/>
    <character-ref id="Lucy"/>
  </book>
  <book id="-0805033106">
    <isbn>
      0805033106
    </isbn>
    <title>
      Peanuts Every Sunday
    </title>
    <author-ref id="Charles-M.-Schulz"/>
    <character-ref id="Sally-Brown"/>
    <character-ref id="Snoopy"/>
    <character-ref id="Linus"/>
    <character-ref id="Snoopy"/>
  </book>
  <author id="Charles-M.-Schulz">
    <name>
      Charles M. Schulz
    </name>
    <nickName>
      SPARKY
    </nickName>
    <born>
      November 26, 1922
    </born>
    <dead>
      February 12, 2000
    </dead>
  </author>
  <character id="Peppermint-Patty">
    <name>
      Peppermint Patty
    </name>
    <since>
      Aug. 22, 1966
    </since>
    <qualification>
      bold, brash and tomboyish
    </qualification>
  </character>
  <character id="Snoopy">
    <name>
      Snoopy
    </name>
    <since>
      October 4, 1950
    </since>
    <qualification>
      extroverted beagle
    </qualification>
  </character>
  <character id="Schroeder">
    <name>
      Schroeder
    </name>
    <since>
      May 30, 1951
    </since>
    <qualification>
      brought classical music to the Peanuts strip
    </qualification>
  </character>
  <character id="Lucy">
    <name>
      Lucy
    </name>
    <since>
      March 3, 1952
    </since>
    <qualification>
      bossy, crabby and selfish
    </qualification>
  </character>
  <character id="Sally-Brown">
    <name>
      Sally Brown
    </name>
    <since>
      Aug, 22, 1960
    </since>
    <qualification>
      always looks for the easy way out
    </qualification>
  </character>
  <character id="Linus">
    <name>
      Linus
    </name>
    <since>
      Sept. 19, 1952
    </since>
    <qualification>
      the intellectual of the gang
    </qualification>
  </character>
</library>
Previous | Home | Next
A.3. A Short History of XML Schema Languages | Book Index | A.5. XML DTDs
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML DTDs (XML Schema)

 XML SchemaSearch this book

A.5. XML DTDs

Inherited from SGML, the XML DTD is the most widely deployed means of defining an XML schema. Defined in the XML 1.0 Recommendation, DTDs do not support namespaces, which were specified later. This, together with the fact that its datatype system is weak and applies only to attributes, is one of the main motivations for the W3C to develop a new schema language. Table A-1 provides the fact sheet for XML DTDs.

Table A-1. XML DTD fact sheet

Author: | W3C.
Status: | Recommendation ("embedded" in XML 1.0).
Location: | http://www.w3.org/TR/REC-xml.
Type: | Grammar-based.
PSVI: | Yes (weak).
Structures: | Yes.
Datatypes: | Yes (weak).
Integrity: | Yes (internal through ID/IDREF/IDREFS attributes).
Rules: | No.
Vendor support: | Excellent.
Miscellaneous: | Non-XML syntax; no support for namespaces. Schema definition is only one of the features of DTDs. Requires deterministic content models.

A.5.1. Example

<!ELEMENT author (name, nickName, born, dead)>
<!ATTLIST author
  id ID #REQUIRED
>
<!ELEMENT author-ref EMPTY>
<!ATTLIST author-ref
  id IDREF #REQUIRED
>
<!ELEMENT book (isbn, title, author-ref*, character-ref*)>
<!ATTLIST book
  id ID #REQUIRED
>
<!ELEMENT born (#PCDATA)>
<!ELEMENT character (name, since, qualification)>
<!ATTLIST character
  id ID #REQUIRED
>
<!ELEMENT character-ref EMPTY>
<!ATTLIST character-ref
  id IDREF #REQUIRED
>
<!ELEMENT dead (#PCDATA)>
<!ELEMENT isbn (#PCDATA)>
<!ELEMENT library (book+, author*, character*)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT nickName (#PCDATA)>
<!ELEMENT qualification (#PCDATA)>
<!ELEMENT since (#PCDATA)>
<!ELEMENT title (#PCDATA)>
Previous | Home | Next
A.4. Sample Application | Book Index | A.6. W3C XML Schema
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


W3C XML Schema (XML Schema)

 XML SchemaSearch this book

A.6. W3C XML Schema

As we have seen, the W3C XML Schema is a strongly typed schema language that eliminates any nondeterministic design from the described markup to insure that there is no ambiguity in the determination of the datatypes, and that the validation can be performed by a finite state machine. Table A-2 provides the fact sheet for W3C XML Schema.

Table A-2. W3C XML Schema fact sheet

Author: | W3C.
Status: | Recommendation.
Location: | http://www.w3.org/TR/xmlschema-0.
Type: | object-oriented
PSVI: | Yes.
Structures: | Yes.
Datatypes: | Yes.
Integrity: | Yes (internal through ID/IDREF/IDREFS and xs:unique/xs:key/xs:keyref).
Rules: | No.
Vendor support: | Potentially excellent but currently still immature.
Miscellaneous: | Borrows many ideas from OOP design; considered complex; requires deterministic content models; part of the foundation of XML in the vision of the W3C.

A.6.1. Example

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="library">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="isbn" type="xs:string"/>
              <xs:element name="title" type="xs:string"/>
              <xs:element name="author-ref" minOccurs="0"
                maxOccurs="unbounded">
               <xs:complexType>
               <xs:attribute name="id" type="xs:IDREF"
                 use="required"/>
               </xs:complexType>
              </xs:element>
              <xs:element name="character-ref" minOccurs="0"
                maxOccurs="unbounded">
               <xs:complexType>
               <xs:attribute name="id" type="xs:IDREF"
                 use="required"/>
               </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID" use="required"/>
          </xs:complexType>
        </xs:element>
        <xs:element name="author" minOccurs="0"
          maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element ref="name"/>
              <xs:element name="nickName" type="xs:string"/>
              <xs:element name="born" type="xs:string"/>
              <xs:element name="dead" type="xs:string"/>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID" use="required"/>
          </xs:complexType>
        </xs:element>
        <xs:element name="character" minOccurs="0"
          maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element ref="name"/>
              <xs:element name="since" type="xs:string"/>
              <xs:element name="qualification" type="xs:string"/>
            </xs:sequence>
            <xs:attribute name="id" type="xs:ID" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="name" type="xs:string"/>
</xs:schema>
Previous | Home | Next
A.5. XML DTDs | Book Index | A.7. RELAX NG
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


RELAX NG (XML Schema)

 XML SchemaSearch this book

A.7. RELAX NG

Its editors (James Clark and Murata Makoto) define RELAX NG as "the next generation schema language for XML: clean, simple, and powerful." RELAX NG appears to be closer to a description of the instance documents in ordinary English as well as simpler than the W3C XML Schema, to which it might become a serious alternative. Many constraints, especially those on the fringe of nondeterministic models, can be expressed by RELAX NG and not by the W3C XML Schema. Some combinations in document structures forbidden by the W3C XML Schema can be described by RELAX NG. Even though RELAX NG seems to be technically superior to the W3C XML Schema, support by software vendors and XML developers is uncertain given that W3C XML Schema is a W3C Recommendation. Table A-3 provides the fact sheet for RELAX NG.

Table A-3. RELAX NG fact sheet

Author: | OASIS and possibly ISO/DSDL.
Status: | OASIS RELAX NG Committee Specification.
Location: | http://relaxng.org.
Type: | Grammar-based.
PSVI: | No.
Structures: | Yes.
Datatypes: | No, but a modular mechanism has been defined to plug in datatype systems (W3C XML Schema Part 2 and others if needed).
Integrity: | No.
Rules: | No.
Vendor support: | To be seen.
Miscellaneous: | Result of the merge between RELAX and TREX, might become an ISO IS. Strong mathematical grounding. Alternate non-XML syntax proposed by James Clark.

A.7.1. Example

<grammar
  datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
  xmlns="http://relaxng.org/ns/structure/1.0">
  <start>
    <ref name="library"/>
  </start>
  <define name="library">
    <element name="library">
      <oneOrMore>
        <ref name="book"/>
      </oneOrMore>
      <zeroOrMore>
        <ref name="author"/>
      </zeroOrMore>
      <zeroOrMore>
        <ref name="character"/>
      </zeroOrMore>
    </element>
  </define>
  <define name="author">
    <element name="author">
      <attribute name="id">
        <data type="ID"/>
      </attribute>
      <element name="name">
        <text/>
      </element>
      <element name="nickName">
        <text/>
      </element>
      <element name="born">
        <text/>
      </element>
      <element name="dead">
        <text/>
      </element>
    </element>
  </define>
  <define name="book">
    <element name="book">
      <ref name="id-attribute"/>
      <ref name="isbn"/>
      <ref name="title"/>
      <zeroOrMore>
        <element name="author-ref">
          <attribute name="id">
            <data type="IDREF"/>
          </attribute>
          <empty/>
        </element>
      </zeroOrMore>
      <zeroOrMore>
        <element name="character-ref">
          <attribute name="id">
            <data type="IDREF"/>
          </attribute>
          <empty/>
        </element>
      </zeroOrMore>
    </element>
  </define>
  <define name="id-attribute">
    <attribute name="id">
      <data type="ID"/>
    </attribute>
  </define>
  <define name="character">
    <element name="character">
      <ref name="id-attribute"/>
      <ref name="name"/>
      <ref name="since"/>
      <ref name="qualification"/>
    </element>
  </define>
  <define name="isbn">
    <element name="isbn">
      <text/>
    </element>
  </define>
  <define name="name">
    <element name="name">
      <text/>
    </element>
  </define>
  <define name="nickName">
    <element name="nickName">
      <text/>
    </element>
  </define>
  <define name="qualification">
    <element name="qualification">
      <text/>
    </element>
  </define>
  <define name="since">
    <element name="since">
      <data type="date"/>
    </element>
  </define>
  <define name="title">
    <element name="title">
      <text/>
    </element>
  </define>
</grammar>
Previous | Home | Next
A.6. W3C XML Schema | Book Index | A.8. Schematron
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schematron (XML Schema)

 XML SchemaSearch this book

A.8. Schematron

Schematron is a XPath/XSLT-based language for defining context-dependent rules. Schematron doesn't directly support structure or datatype validation, but a schema author may write rules that implement these checks. To write a full schema with Schematron, the author needs to take care to include all the rules needed to qualify the structure of the document. Table A-4 provides the fact sheet for Schematron.

Table A-4. Schematron fact sheet

Author: | Rick Jelliffe and other contributors. Will be described by ISO/DSDL.
Status: | Unofficial.
Location: | http://www.ascc.net/xml/schematron.
Type: | Rule-based.
PSVI: | No.
Structures: | No (although the structure can be controlled by rules).
Datatypes: | No (although the datatypes can be controlled by rules).
Integrity: | No (although the integrity can be controlled by rules).
Rules: | Yes.
Vendor support: | Low but increasing.
Miscellaneous: | Pure rule expression. Can be embedded in W3C XML Schema and RELAX NG.

A.8.1. Example

<sch:schema xmlns:sch="http://www.ascc.net/xml/schematron">
  <sch:title>
    Schematron Schema for library
  </sch:title>
  <sch:pattern>
    <sch:rule context="/">
      <sch:assert test="library">
        The document element should be "library".
      </sch:assert>
    </sch:rule>
    <sch:rule context="/library">
      <sch:assert test="book">
        There should be at least a book!
      </sch:assert>
      <sch:assert test="not(@*)">
        No attribute for library, please!
      </sch:assert>
    </sch:rule>
    <sch:rule context="/library/book">
      <sch:assert test="not(following-sibling::book/@id=@id)">
        Duplicated ID for this book.
      </sch:assert>
      <sch:assert test="@id=concat('-', isbn)">
        The id should be derived from the ISBN.
      </sch:assert>
    </sch:rule>
    <sch:rule context="/library/*">
      <sch:assert test="name()='book' or name( )='author' or
        name( )='character'">
        This element shouldn't be here...
      </sch:assert>
    </sch:rule>
  </sch:pattern>
</sch:schema>

Note that the previous Schematron is a partial schema meant as a supplement.

Previous | Home | Next
A.7. RELAX NG | Book Index | A.9. Examplotron
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Examplotron (XML Schema)

 XML SchemaSearch this book

A.9. Examplotron

Examplotron is an experiment to define a schema language based on sample trees, much like early proposals for XPath. Table A-5 provides the fact sheet for Examplotron.

Table A-5. Examplotron fact sheet

Author: | Eric van der Vlist.
Status: | Unofficial.
Location: | http://examplotron.org.
Type: | Grammar-based with native rules support.
PSVI: | No.
Structures: | Yes.
Datatypes: | No (although datatypes can be checked as rules).
Integrity: | No (although integrity can be checked as rules).
Rules: | Yes.
Vendor support: | Low.
Miscellaneous: | Schema by example (a sample document is a schema) with rule checking (syntax borrowed from Schematron).

A.9.1. Example

<library xmlns:eg="http://examplotron.org/0/">
  <book id="-0836217462" eg:occurs="+"
    eg:assert="not(following-sibling::book/@id=@id) and
    @id=concat('-',isbn)">
    <isbn>
      0836217462
    </isbn>
    <title>
      Being a Dog Is a Full-Time Job
    </title>
    <author-ref id="Charles-M.-Schulz" eg:occurs="*"/>
    <character-ref id="Peppermint-Patty" eg:occurs="*"/>
  </book>
  <author id="Charles-M.-Schulz" eg:occurs="*">
    <name>
      Charles M. Schulz
    </name>
    <nickName>
      SPARKY
    </nickName>
    <born>
      November 26, 1922
    </born>
    <dead>
      February 12, 2000
    </dead>
  </author>
  <character id="Peppermint-Patty" eg:occurs="*">
    <name>
      Peppermint Patty
    </name>
    <since>
      Aug. 22, 1966
    </since>
    <qualification>
      bold, brash and tomboyish
    </qualification>
  </character>
</library>
Previous | Home | Next
A.8. Schematron | Book Index | A.10. Decisions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Decisions (XML Schema)

 XML SchemaSearch this book

A.10. Decisions

The choice is open. XML schema languages are still a young technology and none of the candidates are perfect. In these conditions, the diversity allows you to choose the best tool adapted to each task: Schematron for the finishings or for very open applications, RELAX NG when the diversity and flexibility of the instance documents is critical, and W3C XML Schema when the mapping to object oriented applications is important or your partners require it!

Previous | Home | Next
A.9. Examplotron | Book Index | B. Work in Progress
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Work in Progress (XML Schema)

 XML SchemaSearch this book

Appendix B. Work in Progress

Contents:

W3C Projects
ISO: DSDL
Other

Although W3C XML Schema was approved as a W3C Recommendation in May 2001, it is still just getting started. This chapter identifies a short list of schema-related projects that seem either promising, useful, or just on the way.

B.1. W3C Projects

Edited by the W3C, the W3C XML Schema is considered by the consortium to belong to the very foundation of XML--together with XML 1.0 and namespaces in XML--and it does impact virtually all the other XML specifications. The most heavily affected seem to be the triumvirate XPath/XSLT/XQuery (and by consequence, XPointer- and XPointer-based specifications), DOM, and RDF.

B.1.1. XPath, XSLT, and XQuery

One of the most amazing things about XPath and XSLT 1.0 is that queries and transformations can be executed by applications with no prior knowledge of the structure of the documents on which they work. This is a major difference from previous information systems, such as RDBMS, in which the layout of the tables needs to be defined before any query can be run. Even though this works just fine in many circumstances, there are two main areas in which improvements can be obtained if the structure of the instance documents is known.

The first of these areas is optimization. This is not crucial for small documents, but as soon as the size of the document grows (which is typically the case in a XML database), any optimizer will need food for thought to perform his job. The first piece of basic information that is required is about the structure of the documents. The second is typed-aware comparisons and sorts. In XPath and XSLT 1.0, the sort order (numerical or string) is indicated in the XSLT style sheet and the comparisons are always done character by character. Sorting or comparing dates with different time zones is practically impossible in these conditions, and some type information coming out of a schema can help a lot.

For these reasons, XSLT 2.0, which will use XPath 2.0 like XSLT 1.0 uses XPath 1.0, and XQuery 1.0, which is a superset of XPath 2.0, both rely on the W3C XML Schema and use the information coming out of the PSVI.

This will indirectly impact a specification that relies on XPath, and XPointer (a specification that defines how fragments of XML documents can be addressed). It will also affect specifications using XPointer, such as XLink (definition of links between document fragments) and XInclude (inclusion of XML fragments). The case of XInclude is a good illustration of the need to define an overall processing model: XInclude relies on XPointer; XPointer relies on XPath, and XPath relies on the W3C XML Schema. This means that a XInclude processor will need the PSVI of the document containing the fragment to include, but this document may only be a container for the fragments and be invalid or even have no W3C XML Schema. On the contrary, from the schema viewpoint, the schema should be applied after the inclusion when the document is complete. Do we need to apply the schema processing before or after the inclusion (or both) ? This question is open.

TIP: XPath 2.0, XSLT 2.0, XQuery 1.0, XPointer, XLink, and XInclude are either works in progress or published recommendations; their specifications can be found on the W3C web site (http://www.w3.org).

B.1.2. DOM

The W3C DOM Working Group is creating the "Document Object Model (DOM) Level 3 Abstract Schemas and Load and Save Specification." This will define an abstract API to access either DTDs or the W3C XML Schema schemas. This API will be read/write, allowing reading, updating, and writing schemas in the same way DOM Core allows you to read, update, and write XML documents. It also provides extensions to DOM Core interfaces (the Element interface, for instance, is extended into an ElementEditAS interface) to access some information determined by the analysis of the schema.

The DOM Working Group has been very careful to make sure their "Abstract Schemas" remain abstract and as independent of the schema language as possible. The DOM work cannot be considered an API to the PSVI since it does not expose the PSVI but rather objects that are containers to query the schema implementation (to find out if an action such as adding, updating, or removing content from a DOM tree is allowed or not).

B.1.3. RDF

RDF (Resources Description Framework) can be seen as a way to express graphs in XML by splitting these graphs into elementary elements of information named "statements" or "triples." Each triple is a logic assertion associating a subject, verb, and object, such as in the phrase "The book 0836217462 (subject) has been written by Charles M Schulz (object)." RDF has its own schema language (RDF Schema) to model and constrain the relations themselves and define the inheritance between them. Since it's defined as a level on top of XML, a XML schema language does not act at the right level to model a set of RDF triples. However, RDF recognizes two kind of objects, resources identified by a URI and literals (i.e., raw values), and needs a simple datatype system to define constraints on those literals.

Although the idea of associating a W3C XML Schema simple datatype to RDF literals looks simple, it raises several issues. One is the lack of a way to identify W3C XML Schema simple datatypes that would be acceptable for RDF. As we've seen, RDF identifies any resource by a URI. To be coherent with the RDF data model, the simple types associated to the literals should be identified by URIs. On the other hand, the W3C XML Schema does not use the URIs to identify its datatypes but rather uses qualified names (QNames). Furthermore, it decided that elements, attributes, simple and complex types, and groups have independent sets of QNames. The QName bib:book can thus refer to an element, an attribute, a complex and simple type, and an element and attribute group of the same schema. The simple approach of identifing the simple type book through its expanded QName (replacing the prefix by the namespace URI) isn't yet implemented.

Previous | Home | Next
A.10. Decisions | Book Index | B.2. ISO: DSDL
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


ISO: DSDL (XML Schema)

 XML SchemaSearch this book

B.2. ISO: DSDL

ISO (ISO/IEC JTC 1/SC34, to be precise) launched a work named "Document Schema Definition Language" (DSDL) in December 2001, which will be "a multipart International Standard defining a modular set of specifications for describing the document structures, datatypes, and data relationships in structured information resources." DSDL includes efforts to classify the different XML schema languages into "path-based," "grammar-based," and "object-oriented" schema languages, to define a generic semantic for simple datatypes, and to define a common framework in which these languages may be used.

Working from the specifications of existing schema languages (Schematron, RELAX NG, DTDs, and W3C XML Schema), DSDL should clarify the situation by documenting their differences and how they might fit together. More information about DSDL is available at http://www.jtc1.org/FTP/Public/SC34/DOCREG/0275.htm and http://www.dsdl.org.

Previous | Home | Next
B. Work in Progress | Book Index | B.3. Other
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Other (XML Schema)

 XML SchemaSearch this book

B.3. Other

W3C XML Schema is inspiring some new technologies all its own.

B.3.1. PSVI Serialization

The PSVI (Post Schema Validation Infoset) is one of the most obscure features of the W3C XML Schema. Although present throughout the Recommendation, its specification is scattered into small sections describing the "PSVI contribution" of each feature. These contributions are described as plain text even when they are very abstract and difficult to visualize, like those of the ID/IDREF or unique/key/keyref tables. These may eventually be exposed through APIs that haven't been specified yet.

This level of abstraction appears to be due to the organization of the W3C divided into independent working groups. Since the charter of the W3C XML Schema Working Group is hidden on the private members' only section of the W3C web site, we may think it did not include the definition of the processing model of a schema validation and only focused on the definition of the language itself.

However, the effects of this lack of formal specification are similar to those of the unpublished APIs practices that some software editors are famous for: the lack of a concrete description is an obstacle for most users to understand the PSVI, and it creates a kind of "vendor lock-in," since generating the PSVI using another tool instead of W3C XML Schema involves emulating these unspecified APIs and may prove difficult for many developers.

Four years of XML have taught us that there is an easy way to serialize abstract concepts, and the definition of a XML serialization for the PSVI would have a lot of advantages. Assuming the format is simple enough, it would let us visualize what the PSVI is, allow us to process a PSVI using the standard set of XML APIs (DOM, SAX, and friends) and tools (including XSLT), make it easy to include into XML processing pipelines, allow us to save it for reuse, and permit us to generate it out of any application or tool able to generate XML documents.

A proposal has been written by Richard Tobin and Henry S. Thompson and informally published on the W3C web site (http://www.w3.org/2001/05/serialized-infoset-schema.html), but the format is heavyweight and difficult to read.

B.3.2. APIs

Even though the PSVI is produced by W3C XML Schema processors and used by XPath/XSLT 2.0 and XQuery 1.0, no API has been defined to communicate between these applications. The traditional XML APIs (DOM and SAX) have not yet been adapted yet to support this additional amount of information. The most advanced open implementation in this area seems to be the Xerces Native Interface (XNI; see http://xml.apache.org/xerces2-j/xni.html), which is a general framework to add information to the stream of basic events supported by SAX. While it is more generic than it needs to be to support the PSVI, XNI can be used when working with Xerces to expose the information from the PSVI. There is also a Microsoft implementation that similarly exposes information from the PSVI.

The need is there, applications will follow soon, and the general-purpose XML APIs (DOM, SAX and friends) need to take the PSVI into account if they do not want to be replaced by new APIs which will become de facto standards!

B.3.3. Schema Extensions: Error Messages

While the extension mechanisms through foreign attributes and xs:annotation are highly extensible, it might be useful to define a set of commonly used schema extensions that could become interoperable between schema processors. The principle would be similar to the EXSLT extensions (see http://www.exslt.org) proposed by an informal group of XSLT experts, which are now supported by a number of XSLT processors.

The error messages sent by schema processors are often very obscure and difficult for an end user to understand. A schema designer can often provide context-aware messages that are much clearer. Associated with a template, an extension for error messages could look like the following (the namespace URI is just an example):

<xs:simpleType name="dateTimeWithTimezone">
  <xs:restriction base="xs:dateTime">
    <xs:pattern value=".+T.+(Z|[+-].+)">
      <xs:annotation>
        <xs:appinfo>
          <exsd:error xmlns:exsd="http://dyomedea.com/ns/esxd">
            This date should specify a timezone.
          </exsd:error>
        </xs:appinfo>
      </xs:annotation>
    </xs:pattern>
  </xs:restriction>
</xs:simpleType>

or (simpler but less extensible):

<xs:simpleType name="dateTimeWithTimezone">
  <xs:restriction base="xs:dateTime">
    <xs:pattern value=".+T.+(Z|[+-].+)" exsd:error="This date
      should specify a timezone."
      xmlns:exsd="http://dyomedea.com/ns/esxd"/>
  </xs:restriction>
</xs:simpleType>
Previous | Home | Next
B.2. ISO: DSDL | Book Index | Glossary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Glossary (XML Schema)

 XML SchemaSearch this book

Glossary

abstract
A type of element or complex datatype that cannot be used directly in the instance documents. An abstract element must be substituted and is usually the head of a substitution group. An abstract complex type may be used to define content models, in which case the type will have to be substituted in the instance documents using xsi:type. There is no feature to define simple types as abstract (even though the predefined type xs:NOTATION could be considered abstract).

atom
In a regular expression, an atom expresses a condition on a substring. Atoms may be followed by a quantifier defining the expected number of the atom's occurrences. The atom, with its optional number of occurrences, constitutes a "piece." An atom may be a character, a wildcard, a special character, a character class, or a regular expression.

atomic type
A simple type that is not derived by list or union from another simple type.

attributes
Pieces of information attached to an element and defined in its start tag. Considered child nodes by the XPath data model, and considered property nodes by the DOM, attributes are "information items" to the XML Infoset.

attribute groups
Containers that allow you to define, reference, and redefine groups of attributes. B

base type
The datatype that is used as the starting point to define a new datatype by derivation by restriction or extension.

Berners-Lee, Tim
Inventor of HTML and HTTP, and Director of the W3C; he is considered the father of the World Wide Web (see http://www.w3.org/People/all#timbl).

block
Elements and complex datatypes that cannot be substituted in the instance documents. A blocked element or complex type is restricted in the substitutions that may occur in the instance documents. There is no feature to block simple types. C

canonical lexical representation
When a value in the value space may have different lexical representations in the lexical space, the W3C XML Schema Recommendation provides (when possible) a canonical representation, which is the most "normal" or "classical" and may be used as a reference. Although most of the types have canonical representations, some such as xs:duration or xs:QName, do not have one.

chameleon design
Importing a schema without a namespace into a schema with a target namespace is known as "chameleon design." This is because the imported schema takes the target namespace of the schema in which it is imported like a chameleon takes the color of the environment in which it is placed.

character class
In a regular expression, a character class is an atom matching a set of characters. Character classes may be classical Perl character classes, Unicode character classes, or user-defined character classes.

classical Perl character class
A set of character classes designated by a single letter, for which upper- and lowercases of the same letter are complementary (for instance, "\d" is all the decimal digits, and "\D" is all the characters that are not decimal digits).

complex content
An element has a complex content model when it has child element nodes only (and no text node).

component
Something that can be defined and referenced in a schema. Elements, attributes, simple and complex types, and element and attribute groups are components.

compositor
Containers that allow the manipulation of a set of elements as a whole and defines their relative order. Compositors include xs:sequence, xs:choice, and xs:all. Compositors may be included in other compositors to form complex combinations (with some limitations). Most can also be used as particles and have minOccurs and maxOccurs attributes, which allow definition of the number of repetitions expected for the whole group of elements that they define. The child elements of a compositor are "particles." A restriction applies to xs:all as a compositor: it can only include xs:element particles.

Consistent Declaration rule
This states that an element referenced by one "location" in a schema cannot be associated with two different simple or complex types.

content model
A description of the structure of children elements and text nodes (independent of attributes). The content model is "simple" when there is a text node but no elements, "complex" when there are element nodes but no text, "mixed" when there are text and element nodes, and "empty" when there are neither text nor element nodes. These definitions are commonly used by XML developers and slightly different from those of W3C XML Schema, for which there are only simple and complex content models. (Mixed models are considered special cases of complex contents, and empty models are considered either simple or complex contents with no child nodes.) D

datatype
A term used by W3C XML Schema to qualify both the content and the structure of an element or attribute. Datatypes can be either simple (when they describe an attribute or an element without an embedded element or attribute) or complex (when they describe elements with embedded child elements or attributes). W3C XML Schema datatypes should not be confused with XML 1.0 element types, which are called element names by W3C XML Schema.

default value
A value that is used when no value is provided in the instance document. Default values apply to attributes that are either empty or missing in the instance documents and that apply to empty elements.

derivation
The action of defining a datatype by using the definition of one or several other datatypes. Simple datatypes may be defined by derivation by restriction, list, or union, while complex datatypes can be defined by derivation by restriction or extension.

derivation by extension

derivation by list
The action of using a simple datatype (called the list type) to define a new simple datatype as a whitespace-separated list of values of the list type. Derivation by list applies only to simple datatypes.

derivation by restriction
For simple datatypes, a derivation by restriction is the action of defining a simple datatype by adding new constraints (called facets) on the lexical or value space of an existing datatype (called the base type). For complex datatypes, a derivation by restriction is the action of giving a new content model for the datatype that is a restriction of the base type.

derivation by union
The action of using a set of simple datatypes (called the member types) to define a new simple datatype whose lexical space is the union of the lexical spaces of the member types.

derived datatype
A datatype that is defined by derivation from other datatypes. They can be user-defined when defined in a schema, or predefined when defined in the W3C XML Schema Recommendation.

DOM
Document Object Model. An object-oriented model of XML documents, including the definition of the API allowing its manipulation. The third version of DOM (DOM Level 3) will include an API named "Abstract Schemas" to facilitate schema-guided editions of XML documents (see http://www.w3.org/TR/DOM-Level-3-Core).

DSDL
Document Schema Definition Language (DSDL) is a project undertaken by the ISO (ISO/IEC JTC 1/SC 34/WG 1, to be precise) whose objective is "to create a framework within which multiple validation tasks of different types can be applied to an XML document in order to achieve more complete validation results than just the application of a single technology" (see http://dsdl.org). DSDL has classified W3C XML Schema as "object-oriented schema language."

DTD
Document Type Definition. XML 1.0 DTDs are inherited from SGML, in which rules were included that allow the customization of the markup itself and played a very central role. Because of the syntactical rules included in their DTDs, SGML applications need a DTD to be able to read an SGML document. One of the simplifications of XML is to state that a XML parser should be able to read a document without needing a DTD. DTDs have therefore been simplified over their SGML ancestors and remain the first incarnation of what is today called a XML Schema language. E

element
One of the basic type of nodes in the tree represented by a XML document. An element is delimited by start and end tags. In the corresponding tree, an element is a nonterminal node, which may have subnodes of type element, character (text), and namespace and attribute, as well as comment and processing instruction nodes.

element type
Term used in the XML 1.0 Recommendation, which is equivalent to the notion of element names in W3C XML Schema and should not be confused with the simple or complex datatype of an element.

element groups
Containers that allow you to define, reference, and redefine groups of elements.

empty content
An element that has neither child element nor text nodes (with or without attributes). f

facet
A constraint added to the lexical or value space of a simple datatype during a derivation by restriction. The list of facets that can be used depends on the simple datatype. Facets can be "fixed" to disable their use during further derivations.

final
Elements and datatypes that cannot be substituted or derived any longer in the schema. A final element may not be chosen as the head of a substitution group while a final complex or simple type cannot be used as a base for further derivation.

fixed facets
Facets that are "fixed" during a derivation by restriction cannot be used during further derivations by restriction.

fixed values
A value that must match the value found in the instance document. Used as default values if no value is supplied. G

global definition
All the components (elements, attributes, simple and complex types, element and attribute groups) can be defined at the top level of the schema, directly under the xs:schema document element. Their definition is said to be "global," and they can be referenced elsewhere in the schema, as well as in any schema that has imported or included this schema. I

Infoset
XML Information Set. A formal description of the information that may be found in a well-formed XML document.

instance document
A XML document that is a candidate to be validated by a schema. Any well-formed XML 1.0 document that conforms to the Namespaces in XML 1.0 Recommendation can be considered a valid or invalid instance document.

item type
The simple datatype that is used as the starting point to define a new simple datatype using a derivation by list. L

lexical space
The set of all representations (after parsing and whitespace processing) allowed for a simple datatype.

local definition
Most of the components (elements, attributes, simple and complex types) can be defined inside of other components where they are used. Their definition is said to be "local" and they cannot be referenced in other parts of the schema.

local name
The name of a component in its namespace, i.e., the part of the qualified name that comes after the namespace prefix. M

member types
The simple datatypes used as the starting point to define a new simple datatype using a derivation by union.

mixed content
The content of an element that contains both child element and text nodes. N

namespace
A unique identifier that can be associated with a set of XML elements and attributes. This identifier is a URI, which is not required to point to an actual resource but must "belong" to the author of these elements and attributes. Since this full URI can't be included in the name of each element and attribute, a namespace prefix is assigned to the namespace URI through a namespace declaration. This prefix is added to the local name of the elements and attributes to form a qualified name. Namespaces are optional and elements and attributes may have no namespaces attached. W3C XML Schema has extended the scope of namespaces by using them not only for elements and attributes but also for all the components of a schema. A schema identifies the namespace of the components described in a schema as a target namespace. When these components do not have a namespace, the schema is said to have no target namespace. P

parsed space
The set of values that are sent by the parser to the applications. It is at the interface between the parser and the schema validator. Values from the parsed space undergo whitespace processing, as defined by their simple datatype, to feed the lexical space. The parsed space is, therefore, not visible by the facets.

particle
An element, such as a compositor, a group of elements (xs:group), an element definition or reference (xs:element), or an element wildcard (xs:any), which is included in a compositor to define a list of elements. A restriction applies to xs:all, which cannot be used as a particle even though it is defined as a compositor. The number of occurrences of particles may be constrained using their minOccurs and maxOccurs attributes.

pattern
A facet that allows definition of a regular expression, which will be applied to the lexical space to check its validity. By extension, the regular expression defined in a pattern is often called "pattern" as well.

piece
Regular expressions (or patterns) are composed of pieces. Each piece is itself composed of an atom describing a condition on a substring and an optional quantifier defining the expected number of occurrences of the atom.

predefined datatype
The simple datatypes (both primitive and derived) that are defined in the W3C XML Schema Recommendation.

primitive datatype
A simple datatype that cannot be defined by derivation from other datatypes. There is no way to create primitive datatypes, so all the primitive datatypes are therefore predefined.

PSVI
The Post Schema Validation Infoset. The Infoset after the information gathered during a schema validation is added. Q

qualified element or attribute
Elements and attributes that belong to a namespace; i.e., a namespace URI is defined for them. The name of qualified elements may have no prefix if a default namespace is defined, but the name of qualified attributes must be prefixed.

qualified name
The complete name of a component, including the prefix associated to its target namespace if one is defined. R

RDBMS
Relational DataBase Management System. Developed in the late 70s, this system has taken most of the database market and hosts a significant amount of the data of many organizations. XML Schema languages may help to insure the interface between that information and XML documents.

Recommendation
Specifications published by the W3C. They cannot be officially called "standards," since the W3C is a consortium that does not have the status of the standard body reserved for the ISO and national standard bodies. The specifications, which are finalized and approved by the Director, are then called "W3C Recommendations."

reference
All of the components (elements, attributes, simple and complex types, element and attribute groups) that have been created with a global definition can be referenced when needed in the schema in which they are defined, and in any schema that has imported or included this schema. Their definition is used at the location where they are referenced.

regular expression
A syntax to express conditions on strings. The syntax used by the W3C XML Schema for its patterns is very close to the syntax introduced by the Perl programming language. A regular expression is composed of elementary "pieces."

RELAX
A grammar-based XML Schema language developed by Murata Makoto and published in March 2000 as a Japanese ISO Standard (see http://www.xml.gr.jp/relax).

RELAX NG
A grammar-based XML Schema language resulting from a merger between RELAX and TREX (see http://relaxng.org). S

SAX
Simple API for XML. A streaming event-based API used between parsers and applications. Its streaming nature means that pipelines of XML processing may be created using SAX (see http://www.saxproject.org).

Schematron
A rule-based XML Schema language, developed by Rick Jelliffe, using XPath expressions to describe validation rules (see http://www.ascc.net/xml/resource/schematron/schematron.html).

serialization space
The set of values as they are stored in a document. These values are transformed by the parser, as defined in the Recommendation XML 1.0, before reaching the application. The serialization space is not visible to the schema processors.

SGML
Standard Generalized Markup Language. Created in 1980, the ancestor of XML. XML was designed as a simplified subset of SGML to be used on the Web.

simple content
An element has a simple content model when it has a child text node only (and no subelements). A simple content element has a simple type if it has no attributes, and it has a complex type if it has any attributes.

simple datatype
A datatype that accepts only a text value. Simple datatypes can be directly assigned to attributes and simple content elements that do not accept any attribute. Simple datatypes can be used to define complex datatypes by extension.

SOAP
The major XML protocol used by Web Services; relies on W3C XML Schema to describe the messages exchanged (see http://www.w3.org/TR/SOAP).

space
W3C XML Schema uses the term "space" to mean a set of values (lexical versus value spaces). For completeness, we introduced two additional spaces in this book (the serialization and parsed spaces).

special character
A character that may be used as an atom after a "\" to accept a specific character, either for convenience or because this character is interpreted differently in the context of a regular expression.

substitution group
A feature of W3C XML Schema, allowing you to define groups of elements that may be used interchangeably in instance documents. They are not declared as element groups, but through the substitutionGroup attribute of xs:element global definitions. T

target namespace
The namespace of the components described in a schema. When these components do not have a namespace, the schema is said to have no target namespace.

TREX
A grammar-based XML Schema language developed by James Clark (see http://www.thaiopensource.com/trex). U

Unicode block
A set of characters classified by their "localization" (Latin, Arabic, Hebrew, Tibetan, and even Gothic or musical symbols).

Unicode category
A set of characters classified by their usage (letters, uppercase, digit, punctuation, etc.).

Unicode character class
A set of character classes defined based on the Unicode blocks and categories.

unqualified element or attribute
Elements and attributes that don't belong to a namespace; i.e., no namespace URI is defined for them. Any unprefixed attribute is unqualified, but unprefixed elements are unqualified only if no default namespace is defined.

UPA rule
The UPA (Unique Particle Attribution) rule states that at any given moment, a W3C XML Schema processor must know—without ambiguity and without needing any forward reference in the document—which particle in the schema describes an element in the instance document. This rule is roughly equivalent to the restrictions known as "non-deterministic content models" for the XML 1.0 DTDs and as "ambiguous content models" by SGML. The UPA rule is often associated with the "Consistent Declaration rule."

URI
Uniform Resource Identifier. Defined by the RFCs 2396 and 2732. URIs were created to extend the notion of URLs (Uniform Resource Locators) to include abstract identifiers that do not necessarily need to "locate" a resource.

URL
Uniform Resource Locator, a common identifier used on the Web. URLs are absolute when the full path to the resource is indicated, and relative when a partial path is given that needs to be evaluated in relation with a base URL.

user-defined character class

user-defined datatype
Datatypes that are defined in a schema. All the datatypes can be defined by derivation or, for the complex datatypes only, by definition. V

valid
A XML document that is well-formed and conforms to a schema (DTD, W3C XML Schema, etc.) of some kind.

value space
The set of all the possible values for a simple datatype, independent of their actual representation in the instance documents. W

W3C
World Wide Web Consortium. Originally created to settle HTML and HTTP as de facto standards. The main specification body for the core specifications of the World Wide Web and the keeper of the core XML specifications (see http://www.w3.org).

Web Services
An approach to using the Web for applications, as opposed to the Web for human consumption that we use on a daily basis. Those services rely on the same infrastructure as the Web, and exchange XML documents over HTTP though a layer of protocols (such as SOAP or XML-RPC), which are themselves based on XML. XML Schema languages are used by these services to describe and control the XML documents that are exchanged.

well-formed
An XML document that meets the conditions defined in the XML 1.0 Recommendation: it must be readable without ambiguity. Syntax errors will be detected by a XML parser without schema of any type.

whitespace
Characters #x9 (tab), #xA (linefeed), #xD (carriage return), and #x20 (space). These are often used to indent the XML documents to give them a more readable aspect, and are filtered by an operation named "whitespace processing."

whitespace collapsing
The action of applying the whitespace replacement, trimming the leading and trailing spaces, and replacing all the sequences of contiguous whitespaces by a single space between the parsed and lexical spaces. Most of the simple datatypes apply whitespace collapsing.

whitespace preservation
The action of preserving all the whitespaces from the parsed to the lexical space. The xs:string datatypes and the user-defined simple types derived from xs:string (which do not change the value of the xs:whitespace facet) are the only datatypes applying whitespace preservation.

whitespace processing
The operation of filtering that is done on the whitespaces present in the value of a simple datatype. The whitespace processing is done during the transformation between parsed and lexical spaces. W3C XML Schema defines three whitespace processing approaches (depending on the simple type): whitespace preservation, whitespace replacement, and whitespace collapsing.

whitespace replacement
The action of replacing all the occurrences of the characters #x9 (tab), #xA (linefeed), and #xD (carriage return) by a #x20 (space) between the parsed and the lexical space. Whitespace replacement doesn't change the length of the string. xs:normalizedString and the user-defined simple types derived from xs:string and xs:normalizedString (for which the value of the xs:whitespace facet is "replace") are the only datatypes that apply whitespace replacement.

wildcard
A character used as an atom in a regular expression to accept a set of characters. W3C XML Schema supports only one such wildcard: the character ".", which means "any character." This expression is also used to designate the xs:any and xs:anyAttribute particles. X

Xerces
The XML parser developed by the XML Apache project (see http://xml.apache.org/xerces2-j/index.html).

XInclude
A W3C specification defining a general purpose inclusion mechanism for XML documents (see http://www.w3.org/TR/xinclude).

XLink
XML Linking Language is a W3C Recommendation (http://www.w3.org/TR/xlink) "which allows elements to be inserted into XML documents in order to create and describe links between resources."

XML
Extensible Markup Language. A subset of SGML created to be used on the Web. Its core specification (XML 1.0) was published by the W3C in February 1998. New specifications have been added since this date, and the W3C considers that, with the addition of W3C XML Schema, the core specifications are now complete.

XML-RPC
Considered the ancestor of SOAP, XML-RPC is a simple XML protocol that may be used to implement Web Services. It does not rely on the W3C XML Schema to describe the content of its messages but has defined a simpler binding mechanism (see http://www.xmlrpc.com).

XPath
A query language used to identify a set of nodes within a XML document. Originally defined to be used with XSLT, it is also used by XPointer and a simple subset is used in the xs:key, xs:keyref, and xs:unique W3C XML Schema elements. The XQuery specification will be a superset of the second version of XPath. This version will use type information provided by W3C XML Schema (see http://www.w3.org/TR/xpath).

XQuery
XML Query language. This will be a superset of XPath 2.0 that will use type information provided by the W3C XML Schema to optimize its queries, and for features such as sort orders (see http://www.w3.org/TR/xquery).

XSLT
Extensible Stylesheet Language Transformations. A programming language specialized for the transformation of XML documents (see http://www.w3.org/TR/xslt).

XSV
An open source W3C XML Schema implementation available at http://www.w3.org/2001/03/webdata/xsv.

Previous | Home | Next
B.3. Other | Book Index | Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Schema: Index

XML SchemaSearch this book

Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Symbols & Numbers

- (minus sign) for ranges in user-defined character classes: 6.4.3.3. User-defined character classes
- special character: 6.4.1. Special Characters
^ (caret) for negating lists in user-defined character classes: 6.4.3.3. User-defined character classes
( ) (parentheses) group character: 6.4.4. Oring and Grouping
. (period) wildcard character: 6.4.2. Wildcard
\(slash) escape character: 6.2. The Simplest Possible Patterns
special characters: 6.4.1. Special Characters
user-defined character classes: 6.4.3.3. User-defined character classes
| special character: 6.4.1. Special Characters
[] (square brackets) for user-defined character classes: 6.4.3.3. User-defined character classes
| (vertical bar) or character: 6.4.4. Oring and Grouping
<Emphasis>Namespaces in XML<Default Para Font>: 10. Controlling Namespaces

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

abstract attribute: 11.2.2. Defining Complex Types
complex type definitions and: 12.2.3.3. Abstract complex types
defining head elements: 12.1.2.2. Abstract elements
substitution groups and: 12.2.2.3. Abstract elements
Academia Sinica Computing Centre: A.3.4. Schematron
accessors, polymorphic: 11.2.1. Defining Simple Types
adornments, schema: 14.2. The W3C XML Schema Annotation Element
#all value (block attribute): 12.2.2.1. Block attribute
12.2.2.1. Block attribute
ambiguous content models: 7.4.1.3. Unique Particle Attribution Rule
anonymous vs. named datatypes: 7.2.2. Named Versus Anonymous Types
##any wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
10.11. Allowing Any Elements or Attributes from a Particular Namespace
anySimpleType type: 4.7. What About anySimpleType?
Apache's XNI: B.3.2. APIs
atomic datatypes, deriving list datatypes from: 5.2. Derivation By List
atoms in patterns: 6.3. Quantifying
character classes and: 6.4.3. Character Classes
group character: 6.4.4. Oring and Grouping
or character: 6.4.4. Oring and Grouping
special characters: 6.4.1. Special Characters
user-defined character classes: 6.4.3.3. User-defined character classes
wildcard character: 6.4.2. Wildcard
attribute groups: 7.4.1.2. Element and attribute groups
defining: 13.1.1.2. Attributes
redefining: 8.2.2. Redefinition of Element and Attribute Groups
by extension: 8.2.2.1. Extension
by restriction: 8.2.2.2. Restriction
rules for, vs. complex types: 8.2.2.2. Restriction
attributeFormDefault attribute: 10.3. To Qualify Or Not to Qualify?
attributes
creating classified lists of: 2.2. Our First Schema
defining
globally: 2.2. Our First Schema
locally: 3.1. Working From the Structure of the Instance Document
3.2.2. Russian Doll and Object-Oriented Design
using xs:ID and xs:IDREF types: 9.1. and
qualified vs. unqualified: 10.3. To Qualify Or Not to Qualify?
XLink: 10.7.2. XLink Attributes
XML: 10.7.1. XML Attributes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

base attribute, referencing simple type definitions with: 7.3.1. Creation of Simple Content Models
base64 encoding, predefined datatype for: 4.3.3.5. Binary string-encoded datatypes
BasicLatin Unicode block: 6.5.1.1. Unicode blocks
binary string-encoded datatypes: 4.3.3.5. Binary string-encoded datatypes
binding
data: 1.1.4. Data Binding
early vs. late: A.1.4. Early Binding Tools
block attribute: 11.2.2. Defining Complex Types
complex type definitions and: 12.2.3.1. Blocking complex types
substitution groups and: 12.2.2.1. Block attribute
blockDefault attribute: 12.2.2.1. Block attribute
blocks, Unicode: 6.4.3.2. Unicode character classes
Bourret, Ronald: 1.1.4. Data Binding
A.3.2. The W3C XML Schema Family
Bray, Tim: 7.4.1.3. Unique Particle Attribution Rule
British Summer Time (BST): 4.5.1. The Realm of ISO 8601
BST (British Summer Time): 4.5.1. The Realm of ISO 8601

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

c character class: 6.4.3.1. Classical Perl character classes
Canonical XML and namespace prefixes: 10.2. Namespace Declarations
categories, Unicode: 6.4.3.2. Unicode character classes
chameleon schemas: 13.1.3. Splitting Schema Components
character classes: 6.4.3. Character Classes
children elements: 2.2. Our First Schema
recursive schemas and: 3.2.1. Depth Versus Modularity?
Clark, James: 7.4.1.3. Unique Particle Attribution Rule
A.1.3. Intrusive Modeling Tools
A.3.3. The RELAX NG Family
A.7. RELAX NG
"classical" Perl character classes: 6.4.3.1. Classical Perl character classes
co-occurrence constraints: 9.4. Using and As Co-occurrence Constraints
Codd, E. F.: 11.3. Defining Nil (Null) Values
collapsed strings: 4.3.3. Collapsed Strings
comma-separated-value lists, not described by XML Schema: 5.2.1. List Datatypes
comments
adding to schemas, using xs:annotation element: 14.2. The W3C XML Schema Annotation Element
XML 1.0: 14.4. XML 1.0 Comments
complements of character classes: 6.4.3.3. User-defined character classes
complex content models: 2.2. Our First Schema
asymmetry of derivation methods: 7.4.2.3. Asymmetry of these two methods
creating: 7.4.1. Creation of Complex Content
empty content models as: 7.6.1.2. As complex content models
defining: 2.2. Our First Schema
derivation by extension: 7.4.2.1. Derivation by extension
7.7. Back to Our Library
derivation by restriction: 7.4.2.2. Derivation by restriction
7.7. Back to Our Library
11.2.2. Defining Complex Types
deriving from mixed contents: 7.5.2.3. Derivation between complex and mixed content models
example of: 7.7. Back to Our Library
complex datatypes: 2.2. Our First Schema
abstract attribute and: 12.2.3.3. Abstract complex types
block attribute and: 12.2.3.1. Blocking complex types
creating: 7. Creating Complex Datatypes
vs. deriving: 7.2.3. Creation Versus Derivation
with complex content models: 7.4.1. Creation of Complex Content
with empty content models: 7.6.1. Creation of Empty Content Models
with mixed content models: 7.5.1. Creating Mixed Content Models
with simple content models: 7.3.1. Creation of Simple Content Models
defining: 2.2. Our First Schema
11.2.2. Defining Complex Types
globally: 13.1.1.1. Elements
extensibility features of different styles: 13.1.1.1. Elements
final attribute and: 12.2.3.2. Final complex types
included schemas: 8.1. Schema Inclusion
example of: 8.4. Simplifying the Library
redefining: 8.2.1. Redefining of Simple and Complex Types
composite fields, defining: 9.2.2. Composite Fields
compositors (element roles): 7.4.1.1. Compositors and particles
Consistent Declaration Rule: 7.4.1.4. Consistent Declaration Rule
co-occurrence constraints as workaround to: 9.4. Using and As Co-occurrence Constraints
xsi:type as workaround against: 11.2.2. Defining Complex Types
constraints
co-occurrence: 9.4. Using and As Co-occurrence Constraints
composite fields and: 9.2.2. Composite Fields
key elements and: 9.2.3. Keys
testing for uniqueness: 9.2.1. Uniqueness
content models: 2.2. Our First Schema
7. Creating Complex Datatypes
(see also complex content models; mixed content models; simple content models)
allowing two for same element: 11.2.2. Defining Complex Types
co-occurrence constraints and: 9.4. Using and As Co-occurrence Constraints
deterministic vs. nondeterministic: 7.4.1.3. Unique Particle Attribution Rule
open schemas and: 13.2. The Need for Open Schemas
relationship between child text/element nodes and: 7.2.1. Content Models
reusable attributes/elements, creating: 7.4.1.2. Element and attribute groups
8. Creating Building Blocks
unordered, limitations on: 7.4.1.5. Limitations on unordered content models
Coordinated Universal Time (UTC): 4.5.1. The Realm of ISO 8601
4.5.1. The Realm of ISO 8601
counting words with regular expressions: 6.5.1.2. Counting words
Cowan, John: A.3.2. The W3C XML Schema Family

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

d character class: 6.4.3.1. Classical Perl character classes
datatypes: 4. Using Predefined Simple Datatypes
(see also entries under xs:)
adding constraints on: 5.5. Back to Our Library
applying new restrictions to: 5.1.2. Multiple Restrictions and Fixed Attribute
complex (see complex datatypes)
date: 4.5. Date and Time Datatypes
decimal: 4.4.1. Decimal Types
facets for: 5.1.1.6. Decimals
float: 4.4.2. Float Datatypes
lexical and value spaces: 4.1. Lexical and Value Spaces
list (see list datatypes)
numeric: 4.4. Numeric Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
predefined: 4. Using Predefined Simple Datatypes
used in schemas: 4.8. Back to Our Library
reference guide of: 16. Datatype Reference Guide
restricting: 7.3.2.2. Derivation by restriction
simple (see simple datatypes)
string: 4.3. String Datatypes
time: 4.5. Date and Time Datatypes
date datatypes: 4.5. Date and Time Datatypes
constraining when no time zones are needed: 6.6. Back to Our Library
facets for: 5.1.1.4. Date and time datatypes
regular expressions and: 6.5.3. Datetimes
datetime datatypes (see date datatypes time datatypes)
DCD (Document Content Description for XML) proposal: A.3.2. The W3C XML Schema Family
DDML (Document Definition Markup Language or XSchema) proposal: A.3.2. The W3C XML Schema Family
decimal datatypes, facets for: 5.1.1.6. Decimals
decimal types: 4.4.1. Decimal Types
declarations, namespace: 10.2. Namespace Declarations
default namespaces: 10.1. Namespaces Present Two Challenges to Schema Languages
importing schemas with no namespaces: 10.9. Importing Schemas with No Namespaces
not applied to
attributes: 10.3. To Qualify Or Not to Qualify?
10.4. Disruptive Attributes
XPath expressions: 10.5. Namespaces and XPath Expressions
using target namespaces as: 10.3. To Qualify Or Not to Qualify?
derivation methods: 5. Creating Simple Datatypes
asymmetry of: 7.4.2.3. Asymmetry of these two methods
controlling usage of: 12.2. Controlling Derivations
by extension
of complex content complex types: 7.4.2.1. Derivation by extension
7.7. Back to Our Library
of empty content complex types: 7.6.2.1. Derivation by extension
of mixed content complex types: 7.5.2.1. Derivation by extension
of simple content complex types: 7.3.2.1. Derivation by extension
importance of order: 5.4.1. Beware of the Order
by list: 5.2. Derivation By List
by restriction: 5.1. Derivation By Restriction
of complex content complex types: 7.4.2.2. Derivation by restriction
7.7. Back to Our Library
11.2.2. Defining Complex Types
12.1.4.2. Restricting substitution groups
of empty content complex types: 7.6.2.2. Derivation by restriction
of mixed content complex types: 7.5.2.2. Derivation by restriction
of simple content complex types: 7.3.2.2. Derivation by restriction
by union: 5.3. Derivation By Union
11.2.1. Defining Simple Types
deterministic content models: 7.4.1.3. Unique Particle Attribution Rule
Document Content Description for XML (DCD) proposal: A.3.2. The W3C XML Schema Family
Document Definition Markup Language or XSchema (DDML) proposal: A.3.2. The W3C XML Schema Family
Document Schema Definition Language (DSDL): B.2. ISO: DSDL
Document Type Definitions (DTDs): 1.2. W3C XML Schema
A.3.1. The DTD Family
sample application: A.5. XML DTDs
documenting
schemas: 14. Documenting Schemas
XML vocabularies: 1.1.2. Documentation
DOM Level 3 specification, W3C XML Schema's impact on: B.1.2. DOM
DSDL (Document Schema Definition Language): B.2. ISO: DSDL
DTDs (Document Type Definitions): 1.2. W3C XML Schema
A.3.1. The DTD Family
sample application: A.5. XML DTDs
Dublin Core elements, allowed in schema annotations: 14.2. The W3C XML Schema Annotation Element
duration, datatype for: 4.5.2. Datatypes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

element groups: 7.4.1.2. Element and attribute groups
defining: 13.1.1.1. Elements
13.1.1.1. Elements
extensibility of, using substitution groups: 12.1. Substitution Groups
redefining: 8.2.2. Redefinition of Element and Attribute Groups
by extension: 8.2.2.1. Extension
by restriction: 8.2.2.2. Restriction
vs. substitution groups: 12.1.3. Traditional Declarations or Substitution Groups?
element types
changed from XML 1.0 Recommendation: 3.2.3. Where Have the Element Types Gone?
defining: 11.2. Defining Element Types
simple vs. complex: 2.2. Our First Schema
xsi:type attribute and: 11.2. Defining Element Types
elementFormDefault attribute: 10.3. To Qualify Or Not to Qualify?
elements
controlling derivations: 12.2.2. Elements
creating classified lists of: 2.2. Our First Schema
defining
globally: 2.2. Our First Schema
locally: 3.1. Working From the Structure of the Instance Document
3.2.2. Russian Doll and Object-Oriented Design
using xs:ID and xs:IDREF types: 9.1. and
foreign attributes accepted by: 14.3. Foreign Attributes
global (see global elements)
reference guide of: 15. Elements Reference Guide
empty content models: 2.2. Our First Schema
creating: 7.6.1. Creation of Empty Content Models
defining: 7.7. Back to Our Library
derivation by extension: 7.6.2.1. Derivation by extension
derivation by restriction: 7.6.2.2. Derivation by restriction
simple vs. complex types for: 7.6.3. Simple or Complex Content Models for Empty Content Models?
vs. null elements: 11.3. Defining Nil (Null) Values
encodings, datatypes for: 4.3.3.5. Binary string-encoded datatypes
English, Joe: 7.4.1.3. Unique Particle Attribution Rule
error messages and schema extensions: B.3.3. Schema Extensions: Error Messages
errors, detecting using validation: 1.1.1. Validation
escape character ( ): 6.2. The Simplest Possible Patterns
special characters: 6.4.1. Special Characters
user-defined character classes: 6.4.3.3. User-defined character classes
Examplotron schema language: A.3.5. Examplotron
sample application: A.9. Examplotron
extensible schemas: 13. Creating Extensible Schemas
extension, derivation by (see derivation methods, by extension)
extensions, schema: B.3.3. Schema Extensions: Error Messages
external parsed entities, using instead of xs:include and xs:reference: 8.3.1. External Parsed Entities

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

facets: 5.1.1. Facets
fixed attribute and: 5.1.2.5. Fixed facets
multiple restrictions and: 5.1.2. Multiple Restrictions and Fixed Attribute
non-whitespace-collapsed strings: 5.1.1.2. Other strings
restricting lexical space of simple datatypes: 6. Using Regular Expressions to Specify Simple Datatypes
whitespace-collapsed strings: 5.1.1.1. Whitespace collapsed strings
final attribute
complex type definitions and: 12.2.3.2. Final complex types
extensible schemas and: 13.1.2. final and fixed Attributes
simple type definitions and: 12.2.4. Simple Types
substitution groups and: 12.2.2.2. Final elements
finalDefault attribute
simple type definitions and: 12.2.4. Simple Types
substitution groups and: 12.2.2.2. Final elements
finite state machines (FSM): 7.4.1.3. Unique Particle Attribution Rule
firewalls
open vs. closed: A.1.2. Firewalls Against Diversity
validating documents and: 1.1.1. Validation
fixed attribute
controlling derivation of simple types: 12.2.4. Simple Types
extensible schemas and: 13.1.2. final and fixed Attributes
included in facets: 5.1.2.5. Fixed facets
flat schema design: 2.2. Our First Schema
2.3.3. Flat Design, Global Components
creating readable schemas: 14.1.3. When It's Similar, Show It
recursive schema and: 3.2.1. Depth Versus Modularity?
flexible schemas, creating: 13. Creating Extensible Schemas
float datatypes: 4.4.2. Float Datatypes
regular expressions and: 6.5.2. Numeric and Float Types
FSM (finite state machines): 7.4.1.3. Unique Particle Attribution Rule

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

global attributes: 2.3.3. Flat Design, Global Components
13.1.1.2. Attributes
defining: 13.1.1.2. Attributes
in Namespaces in XML: 10.3. To Qualify Or Not to Qualify?
10.4. Disruptive Attributes
limitations of: 6.6. Back to Our Library
global definitions of complex datatypes: 7.2.2. Named Versus Anonymous Types
creating simple content models: 7.3.1. Creation of Simple Content Models
xs:redefine element and: 8.2. Schema Inclusion with Redefinition
13.1.1.1. Elements
13.1.1.1. Elements
global elements: 2.3.3. Flat Design, Global Components
13.1.1.1. Elements
defining: 13.1.1.1. Elements
13.1.1.1. Elements
documenting schemas: 14.1.2. Think Globally
substitution groups and: 12.1. Substitution Groups
12.1.2.1. Using substitution groups
globally defining components: 2.2. Our First Schema
13.1.1. Global Components
glossary: Gloss
grammar-based XML Schema languages: A.2.2. Grammar-Based XML Schema Languages
group character ( ) (parentheses): 6.4.4. Oring and Grouping
groups
attribute (see attribute groups)
element (see element groups)
guided editing applications: 1.1.5. Guided Editing

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

head elements of substitution groups: 12.1.2.1. Using substitution groups
defining, using abstract attribute: 12.1.2.2. Abstract elements
hexadecimal encoding, predefined datatype for: 4.3.3.5. Binary string-encoded datatypes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

i character class: 6.4.3.1. Classical Perl character classes
identifiers: 4.3.3.1. Tokenss
xs:ID and xs:IDREF types: 9.1. and
imported schemas
namespace behavior of: 10.8. Namespace Behavior of Imported Components
referencing other namespaces: 10.6. Referencing Other Namespaces
splitting schema components: 13.1.3. Splitting Schema Components
included schemas (see schemas, inclusion xsinclude element)
instance documents
adapting to constraints of xs:all (outside a group): 7.4.1.5.2. Adapting the structure of your document
associating schemas with: 11.1. Associating Schemas with Instance Documents
co-occurrence constraints in: 9.4. Using and As Co-occurrence Constraints
creating: 2.1. The Instance Document
defining complex types in: 11.2.2. Defining Complex Types
defining simple types in: 11.2.1. Defining Simple Types
locally defining components: 3.1. Working From the Structure of the Instance Document
null values in: 11.3. Defining Nil (Null) Values
intersections of character classes: 6.4.3.3. User-defined character classes
ISO 8601 standard and date/time formats: 4.5.1. The Realm of ISO 8601
14.2. The W3C XML Schema Annotation Element
ISO DSDL (Document Schema Definition Language): B.2. ISO: DSDL
itemType attribute: 5.2.1. List Datatypes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

Jelliffe, Rick: A.3.4. Schematron

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: K

Kawaguchi, Kohsuke: 7.8. Derivation or Groups
keys/key references, defining: 9.2.3. Keys

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

languages, limiting number of: 5.5. Back to Our Library
Latin-1Supplement Unicode block: 6.5.1.1. Unicode blocks
leading zeros, removing with regular expressions: 6.5.2.1. Leading zeros
length-constraining facets: 5.1.1.1.2.
using lists to change behavior of: 5.4.2. Using or Abusing Lists to Change the Behavior of Length Constraining Facets
lexical spaces: 4.1. Lexical and Value Spaces
fixing format of: 6.5.2.2. Fixed format
multiple restrictions and: 5.1.2. Multiple Restrictions and Fixed Attribute
restricting, using regular expressions: 6. Using Regular Expressions to Specify Simple Datatypes
of xs:ID and xs:IDREF types: 9.1. and
list datatypes: 4.6. List Types
5.2.1. List Datatypes
defining by embedding xs:simpleType (global definition): 5.2.1. List Datatypes
deriving from atomic datatypes: 5.2. Derivation By List
local complex types: 7.2.2. Named Versus Anonymous Types
##local wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
10.11. Allowing Any Elements or Attributes from a Particular Namespace
locally defining components: 3.1. Working From the Structure of the Instance Document
Russian doll design: 3.2.2. Russian Doll and Object-Oriented Design
when to use: 3.2.1. Depth Versus Modularity?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

Macherius, Ingo: A.3.2. The W3C XML Schema Family
maxOccurs attribute: 2.2. Our First Schema
compositors and particles: 7.4.1.1. Compositors and particles
extending attribute/element groups, issues with: 8.2.2.1. Extension
minOccurs attribute: 2.2. Our First Schema
compositors and particles: 7.4.1.1. Compositors and particles
extending attribute/element groups, issues with: 8.2.2.1. Extension
mixed attribute: 7.2.1. Content Models
mixed content models: 2.2. Our First Schema
creating: 7.5.1. Creating Mixed Content Models
derivation by extension: 7.5.2.1. Derivation by extension
derivation by restriction: 7.5.2.2. Derivation by restriction
deriving from complex contents: 7.5.2.3. Derivation between complex and mixed content models
xs:annotation element and: 14.2. The W3C XML Schema Annotation Element
modeling tools for schema languages: A.1.3. Intrusive Modeling Tools
modularity of W3C XML Schema: 2.3.1. W3C XML Schema Is Modular
vs. depth of schemas: 3.2.1. Depth Versus Modularity?
Murata, Makoto: 7.4.1.3. Unique Particle Attribution Rule
A.3.3. The RELAX NG Family
A.7. RELAX NG

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

n special character (new line): 6.4.1. Special Characters
name tokens, datatypes for: 4.3.3.1. Tokenss
named vs. anonymous datatypes: 7.2.2. Named Versus Anonymous Types
namespace attribute: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
namespaces: 10. Controlling Namespaces
accommodating attributes/elements from other: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
behavior of imported schemas: 10.8. Namespace Behavior of Imported Components
challenges presented to schema languages: 10.1. Namespaces Present Two Challenges to Schema Languages
declarations: 10.2. Namespace Declarations
default (see default namespaces)
importing schemas with no namespaces: 10.9. Importing Schemas with No Namespaces
including schemas using xs:include and xs:redefine: 10.10. Chameleon Design
prefixes
controversy with: 10.2. Namespace Declarations
declaring, in XPath expressions: 10.5. Namespaces and XPath Expressions
defining in schemas: 10.3. To Qualify Or Not to Qualify?
issues with: 10.1. Namespaces Present Two Challenges to Schema Languages
namespace declarations and: 10.2. Namespace Declarations
qualified/unqualified attributes: 10.3. To Qualify Or Not to Qualify?
XML 1.0 namespace: 10.7.1. XML Attributes
referencing other namespaces: 10.6. Referencing Other Namespaces
target (see target namespaces)
XML 1.0 namespace: 10.7.1. XML Attributes
XPath expressions and: 10.5. Namespaces and XPath Expressions
Namespaces in XML, qualified/unqualified attributes: 10.4. Disruptive Attributes
negating lists in user-defined character classes: 6.4.3.3. User-defined character classes
nil values, defining: 11.3. Defining Nil (Null) Values
nillable attribute: 11.3. Defining Nil (Null) Values
noncolonized names, datatypes for: 4.3.3.1. Tokenss
nondeterministic content models: 7.4.1.3. Unique Particle Attribution Rule
normalized strings: 4.2. Whitespace Processing
notations: 4.3.3.4. Notations
null values, defining: 11.3. Defining Nil (Null) Values
numeric datatypes: 4.4. Numeric Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
regular expressions and: 6.5.2. Numeric and Float Types

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

OASIS Technical Committee: A.3.3. The RELAX NG Family
object-oriented design and Russian doll design: 3.2.2. Russian Doll and Object-Oriented Design
object-oriented XML schema languages: A.2.3. Object-Oriented XML Schema Languages
open schemas, need for: 13.2. The Need for Open Schemas
or character (|): 6.4.4. Oring and Grouping
##other wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
10.11. Allowing Any Elements or Attributes from a Particular Namespace

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

Paoli, Jean: 7.4.1.3. Unique Particle Attribution Rule
parentheses ( ) group character: 6.4.4. Oring and Grouping
parsed spaces: 4.1. Lexical and Value Spaces
particles (element roles): 7.4.1.1. Compositors and particles
patterns: 6. Using Regular Expressions to Specify Simple Datatypes
(see also regular expressions)
atoms in (see atoms in patterns)
commonly used: 6.5. Common Patterns
constraining simple datatypes: 6.1. The Swiss Army Knife
quantifiers in: 6.3. Quantifying
simplest forms of: 6.2. The Simplest Possible Patterns
period (.) wildcard character: 6.4.2. Wildcard
Perl character classes: 6.4.3.1. Classical Perl character classes
polymorphic accessors: 11.2.1. Defining Simple Types
Post Schema Validation Infoset (see PSVI)
predefined datatypes: 4. Using Predefined Simple Datatypes
used in schemas: 4.8. Back to Our Library
prefixes, namespace (see namespaces, prefixes)
processContents attribute: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
associating schemas with instance documents: 11.1. Associating Schemas with Instance Documents
open schemas and: 13.2.2. Wildcards
processing instructions: 14.4. XML 1.0 Comments
PSVI (Post Schema Validation Infoset)
defining simple types: 11.2.1. Defining Simple Types
defining XLink attributes: 10.7.2. XLink Attributes
object-oriented XML schema languages and: A.2.3. Object-Oriented XML Schema Languages
serialization: B.3.1. PSVI Serialization

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

qualified names: 4.3.3.2. Qualified names
qualified vs. unqualified attributes: 10.3. To Qualify Or Not to Qualify?
quantifiers in patterns: 6.3. Quantifying
querying support for XML documents: 1.1.3. Querying Support

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

r special character (carriage return): 6.4.1. Special Characters
ranges in user-defined character classes, defining: 6.4.3.3. User-defined character classes
RDF (Resources Description Framework) specification
element types and: 3.2.3. Where Have the Element Types Gone?
W3C XML Schema's impact on: B.1.3. RDF
recursive schemas: 3.2.1. Depth Versus Modularity?
redefinition, schema inclusion with: 8.2. Schema Inclusion with Redefinition
8.2. Schema Inclusion with Redefinition
(see also xs:redefine element)
refer attribute (xs:keyref element): 10.5. Namespaces and XPath Expressions
regular expressions: 6. Using Regular Expressions to Specify Simple Datatypes
(see also patterns)
specifying simple datatypes with: 6. Using Regular Expressions to Specify Simple Datatypes
relative XPath expressions, defining constraints with: 9.2.1. Uniqueness
RELAX NG schema language: A.1.3. Intrusive Modeling Tools
A.3.3. The RELAX NG Family
sample application: A.7. RELAX NG
restrictions
applying new ones to datatypes: 5.1.2. Multiple Restrictions and Fixed Attribute
derivation by (see derivation methods, by restriction)
redefining attribute/element groups: 8.2.2.2. Restriction
of substitution groups: 12.1.4.2. Restricting substitution groups
reusable attributes/elements
creating: 7.4.1.2. Element and attribute groups
8. Creating Building Blocks
reusable schemas, creating: 13. Creating Extensible Schemas
rule-based XML schema languages: A.2.1. Rule-Based XML Schema Languages
Russian doll design
defining attributes using: 13.1.1.2. Attributes
defining elements using: 13.1.1.1. Elements
13.1.1.1. Elements
documenting schemas: 14.1.2. Think Globally
object orientation and: 3.2.2. Russian Doll and Object-Oriented Design

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

s character class: 6.4.3.1. Classical Perl character classes
SAF (Schema Adjunct Framework): 14.2. The W3C XML Schema Annotation Element
foreign attributes and: 14.3. Foreign Attributes
schema adornments (SAF): 14.2. The W3C XML Schema Annotation Element
schema-based XML-RPC, defining: 11.2.1. Defining Simple Types
schema extensions and error messages: B.3.3. Schema Extensions: Error Messages
Schema for Object-Oriented XML (SOX) proposal: A.3.2. The W3C XML Schema Family
schema languages (see XML schema languages)
schema validators
guessing element types: 11.2. Defining Element Types
locating schemas: 11.1. Associating Schemas with Instance Documents
schemaLocation attribute: 8.2. Schema Inclusion with Redefinition
schemas
adding constraints on datatypes: 5.5. Back to Our Library
associating, with instance documents: 11.1. Associating Schemas with Instance Documents
attributes (see attributes)
creating: 2.2. Our First Schema
documenting: 14. Documenting Schemas
elements (see elements)
extensible: 13. Creating Extensible Schemas
flat design style: 2.2. Our First Schema
2.3.3. Flat Design, Global Components
global components: 2.3.3. Flat Design, Global Components
importing, with no namespaces: 10.9. Importing Schemas with No Namespaces
inclusion: 8.1. Schema Inclusion
isolating simple/complex type definitions: 8.4. Simplifying the Library
using xs:include and xs:redefine: 10.10. Chameleon Design
with redefinition: 8.2. Schema Inclusion with Redefinition
lexical spaces: 4.1. Lexical and Value Spaces
locating: 11.1. Associating Schemas with Instance Documents
namespace behavior of imported components: 10.8. Namespace Behavior of Imported Components
namespace declarations: 10.2. Namespace Declarations
open: 13.2. The Need for Open Schemas
patterns used in: 6.6. Back to Our Library
qualified/unqualified attributes and elements: 10.3. To Qualify Or Not to Qualify?
referencing other namespaces: 10.6. Referencing Other Namespaces
regular expressions used in: 6.6. Back to Our Library
splitting components: 13.1.3. Splitting Schema Components
value spaces: 4.1. Lexical and Value Spaces
Schematron schema language: A.2.1. Rule-Based XML Schema Languages
A.3.4. Schematron
sample application: A.8. Schematron
xs:appinfo element and: 14.2. The W3C XML Schema Annotation Element
serialization, PSVI: B.3.1. PSVI Serialization
serialization spaces: 4.1. Lexical and Value Spaces
sets, manipulating: 6.4.3.3. User-defined character classes
Sharpe, Peter: 7.4.1.3. Unique Particle Attribution Rule
simple content models: 2.2. Our First Schema
creating: 7.3.1. Creation of Simple Content Models
empty content models as: 7.6.1.1. As simple content models
defining: 2.2. Our First Schema
derivation by extension: 7.3.2.1. Derivation by extension
derivation by restriction: 7.3.2.2. Derivation by restriction
simple datatypes: 2.2. Our First Schema
4. Using Predefined Simple Datatypes
complications with: 5.4. Some Oddities of Simple Types
controlling derivations: 12.2.4. Simple Types
creating: 5. Creating Simple Datatypes
defining: 2.2. Our First Schema
11.2.1. Defining Simple Types
globally: 13.1.1.2. Attributes
extensibility features of different styles: 13.1.1.2. Attributes
included schemas: 8.1. Schema Inclusion
example of: 8.4. Simplifying the Library
redefining: 8.2.1. Redefining of Simple and Complex Types
specifying, using regular expressions: 6. Using Regular Expressions to Specify Simple Datatypes
used in schemas: 4.8. Back to Our Library
vs. complex datatypes: 7.1. Simple Versus Complex Types
slash (\) escape character: 6.2. The Simplest Possible Patterns
special characters: 6.4.1. Special Characters
user-defined character classes: 6.4.3.3. User-defined character classes
SOX (Schema for Object-Oriented XML) proposal: A.3.2. The W3C XML Schema Family
spaces, replacing whitespace with: 4.2. Whitespace Processing
special characters (atoms): 6.4.1. Special Characters
splitting schema components: 13.1.3. Splitting Schema Components
St. Laurent, Simon: A.3.2. The W3C XML Schema Family
string datatypes: 4.3. String Datatypes
regular expressions and: 6.5.1. String Datatypes
strings
collapsed: 4.3.3. Collapsed Strings
limiting size of: 5.5. Back to Our Library
normalized: 4.2. Whitespace Processing
tokenized: 4.3.3.1. Tokenss
substitution groups: 12.1. Substitution Groups
controlling usage of derivations: 12.2. Controlling Derivations
defining content models using: 12.1.2.1. Using substitution groups
head elements of: 12.1.2.1. Using substitution groups
open schemas and: 13.2.3. And Substitution Groups?
restricting: 12.1.4.2. Restricting substitution groups
trees of: 12.1.2.3. Trees of substitution groups
vs. element groups: 12.1.3. Traditional Declarations or Substitution Groups?
substitutionGroup attribute: 12.1.2.1. Using substitution groups
12.1.3. Traditional Declarations or Substitution Groups?
SVG, allowed in schema annotations: 14.2. The W3C XML Schema Annotation Element
Swiss army knife, metaphor for regular expressions: 6.1. The Swiss Army Knife

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

t special character (tab): 6.4.1. Special Characters
target namespaces: 10.2. Namespace Declarations
defining prefixes in schemas for: 10.3. To Qualify Or Not to Qualify?
importing schemas: 10.8. Namespace Behavior of Imported Components
with no namespaces: 10.9. Importing Schemas with No Namespaces
qualified/unqualified attributes and elements: 10.3. To Qualify Or Not to Qualify?
xs:include and xs:redefine elements: 10.10. Chameleon Design
xsi:noNamespaceSchemaLocation attribute and: 11.1. Associating Schemas with Instance Documents
xsi:schemaLocation attribute and: 11.1. Associating Schemas with Instance Documents
targetNamespace attribute: 10.2. Namespace Declarations
##targetNamespace wildcard: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
10.11. Allowing Any Elements or Attributes from a Particular Namespace
text nodes: 2.2. Our First Schema
restricting length of: 7.3.2.2. Derivation by restriction
Thompson, Henry S.: B.3.1. PSVI Serialization
time datatypes: 4.5. Date and Time Datatypes
facets for: 5.1.1.4. Date and time datatypes
regular expressions and: 6.5.3. Datetimes
time zones
ISO 8601 standard and: 4.5.1. The Realm of ISO 8601
specifying using datatypes: 4.5.2. Datatypes
specifying using patterns: 6.5.3.1. Time zones
xs:enumeration facet and: 5.1.1.4.1.
Tobin, Richard: B.3.1. PSVI Serialization
tokenized strings: 4.3.3.1. Tokenss
tokens: 4.3.3.1. Tokenss
trees of substitution groups: 12.1.2.3. Trees of substitution groups
TREX (Tree Regular Expressions for XML) schema: A.3.3. The RELAX NG Family
type attribute, referencing global definitions: 7.2.2. Named Versus Anonymous Types
type substitutions and block attribute: 12.2.2.1. Block attribute

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

Unicode
blocks: 6.4.3.2. Unicode character classes
checking string datatypes: 6.5.1.1. Unicode blocks
categories: 6.4.3.2. Unicode character classes
character classes: 6.4.3.2. Unicode character classes
union
of character classes: 6.4.3.3. User-defined character classes
derivation by: 5.3. Derivation By Union
11.2.1. Defining Simple Types
Unique Particle Attribution Rule: 7.4.1.3. Unique Particle Attribution Rule
xsi:type as workaround against: 11.2.2. Defining Complex Types
uniqueness, defining tests for: 9.2.1. Uniqueness
unordered content models, limitations on: 7.4.1.5. Limitations on unordered content models
unparsed entities, datatypes for: 4.3.3.1. Tokenss
unqualified vs. qualified attributes: 10.3. To Qualify Or Not to Qualify?
URIs
absolute vs. relative: 11.1. Associating Schemas with Instance Documents
namespace attribute and: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
referencing schemas with target namespaces: 11.1. Associating Schemas with Instance Documents
targetNamespace attribute and: 10.2. Namespace Declarations
using schemas at original locations: 10.7.1. XML Attributes
xs:anyURI datatype: 4.3.3.3. URIs
user-defined character classes: 6.4.3.3. User-defined character classes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

validating XML documents: 1.1.1. Validation
2.3.2. W3C XML Schema Is Both About Structure and Datatyping
value spaces: 4.1. Lexical and Value Spaces
multiple facet definitions and: 5.1.2.1. Facet that can be changed but needs to be more restrictive
vocabularies
multi-namespace, limitations for: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
qualified vs. unqualified attributes: 10.3. To Qualify Or Not to Qualify?
10.4. Disruptive Attributes
referencing other namespaces: 10.6. Referencing Other Namespaces

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

w character class: 6.4.3.1. Classical Perl character classes
W3C (World Wide Web Consortium): 1.2. W3C XML Schema
W3C XML Schema: 1.2. W3C XML Schema
Consistent Declaration Rule: 7.4.1.4. Consistent Declaration Rule
datatypes reference guide: 16. Datatype Reference Guide
elements reference guide: 15. Elements Reference Guide
modularity of: 2.3.1. W3C XML Schema Is Modular
vs. depth of schemas: 3.2.1. Depth Versus Modularity?
namespace prefixes: 10.2. Namespace Declarations
pattern matching in: 6.2. The Simplest Possible Patterns
proposal: A.3.2. The W3C XML Schema Family
proposals submitted: A.3.2. The W3C XML Schema Family
time zone support in: 6.5.3.1. Time zones
Unique Particle Attribution Rule: 7.4.1.3. Unique Particle Attribution Rule
works in progress: B. Work in Progress
whitespace collapse: 4.2. Whitespace Processing
4.8. Back to Our Library
facets for: 5.1.1.1. Whitespace collapsed strings
whitespace processing
collapsed strings and: 4.3.3. Collapsed Strings
defining atomic datatypes that allow whitespace: 5.2.1. List Datatypes
exceptions in: 4.3. String Datatypes
lexical spaces and: 4.1. Lexical and Value Spaces
whitespace replacement: 4.2. Whitespace Processing
xs:string and xs:normalizedString types: 4.3.1. No Whitespace Replacement
wildcard character (.): 6.4.2. Wildcard
wildcards
accommodating attributes/elements from other namespaces: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
creating open schemas: 13.2.2. Wildcards
words, counting: 6.5.1.2. Counting words
World Wide Web Consortium (W3C): 1.2. W3C XML Schema

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

XDR (XML-Data-Reduced) proposal: A.3.2. The W3C XML Schema Family
XDuce programming language: A.3.3. The RELAX NG Family
Xerces Native Interface (XNI): B.3.2. APIs
XForms specification: 1.1.5. Guided Editing
XHTML, allowed in schema annotations: 14.2. The W3C XML Schema Annotation Element
XInclude specification
using instead of xs:include and xs:reference: 8.3.2. XInclude
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery
XLink specification
attributes: 10.7.2. XLink Attributes
foreign attributes accepted by: 14.3. Foreign Attributes
linking resources in schema annotations: 14.2. The W3C XML Schema Annotation Element
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery
xlink:href attribute: 10.7. Schemas for XML, XML Base and XLink
XML 1.0 comments: 14.4. XML 1.0 Comments
XML 1.0 namespace: 10.7.1. XML Attributes
XML attributes: 10.7.1. XML Attributes
XML Data Binding Resources: 1.1.4. Data Binding
XML-Data proposal: 1.2. W3C XML Schema
A.3.2. The W3C XML Schema Family
XML-Data-Reduced (XDR) proposal: A.3.2. The W3C XML Schema Family
XML documents
binding
data: 1.1.4. Data Binding
early vs. late: A.1.4. Early Binding Tools
guided editing: 1.1.5. Guided Editing
open schemas and: 13.2. The Need for Open Schemas
querying support for: 1.1.3. Querying Support
validating: 1.1.1. Validation
2.3.2. W3C XML Schema Is Both About Structure and Datatyping
XML schema languages: A.1. What Is a XML Schema Language?
challenges presented by namespaces to: 10.1. Namespaces Present Two Challenges to Schema Languages
classifying: A.2. Classification of XML Schema Languages
history of: A.3. A Short History of XML Schema Languages
modeling tools: A.1.3. Intrusive Modeling Tools
open vs. closed: A.1.2. Firewalls Against Diversity
sample applications: A.4. Sample Application
XML vocabularies, documenting: 1.1.2. Documentation
xml:base attribute: 10.7.1. XML Attributes
xml:lang attribute: 10.7.1. XML Attributes
xml:space attribute: 10.7.1. XML Attributes
XNI (Xerces Native Interface): B.3.2. APIs
XPath specification
embedded Schematron rules and: 14.2. The W3C XML Schema Annotation Element
identity checks: 9.2. XPath-Based Identity Checks
namespaces and: 10.5. Namespaces and XPath Expressions
permitted XPath expressions: 9.2.5. Permitted XPath Expressions
querying support and: 1.1.3. Querying Support
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery
XPointer specification: 8.3.2. XInclude
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery
XQuery specification: 1.1.3. Querying Support
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery
xs:all (outside a group): 15.1. Datatypes
adapting document structures to constraints of: 7.4.1.5.2. Adapting the structure of your document
limitations of: 7.4.1.5.1. Limitations of xs:all
role as compositor: 7.4.1.1. Compositors and particles
12.1.1. Using a "Traditional" Group
using xs:choice (outside a group) instead: 7.4.1.5.3. Using xs:choice instead of xs:all
xs:all (within a group): 15.1. Datatypes
xs:annotation element: 14.2. The W3C XML Schema Annotation Element
15.1. Datatypes
xs:any element: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
11.1. Associating Schemas with Instance Documents
15.1. Datatypes
xs:anyAttribute element: 10.11. Allowing Any Elements or Attributes from a Particular Namespace
15.1. Datatypes
xs:anyType type: 12.1.2.1. Using substitution groups
xs:anyURI type: 4.3.3.3. URIs
16.1. Datatypes
constraining strings with regular expressions: 6.5.1.3. URIs
xs:appinfo element: 13.2.2. Wildcards
15.1. Datatypes
schema adornments and: 14.2. The W3C XML Schema Annotation Element
source attribute and: 14.2. The W3C XML Schema Annotation Element
xs:annotation element and: 14.2. The W3C XML Schema Annotation Element
xs:attribute (global definition): 15.1. Datatypes
xs:attribute (reference or local definition): 15.1. Datatypes
xs:attributeGroup (global definition): 7.4.1.2. Element and attribute groups
15.1. Datatypes
xs:attributeGroup (reference): 15.1. Datatypes
xs:base64Binary type: 4.3.3.5. Binary string-encoded datatypes
16.1. Datatypes
xs:boolean type: 4.4.3.
16.1. Datatypes
facets for: 5.1.1.7. Booleans
xs:byte type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:choice (outside a group): 15.1. Datatypes
avoiding use of, when redefining groups: 8.2.2.1. Extension
complex content, deriving by extension: 7.4.2.1. Derivation by extension
compositors and particles: 7.4.1.1. Compositors and particles
creating, using substitution groups: 12.1. Substitution Groups
derivation by restriction: 12.1.4.2. Restricting substitution groups
extending, through group redefinitions: 12.1.4.1. Extension of xs:choice through group redefinitions
using instead of xs:all (outside a group): 7.4.1.5.3. Using xs:choice instead of xs:all
xs:choice (within a group): 15.1. Datatypes
xs:complexContent element: 15.1. Datatypes
xs:complexType (global definition): 3.2.1. Depth Versus Modularity?
15.1. Datatypes
creating
complex content models: 7.4.1. Creation of Complex Content
mixed content models: 7.5.1. Creating Mixed Content Models
vs. xs:simpleType (global definition): 5. Creating Simple Datatypes
xs:complexType (local definition): 15.1. Datatypes
xs:date type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:dateTime type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:decimal type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.6. Decimals
xs:documentation element: 15.1. Datatypes
source attribute and: 14.2. The W3C XML Schema Annotation Element
xs:annotation element and: 14.2. The W3C XML Schema Annotation Element
xs:double type: 4.4.2. Float Datatypes
16.1. Datatypes
facets for: 5.1.1.3. Float datatypes
xs:duration type: 4.5.2. Datatypes
16.1. Datatypes
defining datatypes with complete sort order: 6.5.3.1. Time zones
facets for: 5.1.1.4. Date and time datatypes
xs:element (global definition): 2.2. Our First Schema
15.1. Datatypes
compositors and particles: 7.4.1.1. Compositors and particles
creating complex contents: 7.4.1. Creation of Complex Content
xs:element (reference or local definition): 15.1. Datatypes
xs:element (within xs:all): 15.1. Datatypes
xs:ENTITIES type: 4.6. List Types
5.2.1. List Datatypes
16.1. Datatypes
facets for: 5.1.1.8. List datatypes
xs:ENTITY type: 4.3.3.1. Tokenss
16.1. Datatypes
xs:enumeration facet: 15.1. Datatypes
date and time datatypes: 5.1.1.4.1.
float datatypes: 5.1.1.3.1.
list datatypes and: 5.2.1. List Datatypes
patterns and: 6.2. The Simplest Possible Patterns
union datatype and: 5.3. Derivation By Union
whitespace-collapsed strings: 5.1.1.1.1.
xs:extension (complex content): 15.1. Datatypes
xs:extension (simple content): 15.1. Datatypes
xs:field element: 15.1. Datatypes
permitted XPath expressions: 9.2.5. Permitted XPath Expressions
xs:float type: 4.4.2. Float Datatypes
16.1. Datatypes
facets for: 5.1.1.3. Float datatypes
xs:fractionDigits facet: 5.1.1.6.1.
15.1. Datatypes
fixing maximum number of digits: 6.5.2.2. Fixed format
xs:gDay type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:gMonth type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:gMonthDay type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:group (definition): 3.2.1. Depth Versus Modularity?
15.1. Datatypes
xs:group (reference): 15.1. Datatypes
xs:gYear type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:gYearMonth type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:hexBinary type: 4.3.3.5. Binary string-encoded datatypes
16.1. Datatypes
xs:ID type: 4.3.3.1. Tokenss
9.1. and
16.1. Datatypes
vs. xs:key/xs:keyref elements: 9.3. ID/IDREF Versus /
XPath-based identity checks for: 9.2. XPath-Based Identity Checks
xs:IDREF type: 4.3.3.1. Tokenss
9.1. and
16.1. Datatypes
vs. xs:key/xs:keyref elements: 9.3. ID/IDREF Versus /
XPath-based identity checks for: 9.2. XPath-Based Identity Checks
xs:IDREFS type: 4.6. List Types
5.2.1. List Datatypes
9.1. and
16.1. Datatypes
facets for: 5.1.1.8. List datatypes
xs:import element: 10.6. Referencing Other Namespaces
15.1. Datatypes
namespace behavior of imported components: 10.8. Namespace Behavior of Imported Components
splitting schema components: 13.1.3. Splitting Schema Components
xs:include element: 15.1. Datatypes
alternatives to: 8.3. Other Alternatives
schema inclusions and: 8.1. Schema Inclusion
splitting schema components: 13.1.3. Splitting Schema Components
target namespaces and: 10.10. Chameleon Design
xsi:nil attribute: 11.3. Defining Nil (Null) Values
xsi:noNamespaceSchemaLocation attribute: 11.1. Associating Schemas with Instance Documents
xs:int type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:integer type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xsi:schemaLocation attribute: 11.1. Associating Schemas with Instance Documents
xsi:type attribute: 11.2. Defining Element Types
block attribute and: 12.2.2.1. Block attribute
creating open schemas: 13.2.1. xsi:type
xs:key/xs:keyref elements: 9.2.3. Keys
15.1. Datatypes
as co-occurrence constraints: 9.4. Using and As Co-occurrence Constraints
defining prefixes for namespaces: 10.5. Namespaces and XPath Expressions
vs. xs:ID/xs:IDREF types: 9.3. ID/IDREF Versus /
xs:language type: 4.3.3.1. Tokenss
16.1. Datatypes
xs:length facet: 5.1.1.1.2.
15.1. Datatypes
adding constraints on datatypes: 5.5. Back to Our Library
list datatypes and: 5.2.1. List Datatypes
redefining not allowed: 5.1.2.2. Facet that cannot be changed
xs:list element: 5.2.1. List Datatypes
15.1. Datatypes
xs:long type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
XSLT specification
embedding code snippets in schema annotations: 14.2. The W3C XML Schema Annotation Element
querying support and: 1.1.3. Querying Support
W3C XML Schema's impact on: B.1.1. XPath, XSLT, and XQuery
xs:maxExclusive facet: 15.1. Datatypes
date and time datatypes: 5.1.1.4.2.
float datatypes: 5.1.1.3.2.
xs:maxInclusive facet: 15.1. Datatypes
date and time datatypes: 5.1.1.4.3.
float datatypes: 5.1.1.3.3.
xs:maxLength facet: 5.1.1.1.3.
15.1. Datatypes
changing behavior of facets: 5.4.2. Using or Abusing Lists to Change the Behavior of Length Constraining Facets
list datatypes and: 5.2.1. List Datatypes
xs:minExclusive facet: 15.1. Datatypes
date and time datatypes: 5.1.1.4.4.
float datatypes: 5.1.1.3.4.
xs:minInclusive facet: 15.1. Datatypes
date and time datatypes: 5.1.1.4.5.
float datatypes: 5.1.1.3.5.
xs:minLength facet: 5.1.1.1.4.
15.1. Datatypes
changing behavior of facets: 5.4.2. Using or Abusing Lists to Change the Behavior of Length Constraining Facets
list datatypes and: 5.2.1. List Datatypes
xs:Name type: 4.3.3.1. Tokenss
16.1. Datatypes
xs:NCName type: 4.3.3.1. Tokenss
9.1. and
16.1. Datatypes
xs:negativeInteger type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:NMTOKEN type: 4.3.3.1. Tokenss
16.1. Datatypes
xs:NMTOKENS type: 4.6. List Types
5.2.1. List Datatypes
16.1. Datatypes
facets for: 5.1.1.8. List datatypes
xs:nonNegativeInteger type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:nonPositiveInteger type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:normalizedString type: 4.2. Whitespace Processing
16.1. Datatypes
facets for: 5.1.1.2. Other strings
xs:notation element: 15.1. Datatypes
xs:NOTATION type: 4.3.3.4. Notations
16.1. Datatypes
xs:enumeration facet and: 5.1.1.1.1.
xs:pattern facet: 6.2. The Simplest Possible Patterns
15.1. Datatypes
booleans and: 5.1.1.7.1.
character classes and: 6.4.3. Character Classes
date and time datatypes: 5.1.1.4.6.
float datatypes: 5.1.1.3.6.
performing intersection of lexical spaces: 5.1.2.3. Facet that performs the intersection of the lexical spaces
quantifiers and: 6.3. Quantifying
union datatype and: 5.3. Derivation By Union
whitespace-collapsed strings: 5.1.1.1.5.
wildcard character and: 6.4.2. Wildcard
xs:positiveInteger type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:QName type: 4.3.3.2. Qualified names
16.1. Datatypes
xs:redefine element: 15.1. Datatypes
alternatives to: 8.3. Other Alternatives
attribute/element groups
by extension: 8.2.2.1. Extension
by restriction: 8.2.2.2. Restriction
extending xs:choice (outside a group): 12.1.4.1. Extension of xs:choice through group redefinitions
simple/complex types: 8.2.1. Redefining of Simple and Complex Types
splitting schema components: 13.1.3. Splitting Schema Components
target namespaces and: 10.10. Chameleon Design
xs:restriction (complex content): 15.1. Datatypes
xs:restriction (simple content): 15.1. Datatypes
xs:restriction (simple type): 5.1. Derivation By Restriction
15.1. Datatypes
xs:schema element: 15.1. Datatypes
blockDefault attribute of: 12.2.2.1. Block attribute
blocking complex types: 12.2.3.1. Blocking complex types
global components: 2.3.3. Flat Design, Global Components
namespace declarations and: 10.2. Namespace Declarations
xs:selector element: 15.1. Datatypes
defining, using ID/IDREF types: 9.3. ID/IDREF Versus /
permitted XPath expressions: 9.2.5. Permitted XPath Expressions
xs:sequence (outside a group): 15.1. Datatypes
compositors and particles: 7.4.1.1. Compositors and particles
creating complex content: 7.4.1. Creation of Complex Content
xs:sequence (within a group): 15.1. Datatypes
xs:short type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:simpleContent element: 15.1. Datatypes
xs:simpleType (global definition): 5. Creating Simple Datatypes
12.2.4. Simple Types
15.1. Datatypes
defining list datatypes: 5.2.1. List Datatypes
xs:simpleType (local definition): 15.1. Datatypes
xs:string type: 4.2. Whitespace Processing
16.1. Datatypes
facets for: 5.1.1.2. Other strings
xs:time type: 4.5.2. Datatypes
16.1. Datatypes
facets for: 5.1.1.4. Date and time datatypes
xs:token type: 4.3.3.1. Tokenss
16.1. Datatypes
xs:totalDigits facet: 5.1.1.5.1.
15.1. Datatypes
adding constraints on datatypes: 6.6. Back to Our Library
fixing maximum number of digits: 6.5.2.2. Fixed format
xs:union element: 5.3. Derivation By Union
15.1. Datatypes
xs:unique element: 15.1. Datatypes
as co-occurrence constraint: 9.4. Using and As Co-occurrence Constraints
defining prefixes for namespaces: 10.5. Namespaces and XPath Expressions
key references and: 9.2.4. Key References
xs:unsignedByte type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:unsignedInt type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:unsignedLong type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:unsignedShort type: 4.4.1. Decimal Types
16.1. Datatypes
facets for: 5.1.1.5. Integer and derived datatypes
xs:whiteSpace facet: 5.1.1.2.1.
15.1. Datatypes
list datatypes and: 5.2.1. List Datatypes
multiple facet definitions and: 5.1.2.4. Facet that does its job before the lexical space

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Z

zeros, removing: 6.5.2.1. Leading zeros

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Colophon (XML Schema)

 XML SchemaSearch this book
Previous

Colophon

Our look is the result of reader comments, our own experimentation, and feedback from distribution channels. Distinctive covers complement our distinctive approach to technical topics, breathing personality and life into potentially dry subjects.

The animal on the cover of XML Schema is a Reeves's pheasant. Reeves's pheasants originated in north and central China. They are named after the man who first introduced the species in England to be bred for game. The species is one of the most popular types of pheasant to be held in captivity and can be found all over the world.

Reeves's pheasants are black, white, and bronze in color. Male pheasants have white heads with black stripes across their eyes resembling masks, while females have gold heads with light brown masks. Most remarkable are the birds' tail feathers, which can reach six feet long. These long tail feathers have historically been used in ceremonies.

Hens produce two or more clutches of 8 to 14 eggs per season. The eggs are olive brown or cream color, and hatch after 24 days. They have to be separated from other chicks, especially of different species, soon after birth, as they tend to be quite aggressive. Dominant male Reeves's pheasants are also quite aggressive and need to be separated when held in captivity.

Darren Kelly was the production editor, Mary Brady was the copyeditor, and Tatiana Diaz was the proofreader for XML Schema. Mary Anne Weeks Mayo and Claire Cloutier provided quality control. Interior composition was done by Philip Dangler, Matt Hutchinson, and Emily Quill. Judy Hoer wrote the index.

Hanna Dyer designed the cover of this book, based on a series design by Edie Freedman. The cover image is a 19th-century engraving from the Dover Pictorial Archive. Emma Colby produced the cover layout with QuarkXPress 4.1 using Adobe's ITC Garamond font.

David Futato designed the interior layout. This book was converted to FrameMaker 5.5.6 with a format conversion tool created by Erik Ray, Jason McIntosh, Neil Walls, and Mike Sierra that uses Perl and XML technologies. The text font is Linotype Birka; the heading font is Adobe Myriad Condensed; and the code font is LucasFont's TheSans Mono Condensed. The illustrations that appear in the book were produced by Robert Romano and Jessamyn Read using Macromedia FreeHand 9 and Adobe Photoshop 6. The tip and warning icons were drawn by Christopher Bing. This colophon was written by Linley Dolby.

Previous | Home
Index | Book Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML in a Nutshell, 2nd Edition

by Elliotte Rusty Harold and W. Scott Means
ISBN 0-596-00292-0
Second Edition, published June 2002.
(See the catalog page for this book.)

Table of Contents

Copyright Page
Preface

Part I: XML Concepts

Chapter 1: Introducing XML
Chapter 2: XML Fundamentals
Chapter 3: Document Type Definitions (DTDs)
Chapter 4: Namespaces
Chapter 5: Internationalization

Part II: Narrative-Centric Documents

Chapter 6: XML as a Document Format
Chapter 7: XML on the Web
Chapter 8: XSL Transformations (XSLT)
Chapter 9: XPath
Chapter 10: XLinks
Chapter 11: XPointers
Chapter 12: Cascading Style Sheets (CSS)
Chapter 13: XSL Formatting Objects (XSL-FO)
Chapter 14: Resource Directory Description Language (RDDL)

Part III: Data-Centric XML

Chapter 15: XML as a Data Format
Chapter 16: XML Schemas
Chapter 17: Programming Models
Chapter 18: Document Object Model (DOM)
Chapter 19: Simple API for XML (SAX)

Part IV: Reference

Chapter 20: XML 1.0 Reference
Chapter 21: Schemas Reference
Chapter 22: XPath Reference
Chapter 23: XSLT Reference
Chapter 24: DOM Reference
Chapter 25: SAX Reference
Chapter 26: Character Sets

Index
Colophon
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Copyright (XML in a Nutshell, 2nd Edition)

 XML in a Nutshell, 2nd EditionSearch this book

Copyright © 2002, 2001 O'Reilly & Associates, Inc. All rights reserved.

Printed in the United States of America.

Published by O'Reilly & Associates, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safari.oreilly.com). For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are registered trademarks of O'Reilly & Associates, Inc. Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc., in the United States and other countries. O'Reilly & Associates, Inc. is independent of Sun Microsystems. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps. The association between the image of a peafowl and the topic of XML is a trademark of O'Reilly & Associates, Inc.

While every precaution has been taken in the preparation of this book, the publisher and the authors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Previous | Home | Next
Table of Contents | Book Index | 0. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Preface (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Preface

XML is one of the most important developments in document syntax in the history of computing. In the last few years it has been adopted in fields as diverse as law, aeronautics, finance, insurance, robotics, multimedia, hospitality, travel, art, construction, telecommunications, software, agriculture, physics, journalism, theology, retail, and comics. XML has become the syntax of choice for newly designed document formats across almost all computer applications. It's used on Linux, Windows, Macintosh, and many other computer platforms. Mainframes on Wall Street trade stocks with one another by exchanging XML documents. Children playing games on their home PCs save their documents in XML. Sports fans receive real-time game scores on their cell phones in XML. XML is simply the most robust, reliable, and flexible document syntax ever invented.

XML in a Nutshell is a comprehensive guide to the rapidly growing world of XML. It covers all aspects of XML, from the most basic syntax rules, to the details of DTD and schema creation, to the APIs you can use to read and write XML documents in a variety of programming languages.

0.1. What This Book Covers

There are hundreds of formally established XML applications from the W3C and other standards bodies, such as OASIS and the Object Management Group. There are even more informal, unstandardized applications from individuals and corporations, such as Microsoft's Channel Definition Format and John Guajardo's Mind Reading Markup Language. This book cannot cover them all, any more than a book on Java could discuss every program that has ever been or might ever be written in Java. This book focuses primarily on XML itself. It covers the fundamental rules that all XML documents and authors must adhere to, whether a web designer uses SMIL to add animations to web pages or a C++ programmer uses SOAP to exchange serialized objects with a remote database.

This book also covers generic supporting technologies that have been layered on top of XML and are used across a wide range of XML applications. These technologies include:

XLink
An attribute-based syntax for hyperlinks between XML and non-XML documents that provide the simple, one-directional links familiar from HTML, multidirectional links between many documents, and links between documents to which you don't have write access.

XSLT
An XML application that describes transformations from one document to another, in either the same or different XML vocabularies.

XPointer
A syntax for URI fragment identifiers that selects particular parts of the XML document referred to by the URI--often used in conjunction with an XLink.

XPath
A non-XML syntax used by both XPointer and XSLT for identifying particular pieces of XML documents. For example, an XPath can locate the third address element in the document, or all elements with an email attribute whose value is elharo@metalab.unc.edu.

Namespaces
A means of distinguishing between elements and attributes from different XML vocabularies that have the same name; for instance, the title of a book and the title of a web page in a web page about books.

Schemas
An XML vocabulary for describing the permissible contents of XML documents from other XML vocabularies.

SAX
The Simple API for XML, an event-based application programming interface implemented by many XML parsers.

DOM
The Document Object Model, a language-neutral tree-oriented API that treats an XML document as a set of nested objects with various properties.

XHTML
An XMLized version of HTML that can be extended with other XML applications such as MathML and SVG.

RDDL
The Resource Directory Description Language, an XML application based on XHTML for documents placed at the end of namespace URLs.

All these technologies, whether defined in XML (XLinks, XSLT, Namespaces, Schemas, XHTML, and RDDL) or in another syntax (XPointers, XPath, SAX, and DOM), are used in many different XML applications.

This book does not specifically cover XML applications that are relevant to only some users of XML, such as:

SVG
Scalable Vector Graphics, a W3C-endorsed standard XML encoding of line art.

MathML
The Mathematical Markup Language, a W3C-endorsed standard XML application used for embedding equations in web pages and other documents.

RDF
The Resource Description Framework, a W3C-standard XML application used for describing resources, with a particular focus on the sort of metadata one might find in a library card catalog.

Occasionally we use one or more of these applications in an example, but we do not cover all aspects of the relevant vocabulary in depth. While interesting and important, these applications (and hundreds more like them) are intended primarily for use with special software that knows their format intimately. For instance, most graphic designers do not work directly with SVG. Instead, they use their customary tools, such as Adobe Illustrator, to create SVG documents. They may not even know they're using XML.

This book focuses on standards that are relevant to almost all developers working with XML. We investigate XML technologies that span a wide range of XML applications, not those that are relevant only within a few restricted domains.

Previous | Home | Next
Copyright | Book Index | 0.2. What's New in the Second Edition
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


What's New in the Second Edition (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

0.2. What's New in the Second Edition

XML has hardly stood still in the 18 months since the first edition of XML in a Nutshell was published. To answer the most frequent request from readers of the first edition, there are now two new chapters covering schemas. Furthermore, other chapters throughout the book have been rewritten to reflect the impact of schemas on their subject matter. We added several other new topics as well, including the RDDL, the Transformations API for XML (TrAX), the Java API for XML Processing (JAXP), and SAX filters.

In addition, the treatment of many topics has been upgraded to the latest versions of various specifications, including:

  • XSL Formatting Objects 1.0

  • XLink 1.0

  • XPointer 2nd Candidate Recommendation

  • XHTML 1.1

  • Unicode 3.1.1

Finally, many small errors and omissions were corrected throughout the book.

Previous | Home | Next
0.1. Preface | Book Index | 0.3. Organization of the Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Organization of the Book (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

0.3. Organization of the Book

Part I, introduces you to the fundamental standards that form the essential core of XML to which all XML applications and software must adhere. It teaches you about well-formed XML, DTDs, namespaces, and Unicode as quickly as possible.

Part II, explores technologies that are used mostly for narrative XML documents, such as web pages, books, articles, diaries, and plays. You'll learn about XSLT, CSS, XSL-FO, XLinks, XPointers, XPath, and RDDL.

One of the most unexpected developments in XML was its enthusiastic adoption for data-heavy structured documents such as spreadsheets, financial statistics, mathematical tables, and software file formats. Part III, explores the use of XML for such record-like documents. This part focuses on the tools and APIs needed to write software that processes XML, including SAX, DOM, and schemas.

Finally, Part IV, is a series of quick-reference chapters that form the core of any Nutshell Handbook. These chapters give you detailed syntax rules for the core XML technologies, including XML, DTDs, schemas, XPath, XSLT, SAX, and DOM. Turn to this section when you need to find out the precise syntax quickly for something you know you can do but don't remember exactly how to do.

Previous | Home | Next
0.2. What's New in the Second Edition | Book Index | 0.4. Conventions Used in This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Conventions Used in This Book (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

0.4. Conventions Used in This Book

Constant width is used for:

  • Code examples and fragments.

  • Anything that might appear in an XML document, including element names, tags, attribute values, entity references, and processing instructions.

  • Anything that might appear in a program, including keywords, operators, method names, class names, and literals.

Constant-width bold is used for:

  • User input.

  • Signifying emphasis in code examples and fragments.

Constant-width italic is used for:

  • Replaceable elements in code statements.

Italic is used for:

  • New terms where they are defined.

  • Signifying emphasis in body text.

  • Pathnames, filenames, and program names. (However, if the program name is also the name of a Java class, it is written in constant-width font, like other class names.)

  • Host and domain names (cafeconleche.org).

TIP: This icon indicates a tip, suggestion, or general note.

WARNING: This icon indicates a warning or caution.

Significant code fragments, complete programs, and documents are generally placed into a separate paragraph like this:

<?xml version="1.0"?>
<?xml-stylesheet href="person.css" type="text/css"?>
<person>
  Alan Turing
</person>

XML is case sensitive. The PERSON element is not the same thing as the person or Person element. Case-sensitive languages do not always allow authors to adhere to standard English grammar. It is usually possible to rewrite the sentence so the two do not conflict, and when possible we have endeavored to do so. However, on rare occasions when there is simply no way around the problem, we let standard English come up the loser.

Finally, although most of the examples used here are toy examples unlikely to be reused, a few have real value. Please feel free to reuse them or any parts of them in your own code. No special permission is required. As far as we are concerned, they are in the public domain (though the same is definitely not true of the explanatory text).

Previous | Home | Next
0.3. Organization of the Book | Book Index | 0.5. Request for Comments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Request for Comments (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

0.5. Request for Comments

We enjoy hearing from readers with general comments about how this book could be better, specific corrections, or topics you would like to see covered. You can reach the authors by sending email to elharo@metalab.unc.edu and smeans@enterprisewebmachines.com. Please realize, however, that we each receive several hundred pieces of email a day and cannot respond to everyone personally. For the best chance of getting a personal response, please identify yourself as a reader of this book. And please send the message from the account you want us to reply to and make sure that your reply-to address is properly set. There's nothing so frustrating as spending an hour or more carefully researching the answer to an interesting question and composing a detailed response, only to have it bounce because the correspondent sent the message from a public terminal and neglected to set the browser preferences to include his actual email address.

The information in this book has been tested and verified, but you may find that features have changed (or you may even find mistakes). We believe the old saying, "If you like this book, tell your friends. If you don't like it, tell us." We're especially interested in hearing about mistakes. As hard as the authors and editors worked on this book, inevitably there are a few mistakes and typographical errors that slipped by us. If you find a mistake or a typo, please let us know so we can correct it in a future printing. Please send any errors you find directly to the authors at the previously listed email addresses.

You can also address comments and questions concerning this book to the publisher:

O'Reilly & Associates, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
(800) 998-9938 (in the United States or Canada)
(707) 829-0515 (international or local)
(707) 829-0104 (fax)

We have a web site for the book, where we list errata, examples, and any additional information. You can access this site at:

http://www.cafeconleche.org/books/xian2/

Before reporting errors, please check this web site to see if we have already posted a fix. To ask technical questions or comment on the book, you can send email to the authors directly or send your questions to the publisher at:

bookquestions@oreilly.com

For more information about other O'Reilly books, conferences, software, Resource Centers, and the O'Reilly Network, see the web sites at:

http://www.oreilly.com

http://xml.oreilly.com

http://www.xml.com

Previous | Home | Next
0.4. Conventions Used in This Book | Book Index | 0.6. Acknowledgments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Acknowledgments (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

0.6. Acknowledgments

Many people were involved in the production of this book. The original editor, John Posner, got this book rolling and provided many helpful comments that substantially improved the book. When John moved on, Laurie Petrycki shepherded this book to its completion. The eagle-eyed Jeni Tennison read the entire manuscript from start to finish and caught many errors large and small. Without her attention, this book would not be nearly as accurate. Stephen Spainhour deserves special thanks for his work on the reference section. His efforts in organizing and reviewing material helped create a better book. We'd like to thank Matt Sergeant and Didier P. H. Martin for their thorough technical review of the manuscript and thoughtful suggestions. James Kass's Code2000 font was invaluable in producing Chapter 26.

We'd also like to thank everyone who has worked so hard to make XML such a success over the last few years and thereby given us something to write about. There are so many of these people that we can only list a few. In alphabetical order we'd like to thank Tim Berners-Lee, Jonathan Borden, Jon Bosak, Tim Bray, David Brownell, Mike Champion, James Clark, Charles Goldfarb, Jason Hunter, Arnaud Le Hors, Michael Kay, Keiron Liddle, Murato Makoto, Eve Maler, Brett McLaughlin, David Megginson, David Orchard, Walter E. Perry, Simon St.Laurent, C. M. Sperberg-McQueen, Jonathan Robie, Arved Sandstrom, James Tauber, Henry S. Thompson, B. Tommie Usdin, Daniel Veillard, Norm Walsh, Lauren Wood, and Mark Wutka. Our apologies to everyone we unintentionally omitted.

Elliotte would like to thank his agent, David Rogelberg, who convinced him that it was possible to make a living writing books like this rather than working in an office. The entire Sunsite crew (now ibiblio.org) has also helped him to communicate better with his readers in a variety of ways over the last several years. All these people deserve much thanks and credit. Finally, as always, he offers his largest thanks to his wife, Beth, without whose love and support this book would never have happened.

Scott would most like to thank his lovely wife, Celia, who has already spent way too much time as a "computer widow." He would also like to thank his daughter Selene for understanding why Daddy can't play with her when he's "working" and Skyler for just being himself. Also, he'd like to thank the team at Enterprise Web Machines for helping him make time to write. Finally, he would like to thank John Posner for getting him into this and Laurie Petrycki for working with him when things got tough.

-- Elliotte Rusty Harold, elharo@metalab.unc.edu

-- W. Scott Means, smeans@enterprisewebmachines.com

Previous | Home | Next
0.5. Request for Comments | Book Index | I. XML Concepts
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Concepts (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Part I. XML Concepts

Previous | Home | Next
0.6. Acknowledgments | Book Index | 1. Introducing XML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Introducing XML (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 1. Introducing XML

Contents:

The Benefits of XML
Portable Data
How XML Works
The Evolution of XML

XML, the Extensible Markup Language, is a W3C-endorsed standard for document markup. It defines a generic syntax used to mark up data with simple, human-readable tags. It provides a standard format for computer documents. This format is flexible enough to be customized for domains as diverse as web sites, electronic data interchange, vector graphics, genealogy, real-estate listings, object serialization, remote procedure calls, voice-mail systems, and more.

You can write your own programs that interact with, massage, and manipulate the data in XML documents. If you do, you'll have access to a wide range of free libraries in a variety of languages that can read and write XML so that you can focus on the unique needs of your program. Or you can use off-the-shelf software, such as web browsers and text editors, to work with XML documents. Some tools are able to work with any XML document. Others are customized to support a particular XML application in a particular domain, such as vector graphics, and may not be of much use outside that domain. But in all cases, the same underlying syntax is used, even if it's deliberately hidden by the more user-friendly tools or restricted to a single application.

1.1. The Benefits of XML

XML is a metamarkup language for text documents. Data is included in XML documents as strings of text. The data is surrounded by text markup that describes the data. XML's basic unit of data and markup is called an element. The XML specification defines the exact syntax this markup must follow: how elements are delimited by tags, what a tag looks like, what names are acceptable for elements, where attributes are placed, and so forth. Superficially, the markup in an XML document looks a lot like the markup in an HTML document, but there are some crucial differences.

Most importantly, XML is a metamarkup language. That means it doesn't have a fixed set of tags and elements that are supposed to work for everybody in all areas of interest for all time. Any attempt to create a finite set of such tags is doomed to failure. Instead, XML allows developers and writers to define the elements they need as they need them. Chemists can use elements that describe molecules, atoms, bonds, reactions, and other items encountered in chemistry. Real-estate agents can use elements that describe apartments, rents, commissions, locations, and other items needed for real estate. Musicians can use elements that describe quarter notes, half notes, G-clefs, lyrics, and other objects common in music. The X in XML stands for Extensible. Extensible means that the language can be extended and adapted to meet many different needs.

Although XML is quite flexible in the elements it allows to be defined, it is quite strict in many other respects. It provides a grammar for XML documents that says where tags may be placed, what they must look like, which element names are legal, how attributes are attached to elements, and so forth. This grammar is specific enough to allow the development of XML parsers that can read any XML document. Documents that satisfy this grammar are said to be well-formed. Documents that are not well-formed are not allowed, any more than a C program that contains a syntax error is allowed. XML processors will reject documents that contain well-formedness errors.

For reasons of interoperability, individuals or organizations may agree to use only certain tags. These tag sets are called XML applications. An XML application is not a software application that uses XML, such as Mozilla or Microsoft Word. Rather, it's an application of XML in a particular domain like vector graphics or cooking.

The markup in an XML document describes the structure of the document. It lets you see which elements are associated with which other elements. In a well-designed XML document, the markup also describes the document's semantics. For instance, the markup can indicate that an element is a date or a person or a bar code. In well-designed XML applications, the markup says nothing about how the document should be displayed. That is, it does not say that an element is bold or italicized or a list item. XML is a structural and semantic markup language, not a presentation language.[1]

[1]A few XML applications, such as XSL Formatting Objects, are designed to describe the presentation of text. However, these are exceptions that prove the rule. Although XSL-FO does describe presentation, you'd never write an XSL-FO document directly. Instead, you'd write a more semantically structured XML document, then use an XSL Transformations stylesheet to change the structure-oriented XML into presentation-oriented XML.

The markup permitted in a particular XML application can be documented in a schema. Particular document instances can be compared to the schema. Documents that match the schema are said to be valid. Documents that do not match are invalid. Validity depends on the schema. That is, whether a document is valid or invalid depends on which schema you compare it to. Not all documents need to be valid. For many purposes it is enough that the document merely be well-formed.

There are many different XML schema languages, with different levels of expressivity. The most broadly supported schema language and the only one defined by the XML 1.0 specification itself is the document type definition (DTD). A DTD lists all the legal markup and specifies where and how it may be included in a document. DTDs are optional in XML. On the other hand, DTDs may not always be enough. The DTD syntax is quite limited and does not allow you to make many useful statements such as "This element contains a number" or "This string of text is a date between 1974 and 2032." The W3C XML Schema Language (which sometimes goes by the misleadingly generic label schemas) does allow you to express constraints of this nature. Besides these two, there are many other schema languages from which to choose, including RELAX NG, Schematron, Hook, and Examplotron, and this is hardly an exhaustive list.

All current schema languages are purely declarative. However, there are always some constraints that cannot be expressed in anything less than a Turing complete programming language. For example, given an XML document that represents an order, a Turing complete language is required to multiply the price of each order_item by its quantity, sum them all up, and verify that the sum equals the value of the subtotal element. Today's schema languages are also incapable of verifying extra-document constraints such as "Every SKU element matches the SKU field of a record in the products table of the inventory database." If you're writing programs to read XML documents, you can add code to verify statements like these, just as you would if you were writing code to read a tab-delimited text file. The difference is that XML parsers present you with the data in a much more convenient format and do more of the work for you before you have to resort to your own custom code.

1.1.1. What XML Is Not

XML is a markup language, and it is only a markup language. It's important to remember that. The XML hype has gotten so extreme that some people expect XML to do everything up to and including washing the family dog.

First of all, XML is not a programming language. There's no such thing as an XML compiler that reads XML files and produces executable code. You might perhaps define a scripting language that used a native XML format and was interpreted by a binary program, but even this application would be unusual.[2] XML can be used as a format for instructions to programs that do make things happen, just like a traditional program may read a text config file and take different action depending on what it sees there. Indeed, there's no reason a config file can't be XML instead of unstructured text. Some more recent programs are beginning to use XML config files; but in all cases it's the program taking action, not the XML document itself. An XML document by itself simply is. It does not do anything.

[2]At least one XML application, XSL Transformations, has been proven to be Turing complete by construction. See http://www.unidex.com/turing/utm.htm for one universal Turing machine written in XSLT.

Secondly, XML is not a network transport protocol. XML won't send data across the network, any more than HTML will. Data sent across the network using HTTP, FTP, NFS, or some other protocol might happen to be encoded in an XML format, but again there has to be some software outside the XML document that actually does the sending.

Finally, to mention the example where the hype most often obscures the reality, XML is not a database. You're not going to replace an Oracle or MySQL server with XML. A database can contain XML data, either as a VARCHAR or a BLOB or as some custom XML data type, but the database itself is not an XML document. You can store XML data into a database on a server or retrieve data from a database in an XML format, but to do this, you need to be running software written in a real programming language such as C or Java. To store XML in a database, software on the client side will send the XML data to the server using an established network protocol such as TCP/IP. Software on the server side will receive the XML data, parse it, and store it in the database. To retrieve an XML document from a database, you'll generally pass through some middleware product like Enhydra that makes SQL queries against the database and formats the result set as XML before returning it to the client. Indeed, some databases may integrate this software code into their core server or provide plug-ins to do it such as the Oracle XSQL servlet. XML serves very well as a ubiquitous, platform-independent transport format in these scenarios. However, it is not the database, and it shouldn't be used as one.

Previous | Home | Next
I. XML Concepts | Book Index | 1.2. Portable Data
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Portable Data (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

1.2. Portable Data

XML offers the tantalizing possibility of truly cross-platform, long-term data formats. It's long been the case that a document written on one platform is not necessarily readable on a different platform, or by a different program on the same platform, or even by a future or past version of the same program on the same platform. When the document can be read, there's no guarantee that all the information will come across. Much of the data from the original moon landings in the late 1960s and early 1970s is now effectively lost. Even if you can find a tape drive that can read the now obsolete tapes, nobody knows in what format the data is stored on the tapes!

XML is an incredibly simple, well-documented, straightforward data format. XML documents are text and can be read with any tool that can read a text file. Not just the data, but also the markup is text, and it's present right there in the XML file as tags. You don't have to wonder whether every eighth byte is random padding, guess whether a four-byte quantity is a two's complement integer or an IEEE 754 floating point number, or try to decipher which integer codes map to which formatting properties. You can read the tag names directly to find out exactly what's in the document. Similarly, since element boundaries are defined by tags, you aren't likely to be tripped up by unexpected line-ending conventions or the number of spaces that are mapped to a tab. All the important details about the structure of the document are explicit. You don't have to reverse-engineer the format or rely on incomplete and often unavailable documentation.

A few software vendors may want to lock in their users with undocumented, proprietary, binary file formats. However, in the long term we're all better off if we can use the cleanly documented, well-understood, easy to parse, text-based formats that XML provides. XML lets documents and data be moved from one system to another with a reasonable hope that the receiving system will be able to make sense out of it. Furthermore, validation lets the receiving side check that what it gets is what it expects. Java promised portable code; XML delivers portable data. In many ways, XML is the most portable and flexible document format designed since the ASCII text file.

Previous | Home | Next
1. Introducing XML | Book Index | 1.3. How XML Works
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How XML Works (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

1.3. How XML Works

Example 1-1 shows a simple XML document. This particular XML document might be seen in an inventory-control system or a stock database. It marks up the data with tags and attributes describing the color, size, bar-code number, manufacturer, name of the product, and so on.

Example 1-1. An XML document

<?xml version="1.0"?>
<product barcode="2394287410">
  <manufacturer>Verbatim</manufacturer>
  <name>DataLife MF 2HD</name>
  <quantity>10</quantity>
  <size>3.5"</size>
  <color>black</color>
  <description>floppy disks</description>
</product>

This document is text and might well be stored in a text file. You can edit this file with any standard text editor such as BBEdit, jEdit, UltraEdit, Emacs, or vi. You do not need a special XML editor. Indeed, we find most general-purpose XML editors to be far more trouble than they're worth and much harder to use than simply editing documents in a text editor.

Programs that actually try to understand the contents of the XML document--that is, do more than merely treat it as any other text file--will use an XML parser to read the document. The parser is responsible for dividing the document into individual elements, attributes, and other pieces. It passes the contents of the XML document to an application piece by piece. If at any point the parser detects a violation of the well-formedness rules of XML, then it reports the error to the application and stops parsing. In some cases the parser may read further in the document, past the original error, so that it can detect and report other errors that occur later in the document. However, once it has detected the first well-formedness error, it will no longer pass along the contents of the elements and attributes it encounters.

Individual XML applications normally dictate more precise rules about exactly which elements and attributes are allowed where. For instance, you wouldn't expect to find a G_Clef element when reading a biology document. Some of these rules can be precisely specified with a schema written in any of several languages including the W3C XML Schema Language, RELAX NG, and DTDs. A document may contain a URI indicating where the schema can be found. Some XML parsers will notice this and compare the document to its schema as they read it to see if the document satisfies the constraints specified there. Such a parser is called a validating parser . A violation of those constraints is called a validity error , and the whole process of checking a document against a schema is called validation. If a validating parser finds a validity error, it will report it to the application on whose behalf it's parsing the document. This application can then decide whether it wishes to continue parsing the document. However, validity errors are not necessarily fatal (unlike well-formedness errors), and an application may choose to ignore them. Not all parsers are validating parsers. Some merely check for well-formedness.

The application that receives data from the parser may be:

  • A web browser such as Netscape Navigator or Internet Explorer that displays the document to a reader

  • A word processor such as StarOffice Writer that loads the XML document for editing

  • A database such as Microsoft SQL Server that stores the XML data in a new record

  • A drawing program such as Adobe Illustrator that interprets the XML as two-dimensional coordinates for the contents of a picture

  • A spreadsheet such as Gnumeric that parses the XML to find numbers and functions used in a calculation

  • A personal finance program such as Microsoft Money that sees the XML as a bank statement

  • A syndication program that reads the XML document and extracts the headlines for today's news

  • A program that you yourself wrote in Java, C, Python or some other language that does exactly what you want it to do

  • Almost anything else

XML is an extremely flexible format for data. It is used for all of this and a lot more. These are real examples. In theory, any data that can be stored in a computer can be stored in XML format. In practice, XML is suitable for storing and exchanging any data that can plausibly be encoded as text. It's only really unsuitable for multimedia data such as photographs, recorded sound, video, and other very large bit sequences.

Previous | Home | Next
1.2. Portable Data | Book Index | 1.4. The Evolution of XML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Evolution of XML (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

1.4. The Evolution of XML

XML is a descendant of SGML, the Standard Generalized Markup Language. The language that would eventually become SGML was invented by Charles F. Goldfarb, Ed Mosher, and Ray Lorie at IBM in the 1970s and developed by several hundred people around the world until its eventual adoption as ISO standard 8879 in 1986. SGML was intended to solve many of the same problems XML solves in much the same way XML solves them. It is a semantic and structural markup language for text documents. SGML is extremely powerful and achieved some success in the U.S. military and government, in the aerospace sector, and in other domains that needed ways of efficiently managing technical documents that were tens of thousands of pages long.

SGML's biggest success was HTML, which is an SGML application. However, HTML is just one SGML application. It does not have or offer anywhere near the full power of SGML itself. Since it restricts authors to a finite set of tags designed to describe web pages--and describes them in a fairly presentationally oriented way at that--it's really little more than a traditional markup language that has been adopted by web browsers. It doesn't lend itself to use beyond the single application of web-page design. You would not use HTML to exchange data between incompatible databases or to send updated product catalogs to retailer sites, for example. HTML does web pages, and it does them very well, but it only does web pages.

SGML was the obvious choice for other applications that took advantage of the Internet but were not simple web pages for humans to read. The problem was that SGML is complicated--very, very complicated. The official SGML specification is over 150 very technical pages. It covers many special cases and unlikely scenarios. It is so complex that almost no software has ever implemented it fully. Programs that implemented or relied on different subsets of SGML were often incompatible with each other. The special feature one program considered essential would be considered extraneous fluff and omitted by the next program.

In 1996, Jon Bosak, Tim Bray, C. M. Sperberg-McQueen, James Clark, and several others began work on a "lite" version of SGML that retained most of SGML's power while trimming a lot of the features that had proven redundant, too complicated to implement, confusing to end users, or simply not useful over the previous 20 years of experience with SGML. The result, in February of 1998, was XML 1.0, and it was an immediate success. Many developers who knew they needed a structural markup language but hadn't been able to bring themselves to accept SGML's complexity adopted XML whole-heartedly. It was used in domains ranging from legal court filings to hog farming.

However, XML 1.0 was just the beginning. The next standard out of the gate was Namespaces in XML, an effort to allow markup from different XML applications to be used in the same document without conflicting. Thus a web page about books could have a title element that referred to the title of the page and title elements that referred to the title of a book, and the two would not conflict.

Next up was the Extensible Stylesheet Language (XSL), an XML application for transforming XML documents into a form that could be viewed in web browsers. This soon split into XSL Transformations (XSLT) and XSL Formatting Objects (XSL-FO). XSLT has become a general-purpose language for transforming one XML document into another, whether for web-page display or some other purpose. XSL-FO is an XML application for describing the layout of both printed pages and web pages that rivals PostScript for its power and expressiveness.

However, XSL is not the only option for styling XML documents. The Cascading Style Sheets (CSS) language was already in use for HTML documents when XML was invented, and it proved to be a reasonable fit to XML as well. With the advent of CSS Level 2, the W3C made styling XML documents an explicit goal for CSS and gave it equal importance to HTML. The pre-existing Document Style Sheet and Semantics Language (DSSSL) was also adopted from its roots in the SGML world to style XML documents for print and the Web.

The Extensible Linking Language, XLink, began by defining more powerful linking constructs that could connect XML documents in a hypertext network that made HTML's A tag look like it is an abbreviation for "anemic." It also split into two separate standards: XLink for describing the connections between documents and XPointer for addressing the individual parts of an XML document. At this point, it was noticed that both XPointer and XSLT were developing fairly sophisticated yet incompatible syntaxes to do exactly the same thing: identify particular elements of an XML document. Consequently, the addressing parts of both specifications were split off and combined into a third specification, XPath.

Another piece of the puzzle was a uniform interface for accessing the contents of the XML document from inside a Java, JavaScript, or C++ program. The simplest API was merely to treat the document as an object that contained other objects. Indeed, work was already underway inside and outside the W3C to define such a Document Object Model (DOM) for HTML. Expanding this effort to cover XML was not hard.

Outside the W3C, David Megginson, Peter Murray-Rust, and other members of the xml-dev mailing list recognized that third party XML parsers, while all compatible in the documents they could parse, were incompatible in their APIs. This led to the development of the Simple API for XML, SAX. In 2000, SAX2 was released to add greater configurability in parsing, namespace support, and a cleaner API.

One of the surprises during the evolution of XML was that developers used it more for data-oriented structures such as serialized objects and database records than for the narrative structures for which SGML had traditionally been used. DTDs worked very well for narrative structures, but had some limits when faced with the data-oriented structures developers were actually creating. In particular, the lack of data typing and the fact that DTDs were not themselves XML documents were perceived as major problems. A number of companies and individuals began working on schema languages that addressed these deficiencies. Many of these proposals were submitted to the W3C, which formed a working group to try to merge the best parts of all of these and come up with something greater than the sum of its parts. In 2001, this group released Version 1.0 of the W3C XML Schema Language. Unfortunately, they produced something overly complex and burdensome. Consequently, several developers went back to the drawing board to invent cleaner, simpler, more elegant schema languages, including RELAX NG and Schematron.

Eventually, it became apparent that XML 1.0, XPath, the W3C XML Schema Language, SAX, and DOM all had similar but subtly different conceptual models of the structure of an XML document. For instance, XPath and SAX don't consider CDATA sections to be anything more than syntax sugar, but DOM does treat them differently than plain-text nodes. Thus the W3C XML Core Working Group began work on an XML Information Set that all these standards could rely on and refer to.

Development of extensions to the core XML specification continues. Future directions include:

XML Query Language
A fourth-generation language for extracting information that meets specified criteria from one or more XML documents

Canonical XML
A standard algorithm for determining whether two XML documents are the same after insignificant details, such as whether single or double quotes delimit attribute values, are accounted for

XInclude
A means of building a single XML document out of multiple well-formed, potentially valid XML documents and pieces thereof

XML Signatures
A standard for digitally signing XML documents, embedding those signatures in XML documents, and authenticating the resulting documents

XML Encryption
A standard XML syntax for encrypted digital content, including portions of XML documents

SAX 2.1
A set of small extensions to SAX2 that provides extra information about an XML document recommended by the Infoset, including the XML declaration

DOM Level 3
Many additional classes, interfaces, and methods that build on top of DOM2 to provide schema support, standard means of loading and saving XML documents, and many more additional capabilities

XFragment
An effort to make sense out of pieces of XML documents that may not be well-formed documents when considered in isolation

Doubtless, many new extensions of XML remain to be invented. XML has proven itself a solid foundation for many diverse technologies.

Previous | Home | Next
1.3. How XML Works | Book Index | 2. XML Fundamentals
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Fundamentals (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 2. XML Fundamentals

Contents:

XML Documents and XML Files
Elements, Tags, and Character Data
Attributes
XML Names
Entity References
CDATA Sections
Comments
Processing Instructions
The XML Declaration
Checking Documents for Well-Formedness

This chapter shows you how to write simple XML documents. You'll see that an XML document is built from text content marked up with text tags such as <SKU>, <Record_ID>, and <author> that look superficially like HTML tags. However, in HTML you're limited to about a hundred predefined tags that describe web-page formatting. In XML you can create as many tags as you need. Furthermore, these tags will mostly describe the type of content they contain rather than formatting or layout information. In XML you don't say that something is italicized or indented or bold; you say that it's a book or a biography or a calendar.

Although XML is looser than HTML in regards to which tags it allows, it is much stricter about where those tags are placed and how they're written. In particular, all XML documents must be well-formed. Well-formedness rules specify constraints such as "Every start-tag must have a matching end-tag" and "Attribute values must be quoted." These rules are unbreakable, which makes parsing XML documents easy and writing them a little harder, but they still allow an almost unlimited flexibility of expression.

2.1. XML Documents and XML Files

An XML document contains text, never binary data. It can be opened with any program that knows how to read a text file. Example 2-1 is close to the simplest XML document imaginable. Nonetheless, t is a well-formed XML document. XML parsers can read it and understand it (at least as far as a computer program can be said to understand anything).

Example 2-1. A very simple yet complete XML document

<person>
  Alan Turing
</person>

In the most common scenario, this document would be the entire contents of a file named person.xml, or perhaps 2-1.xml. However, XML is not picky about the filename. As far as the parser is concerned, this file could be called person.txt, person, or Hey you, there's some XML in this here file! Your operating system may or may not like these names, but an XML parser won't care. The document might not even be in a file at all. It could be a record or a field in a database. It could be generated on the fly by a CGI program in response to a browser query. It could even be stored in more than one file, though that's unlikely for such a simple document. If it is served by a web server, it will probably be assigned the MIME media type application/xml or text/xml. However, specific XML applications may use more specific MIME media types such as application/mathml+xml, application/XSLT+xml, image/svg+xml, text/vnd.wap.wml, or even text/html (in very special cases).

WARNING: For generic XML documents, application/xml should be preferred to text/xml, although most web servers use text/xml by default. text/xml uses the ASCII character set as a default, which is incorrect for most XML documents.

Previous | Home | Next
1.4. The Evolution of XML | Book Index | 2.2. Elements, Tags, and Character Data
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Elements, Tags, and Character Data (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.2. Elements, Tags, and Character Data

The document in Example 2-1 is composed of a single element named person. The element is delimited by the start-tag <person> and the end-tag </person>. Everything between the start-tag and the end-tag of the element (exclusive) is called the element's content. The content of this element is the text string:

Alan Turing

The whitespace is part of the content, though many applications will choose to ignore it. <person> and </person> are markup. The string "Alan Turing" and its surrounding whitespace are character data. The tag is the most common form of markup in an XML document, but there are other kinds we'll discuss later.

2.2.1. Tag Syntax

XML tags look superficially like HTML tags. Start-tags begin with < and end-tags begin with </. Both of these are followed by the name of the element and are closed by >. However, unlike HTML tags, you are allowed to make up new XML tags as you go along. To describe a person, use <person> and </person> tags. To describe a calendar, use <calendar> and </calendar> tags. The names of the tags generally reflect the type of content inside the element, not how that content will be formatted.

2.2.1.1. Empty elements

There's also a special syntax for empty elements, i.e., elements that have no content. Such an element can be represented by a single empty-element tag that begins with < but ends with />. For instance, in XHTML, an XMLized reformulation of standard HTML, the line-break and horizontal-rule elements are written as <br /> and <hr /> instead of <br> and <hr>. These are exactly equivalent to <br></br> and <hr></hr>, however. Which form you use for empty elements is completely up to you. However, what you cannot do in XML and XHTML (unlike HTML) is use only the start-tag--for instance <br> or <hr>--without using the matching the end-tag. That would be a well-formedness error.

2.2.1.2. Case sensitivity

XML, unlike HTML, is case sensitive. <Person> is not the same as <PERSON> is not the same as <person>. If you open an element with a <person> tag, you can't close it with a </PERSON> tag. You're free to use upper- or lowercase or both as you choose. You just have to be consistent within any one element.

2.2.2. XML Trees

Let's look at a slightly more complicated XML document. Example 2-2 is a person element that contains more information suitably marked up to show its meaning.

Example 2-2. A more complex XML document describing a person

<person>
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>computer scientist</profession>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

2.2.2.1. Parents and children

This XML document is still composed of one person element. However, now this element doesn't merely contain undifferentiated character data. It contains four child elements: a name element and three profession elements. The name element contains two child elements of its own, first_name and last_name.

The person element is called the parent of the name element and the three profession elements. The name element is the parent of the first_name and last_name elements. The name element and the three profession elements are sometimes called each other's siblings. The first_name and last_name elements are also siblings.

As in human society, any one parent may have multiple children. However, unlike human society, XML gives each child exactly one parent, not two or more. Each element (with one exception I'll note shortly) has exactly one parent element. That is, it is completely enclosed by another element. If an element's start-tag is inside some element, then its end-tag must also be inside that element. Overlapping tags, as in <strong><em>this common example from HTML</strong></em>, are prohibited in XML. Since the em element begins inside the strong element, it must also finish inside the strong element.

2.2.2.2. The root element

Every XML document has one element that does not have a parent. This is the first element in the document and the element that contains all other elements. In Example 2-1 and Example 2-2, the person element filled this role. It is called the root element of the document . It is also sometimes called the document element. Every well-formed XML document has exactly one root element. Since elements may not overlap, and since all elements except the root have exactly one parent, XML documents form a data structure programmers call a tree. Figure 2-1 diagrams this relationship for Example 2-2. Each gray box represents an element. Each black box represents character data. Each arrow represents a containment relationship.

Figure 2-1. A tree diagram for Example 2-2

2.2.3. Mixed Content

In Example 2-2, the contents of the first_name, last_name, and profession elements were character data, that is, text that does not contain any tags. The contents of the person and name elements were child elements and some whitespace that most applications will ignore. This dichotomy between elements that contain only character data and elements that contain only child elements (and possibly a little whitespace) is common in documents that are data oriented. However, XML can also be used for more free-form, narrative documents such as business reports, magazine articles, student essays, short stories, web pages, and so forth, as shown by Example 2-3.

Example 2-3. A narrative-organized XML document

<biography>
  <name><first_name>Alan</first_name> <last_name>Turing</last_name>
  </name> was one of the first people to truly deserve the name
  <emphasize>computer scientist</emphasize>. Although his contributions
  to the field are too numerous to list, his best-known are the
  eponymous <emphasize>Turing Test</emphasize> and
  <emphasize>Turing Machine</emphasize>.

  <definition>The <term>Turing Test</term> is to this day the standard
  test for determining whether a computer is truly intelligent. This
  test has yet to be passed. </definition>

  <definition>The <term>Turing Machine</term> is an abstract finite
  state automaton with infinite memory that can be proven equivalent
  to any any other finite state automaton with arbitrarily large memory.
  Thus what is true for a Turing machine is true for all equivalent
  machines no matter how implemented.
  </definition>

  <name><last_name>Turing</last_name></name> was also an accomplished
  <profession>mathematician</profession> and
  <profession>cryptographer</profession>. His assistance
  was crucial in helping the Allies decode the German Enigma
  machine. He committed suicide on <date><month>June</month>
  <day>7</day>, <year>1954</year></date> after being
  convicted of homosexuality and forced to take female
  hormone injections.
</biography>

The root element of this document is biography. The biography contains name, definition, profession, and emphasize child elements. It also contains a lot of raw character data. Some of these elements such as last_name and profession only contain character data. Others such as name contain only child elements. Still others such as definition contain both character data and child elements. These elements are said to contain mixed content. Mixed content is common in XML documents containing articles, essays, stories, books, novels, reports, web pages, and anything else that's organized as a written narrative. Mixed content is less common and harder to work with in computer-generated and processed XML documents used for purposes such as database exchange, object serialization, persistent file formats, and so on. One of the strengths of XML is the ease with which it can be adapted to the very different requirements of human-authored and computer-generated documents.

Previous | Home | Next
2. XML Fundamentals | Book Index | 2.3. Attributes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Attributes (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.3. Attributes

XML elements can have attributes. An attribute is a name-value pair attached to the element's start-tag. Names are separated from values by an equals sign and optional whitespace. Values are enclosed in single or double quotation marks. For example, this person element has a born attribute with the value 1912-06-23 and a died attribute with the value 1954-06-07:

<person born="1912-06-23" died="1954-06-07">
  Alan Turing
</person>

This next element is exactly the same as far an XML parser is concerned. It simply uses single quotes instead of double quotes, puts some extra whitespace around the equals signs, and reorders the attributes.

<person died = '1954-06-07'  born = '1912-06-23' >
  Alan Turing
</person>

The whitespace around the equals signs is purely a matter of personal aesthetics. The single quotes may be useful in cases where the attribute value itself contains a double quote. Attribute order is not significant.

Example 2-4 shows how attributes might be used to encode much of the same information given in the data-oriented document of Example 2-2.

Example 2-4. An XML document that describes a person using attributes

<person>
  <name first="Alan" last="Turing"/>
  <profession value="computer scientist"/>
  <profession value="mathematician"/>
  <profession value="cryptographer"/>
</person>

This raises the question of when and whether one should use child elements or attributes to hold information. This is a subject of heated debate. Some informaticians maintain that attributes are for metadata about the element while elements are for the information itself. Others point out that it's not always so obvious what's data and what's metadata. Indeed, the answer may depend on where the information is put to use.

What's undisputed is that each element may have no more than one attribute with a given name. That's unlikely to be a problem for a birth date or a death date; it would be an issue for a profession, name, address, or anything else of which an element might plausibly have more than one. Furthermore, attributes are quite limited in structure. The value of the attribute is simply a text string. The division of a date into a year, month, and day with hyphens in the previous example is at the limits of the substructure that can reasonably be encoded in an attribute. Consequently, an element-based structure is a lot more flexible and extensible. Nonetheless, attributes are certainly more convenient in some applications. Ultimately, if you're designing your own XML vocabulary, it's up to you to decide when to use which.

Attributes are also useful in narrative documents, as Example 2-5 demonstrates. Here it's perhaps a little more obvious what belongs to elements and what to attributes. The raw text of the narrative is presented as character data inside elements. Additional information annotating that data is presented as attributes. This includes source references, image URLs, hyperlinks, and birth and death dates. Even here, however, there's more than one way to do it. For instance, the footnote numbers could be attributes of the footnote element rather than character data.

Example 2-5. A narrative XML document that uses attributes

<biography xmlns:xlink="http://www.w3.org/1999/xlink/namespace/">

  <image source="http://www.turing.org.uk/turing/pi1/bus.jpg"
  width="152" height="345"/>
  <person born='1912-06-23'
  died='1954-06-07'><first_name>Alan</first_name>
  <last_name>Turing</last_name> </person> was one of the first people
  to truly deserve the name <emphasize>computer scientist</emphasize>.
  Although his contributions to the field were too numerous to list,
  his best-known are the eponymous <emphasize xlink:type="simple"
  xlink:href="http://cogsci.ucsd.edu/~asaygin/tt/ttest.html">Turing
  Test</emphasize> and <emphasize  xlink:type="simple"
  xlink:href="http://mathworld.wolfram.com/TuringMachine.html">Turing
  Machine</emphasize>.

  <last_name>Turing</last_name> was also an accomplished
  <profession>mathematician</profession> and
  <profession>cryptographer</profession>. His assistance
  was crucial in helping the Allies decode the German Enigma
  machine.<footnote source="The Ultra Secret, F.W. Winterbotham,
  1974">1</footnote>

  He committed suicide on <date><month>June</month> <day>7</day>,
  <year>1954</year></date> after being convicted of homosexuality
  and forced to take female hormone injections.<footnote
  source="Alan Turing: the Enigma, Andrew Hodges, 1983">2</footnote>
</biography>
Previous | Home | Next
2.2. Elements, Tags, and Character Data | Book Index | 2.4. XML Names
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Names (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.4. XML Names

The XML specification can be quite legalistic and picky at times. Nonetheless, it tries to be efficient where possible. One way it does that is by reusing the same rules for different items where possible. For example, the rules for XML element names are also the rules for XML attribute names, as well as for the names of several less common constructs. Generally, these are referred to simply as XML names.

Element and other XML names may contain essentially any alphanumeric character. This includes the standard English letters A through Z and a through z as well as the digits 0 through 9. XML names may also include non-English letters, numbers, and ideograms such as ö, ç, Figure , and Figure . They may also include these three punctuation characters:

_
the underscore

-
the hyphen

.
the period

XML names may not contain other punctuation characters such as quotation marks, apostrophes, dollar signs, carets, percent symbols, and semicolons. The colon is allowed, but its use is reserved for namespaces as discussed in Chapter 4. XML names may not contain whitespace of any kind, whether a space, a carriage return, a line feed, a nonbreaking space, and so forth. Finally, all names beginning with the string XML (in any combination of case) are reserved for standardization in W3C XML-related specifications.

XML names may only start with letters, ideograms, and the underscore character. They may not start with a number, hyphen, or period. There is no limit to the length of an element or other XML name. Thus these are all well-formed elements:

  • <Drivers_License_Number>98 NY 32</Drivers_License_Number>

  • <month-day-year>7/23/2001</month-day-year>

  • <first_name>Alan</first_name>

  • <_4-lane>I-610</_4-lane>

  • <téléphone>011 33 91 55 27 55 27</téléphone>

These are not acceptable elements:

  • <Driver's_License_Number>98 NY 32</Driver's_License_Number>

  • <month/day/year>7/23/2001</month/day/year>

  • <first name>Alan</first name>

  • <4-lane>I-610</4-lane>

Previous | Home | Next
2.3. Attributes | Book Index | 2.5. Entity References
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Entity References (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.5. Entity References

The character data inside an element may not contain a raw unescaped opening angle bracket (<). This character is always interpreted as beginning a tag. If you need to use this character in your text, you can escape it using the &lt; entity reference. When a parser reads the document, it will replace the &lt; entity reference with the actual < character. However, it will not confuse &lt; with the start of a tag. For example:

<SCRIPT LANGUAGE="JavaScript">
  if (location.host.toLowerCase( ).indexOf("cafeconleche") &lt; 0) {
    location.href="http://www.cafeconleche.org/";
  }
</SCRIPT>

The character data inside an element may not contain a raw unescaped ampersand (&) either. This is always interpreted as beginning an entity or character reference. However, the ampersand may be escaped using the &amp; entity reference like this:

<publisher>O'Reilly &amp; Associates</publisher>

Entity references such as &amp; and &lt; are considered to be markup. When an application parses an XML document, it replaces this particular markup with the actual characters to which the entity reference refers.

XML predefines exactly five entity references. These are:

&lt;
The less-than sign; a.k.a. the opening angle bracket (<)

&amp;
The ampersand (&)

&gt;
The greater-than sign; a.k.a. the closing angle bracket (>)

&quot;
The straight, double quotation marks (")

&apos;
The apostrophe; a.k.a. the straight single quote (')

Only &lt; and &amp; must be used instead of the literal characters in element content. The others are optional. &quot; and &apos; are useful inside attribute values where a raw " or ' might be misconstrued as ending the attribute value. For example, this image tag uses the &apos; entity reference to fill in the apostrophe in O'Reilly:

<image source='oreilly_koala3.gif' width='122' height='66'
       alt='Powered by O&apos;Reilly Books'
/>

Although there's no possibility of an unescaped greater-than sign (>) being misinterpreted as closing a tag it wasn't meant to close, &gt; is allowed mostly for symmetry with &lt;.

In addition to the five predefined entity references, you can define others in the document type definition. We'll discuss how to do this in Chapter 3.

Previous | Home | Next
2.4. XML Names | Book Index | 2.6. CDATA Sections
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


CDATA Sections (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.6. CDATA Sections

When an XML document includes samples of XML or HTML source code, the < and & characters in those samples must be encoded as &lt; and &amp;. The more sections of literal code a document includes and the longer they are, the more tedious this encoding becomes. Instead you can enclose each sample of literal code in a CDATA section. A CDATA section is set off by a <![CDATA[ and ]]>. Everything between the <![CDATA[ and the ]]> is treated as raw character data. Less-than signs don't begin. Ampersands don't start entity references. Everything is simply character data, not markup.

For example, in a Scalable Vector Graphics (SVG) tutorial written in XHTML, you might see something like this:

<p>You can use a default <code>xmlns</code> attribute to avoid
having to add the svg prefix to all your elements:</p>
     <![CDATA[
       <svg xmlns="http://www.w3.org/2000/svg"
            width="12cm" height="10cm">
         <ellipse rx="110" ry="130" />
         <rect x="4cm" y="1cm" width="3cm" height="6cm" />
       </svg>
     ]]>

The SVG source code has been included directly in the XHTML file without carefully replacing each < with &lt;. The result will be a sample SVG document, not an embedded SVG picture, as might happen if this example were not placed inside a CDATA section.

The only thing that can not appear in a CDATA section is the CDATA section end delimiter ]]>.

CDATA sections exist for the convenience of human authors, not for programs. Parsers are not required to tell you whether a particular block of text came from a CDATA section, from normal character data, or from character data that contained entity references such as &lt; and &amp;. By the time you get access to the data, these differences will have been washed away.

Previous | Home | Next
2.5. Entity References | Book Index | 2.7. Comments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Comments (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.7. Comments

XML documents can be commented so that coauthors can leave notes for each other and themselves, documenting why they've done what they've done or items that remain to be done. XML comments are syntactically similar to HTML comments. Just as in HTML, they begin with <!-- and end with the first occurrence of -->. For example:

<!-- I need to verify and update these links when I get a chance. -->

The double hyphen -- should not appear anywhere inside the comment until the closing -->. In particular, a three hyphen close like ---> is specifically forbidden.

Comments may appear anywhere in the character data of a document. They may also appear before or after the root element. (Comments are not elements, so this does not violate the tree structure or the one-root element rules for XML.) However, comments may not appear inside a tag or inside another comment.

Applications that read and process XML documents may or may not pass along information included in comments. They are certainly free to drop them out if they choose. Do not write documents or applications that depend on the contents of comments being available. Comments are strictly for making the raw source code of an XML document more legible to human readers. They are not intended for computer programs. For this purpose you should use a processing instruction instead.

Previous | Home | Next
2.6. CDATA Sections | Book Index | 2.8. Processing Instructions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Processing Instructions (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.8. Processing Instructions

In HTML, comments are sometimes abused to support nonstandard extensions. For instance, the contents of the script element are sometimes enclosed in a comment to protect it from display by a nonscript-aware browser. The Apache web server parses comments in .shtml files to recognize server side includes. Unfortunately, these documents may not survive being passed through various HTML editors and processors with their comments and associated semantics intact. Worse yet, it's possible for an innocent comment to be misconstrued as input to the application.

XML provides the processing instruction as an alternative means of passing information to particular applications that may read the document. A processing instruction begins with <? and ends with ?>. Immediately following the <? is an XML name called the target, possibly the name of the application for which this processing instruction is intended or possibly just an identifier for this particular processing instruction. The rest of the processing instruction contains text in a format appropriate for the applications for which the instruction is intended.

For example, in HTML a robots META tag is used to tell search-engine and other robots whether and how they should index a page. The following processing instruction has been proposed as an equivalent for XML documents:

<?robots index="yes" follow="no"?>

The target of this processing instruction is robots. The syntax of this particular processing instruction is two pseudoattributes, one named index and one named follow, whose values are either yes or no. The semantics of this particular processing instruction are that if the index attribute has the value yes, then search-engine robots should index this page. If index has the value no, then it won't be. Similarly, if follow has the value yes, then links from this document will be followed.

Other processing instructions may have totally different syntaxes and semantics. For instance, processing instructions can contain an effectively unlimited amount of text. PHP includes large programs in processing instructions. For example:

<?php
  mysql_connect("database.unc.edu", "clerk", "password");
  $result = mysql("HR", "SELECT LastName, FirstName FROM Employees
    ORDER BY LastName, FirstName");
  $i = 0;
  while ($i < mysql_numrows ($result)) {
     $fields = mysql_fetch_row($result);
     echo "<person>$fields[1] $fields[0] </person>\r\n";
     $i++;
  }
  mysql_close( );
?>

Processing instructions are markup, but they're not elements. Consequently, like comments, processing instructions may appear anywhere in an XML document outside of a tag, including before or after the root element. The most common processing instruction, xml-stylesheet, is used to attach stylesheets to documents. It always appears before the root element, as Example 2-6 demonstrates. In this example, the xml-stylesheet processing instruction tells browsers to apply the CSS stylesheet person.css to this document before showing it to the reader.

Example 2-6. A very simple yet complete XML document

<?xml-stylesheet href="person.css" type="text/css"?>
<person>
  Alan Turing
</person>

The processing instruction names xml, XML, XmL, etc., in any combination of case, are forbidden to avoid confusion with the XML declaration. Otherwise, you're free to pick any legal XML name for your processing instructions.

Previous | Home | Next
2.7. Comments | Book Index | 2.9. The XML Declaration
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The XML Declaration (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.9. The XML Declaration

XML documents should (but do not have to) begin with an XML declaration. The XML declaration looks like a processing instruction with the name xml and version, standalone, and encoding attributes. Technically, it's not a processing instruction though, just the XML declaration; nothing more, nothing less. Example 2-7 demonstrates.

Example 2-7. A very simple XML document with an XML declaration

<?xml version="1.0" encoding="ASCII" standalone="yes"?>
<person>
  Alan Turing
</person>

XML documents do not have to have an XML declaration. However, if an XML document does have an XML declaration, then that declaration must be the first thing in the document. It must not be preceded by any comments, whitespace, processing instructions, and so forth. The reason is that an XML parser uses the first five characters (<?xml) to make some reasonable guesses about the encoding, such as whether the document uses a single byte or multibyte character set. The only thing that may precede the XML declaration is an invisible Unicode byte-order mark. We'll discuss this further in Chapter 5.

2.9.1. encoding

So far we've been a little cavalier about encodings. We've said that XML documents are composed of pure text, but we haven't said what encoding that text uses. Is it ASCII? Latin-1? Unicode? Something else?

The short answer to this question is "Yes." The long answer is that by default XML documents are assumed to be encoded in the UTF-8 variable-length encoding of the Unicode character set. This is a strict superset of ASCII, so pure ASCII text files are also UTF-8 documents. However, most XML processors, especially those written in Java, can handle a much broader range of character sets. All you have to do is tell the parser which character encoding the document uses. Preferably this is done through metainformation, stored in the filesystem or provided by the server. However, not all systems provide character-set metadata so XML also allows documents to specify their own character set with an encoding declaration inside the XML declaration. Example 2-8 shows how you'd indicate that a document was written in the ISO-8859-1 (Latin-1) character set that includes letters like ö and ç needed for many non-English Western European languages.

Example 2-8. An XML document encoded in Latin-1

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<person>
  Erwin Schrödinger
</person>

The encoding attribute is optional in an XML declaration. If it is omitted and no metadata is available, then the Unicode character set is assumed. The parser may use the first several bytes of the file to try to guess which encoding of Unicode is in use. If metadata is available and it conflicts with the encoding declaration, then the encoding specified by the metadata wins. For example, if an HTTP header says a document is encoded in ASCII but the encoding declaration says it's encoded in UTF-8, then the parser will pick ASCII.

The different encodings and the proper handling of non-English XML documents will be discussed in greater detail in Chapter 5.

2.9.2. standalone

If the standalone attribute has the value no, then an application may be required to read an external DTD (that is a DTD in a file other than the one it's reading now) to determine the proper values for parts of the document. For instance, a DTD may provide default values for attributes that a parser is required to report even though they aren't actually present in the document.

Documents that do not have DTDs, like all the documents in this chapter, can have the value yes for the standalone attribute. Documents that do have DTDs can also have the value yes for the standalone attribute if the DTD doesn't in any way change the content of the document or if the DTD is purely internal. Details for documents with DTDs are covered in Chapter 3.

The standalone attribute is optional in an XML declaration. If it is omitted, then the value no is assumed.

Previous | Home | Next
2.8. Processing Instructions | Book Index | 2.10. Checking Documents for Well-Formedness
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Checking Documents for Well-Formedness (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

2.10. Checking Documents for Well-Formedness

Every XML document, without exception, must be well-formed. This means it must adhere to a number of rules, including the following:

  1. Every start-tag must have a matching end-tag.

  2. Elements may nest, but may not overlap.

  3. There must be exactly one root element.

  4. Attribute values must be quoted.

  5. An element may not have two attributes with the same name.

  6. Comments and processing instructions may not appear inside tags.

  7. No unescaped < or & signs may occur in the character data of an element or attribute.

This is not an exhaustive list. There are many, many ways a document can be malformed. You'll find a complete list in Chapter 20. Some of these involve constructs that we have not yet discussed such as DTDs. Others are extremely unlikely to occur if you follow the examples in this chapter (for example, including whitespace between the opening < and the element name in a tag).

Whether the error is small or large, likely or unlikely, an XML parser reading a document is required to report it. It may or may not report multiple well-formedness errors it detects in the document. However, the parser is not allowed to try to fix the document and make a best-faith effort of providing what it thinks the author really meant. It can't fill in missing quotes around attribute values, insert an omitted end-tag, or ignore the comment that's inside a start-tag. The parser is required to return an error. The objective here is to avoid the bug-for-bug compatibility wars that plagued early web browsers and continue to this day. Consequently, before you publish an XML document, whether that document is a web page, input to a database, or something else, you'll want to check it for well-formedness.

The simplest way to do this is by loading the document into a web browser that understands XML documents such as Mozilla. If the document is well-formed, the browser will display it. If it isn't, then it will show an error message.

Instead of loading the document into a web browser, you can use an XML parser directly. Most XML parsers are not intended for end users. They are class libraries designed to be embedded into an easier-to-use program such as Mozilla. They provide a minimal command-line interface, if that; that interface is often not particularly well documented. Nonetheless, it can sometimes be quicker to run a batch of files through a command-line interface than loading each of them into a web browser. Furthermore, once you learn about DTDs and schemas, you can use the same tools to validate documents, which most web browsers won't do.

There are many XML parsers available in a variety of languages. Here, we'll demonstrate checking for well-formedness with the Apache XML Project's Xerces-J 1.4, which you can download from http://xml.apache.org/xerces-j. This open source package is written in pure Java so it should run across all major platforms. The procedure should be similar for other parsers, though details will vary.

To use this parser, you'll first need a Java 1.1 or later compatible virtual machine. Virtual machines for Windows, Solaris, and Linux are available from http://java.sun.com/. To install Xerces-J 1.4.4, just add xerces.jar and xercesSamples.jar files to your Java class path. In Java 2 you can simply put those .jar files into your jre/lib/ext directory.

The class that actually checks files for well-formedness is called sax.SAXCount. It's run from a Unix shell or DOS prompt like any other standalone Java program. The command-line arguments are the URLs to or filenames of the documents you want to check. Here's the result of running SAXCount against an early version of Example 2-5. The very first line of output tells you where the first problem in the file is. The rest of the output is a more or less irrelevant stack trace.

D:\xian\examples\02>java sax.SAXCount 2-5.xml
[Fatal Error] 2-5.xml:3:30: The value of attribute "height" must not contain the '<' character.
Stopping after fatal error: The value of attribute "height" must not contain the '<' character.
at org.apache.xerces.framework.XMLParser.reportError(XMLParser.java:
1282)
at org.apache.xerces.framework.XMLDocumentScanner.reportFatalXMLError(
XMLDocumentScanner.java:644)
at org.apache.xerces.framework.XMLDocumentScanner.scanAttValue(
XMLDocumentScanner.java:519)
at org.apache.xerces.framework.XMLParser.scanAttValue(
XMLParser.java:1932)
at org.apache.xerces.framework.XMLDocumentScanner.scanElement(
XMLDocumentScanner.java:1800)
at org.apache.xerces.framework.XMLDocumentScanner$ContentDispatcher.
dispatch(XMLDocumentScanner.java:1223)
at org.apache.xerces.framework.XMLDocumentScanner.parseSome(
XMLDocumentScanner.java:381)
at org.apache.xerces.framework.XMLParser.parse(XMLParser.java:1138)
at org.apache.xerces.framework.XMLParser.parse(XMLParser.java:1177)
at sax.SAXCount.print(SAXCount.java:135)
at sax.SAXCount.main(SAXCount.java:331)

As you can see, it found an error. In this case the error message wasn't particularly helpful. The actual problem wasn't that an attribute value contained a < character. It was that the closing quote was missing from the height attribute value. Still, that was enough for us to locate and fix the problem. Despite the long list of output, SAXCount only reports the first error in the document, so you may have to run it multiple times until all the mistakes are found and fixed. Once we fixed Example 2-5 to make it well-formed, SAXCount simply reported how long it took to parse the document and what it saw when it did:

D:\xian\examples\02>java sax.SAXCount 2-5.xml
2-5.xml: 140 ms (17 elems, 12 attrs, 0 spaces, 564 chars)

Now that the document has been corrected to be well-formed, it can be passed to a web browser, a database, or whatever other program is waiting to receive it. Almost any nontrivial document crafted by hand will contain well-formedness mistakes. That makes it important to check your work before publishing it.

TIP: This example works with Xerces-J 1.0 through 1.4.4. The recently released Xerces-J 2.0 provides a similar program named sax.Counter.

Previous | Home | Next
2.9. The XML Declaration | Book Index | 3. Document Type Definitions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Document Type Definitions (DTDs) (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 3. Document Type Definitions (DTDs)

Contents:

Validation
Element Declarations
Attribute Declarations
General Entity Declarations
External Parsed General Entities
External Unparsed Entities and Notations
Parameter Entities
Conditional Inclusion
Two DTD Examples
Locating Standard DTDs

While XML is extremely flexible, not all the programs that read particular XML documents are so flexible. Many programs can work with only some XML applications but not others. For example, Adobe Illustrator 10 can read and write Scalable Vector Graphics (SVG) files, but you wouldn't expect it to understand a Platform for Privacy Preferences (P3P) document. And within a particular XML application, it's often important to ensure that a given document indeed adheres to the rules of that XML application. For instance, in XHTML, li elements should only be children of ul or ol elements. Browsers may not know what to do with them, or may act inconsistently, if li elements appear in the middle of a blockquote or p element.

The solution to this dilemma is a document type definition (DTD). DTDs are written in a formal syntax that explains precisely which elements and entities may appear where in the document and what the elements' contents and attributes are. A DTD can make statements such as "A ul element only contains li elements" or "Every employee element must have a social_security_number attribute." Different XML applications can use different DTDs to specify what they do and do not allow.

A validating parser compares a document to its DTD and lists any places where the document differs from the constraints specified in the DTD.[3] The program can then decide what it wants to do about any violations. Some programs may reject the document. Others may try to fix the document or reject just the invalid element. Validation is an optional step in processing XML. A validity error is not necessarily a fatal error like a well-formedness error, though some applications may choose to treat it as one.

[3]The document type declaration and the document type definition are two different things. The abbreviation DTD is properly used only to refer to the document type definition.

3.1. Validation

A valid document includes a document type declaration that identifies the DTD the document satisfies. The DTD lists all the elements, attributes, and entities the document uses and the contexts in which it uses them. The DTD may list items the document does not use as well. Validity operates on the principle that everything not permitted is forbidden. Everything in the document must match a declaration in the DTD. If a document has a document type declaration and the document satisfies the DTD that the document type declaration indicates, then the document is said to be valid. If it does not, it is said to be invalid.

There are many things the DTD does not say. In particular, it does not say the following:

  • What the root element of the document is

  • How many of instances of each kind of element appear in the document

  • What the character data inside the elements looks like

  • The semantic meaning of an element; for instance, whether it contains a date or a person's name

DTDs allow you to place some constraints on the form an XML document takes, but there can be quite a bit of flexibility within those limits. A DTD never says anything about the length, structure, meaning, allowed values, or other aspects of the text content of an element.

Validity is optional. A parser reading an XML document may or may not check for validity. If it does check for validity, the program receiving data from the parser may or may not care about validity errors. In some cases, such as feeding records into a database, a validity error may be quite serious, indicating that a required field is missing, for example. In other cases, rendering a web page perhaps, a validity error may not be so important, and you can work around it. Well-formedness is required of all XML documents; validity is not. Your documents and your programs can use it or not as you find needful.

3.1.1. A Simple DTD Example

Recall Example 2-2 from the last chapter; this described a person. The person had a name and three professions. The name had a first name and a last name. The particular person described in that example was Alan Turing. However, that's not relevant for DTDs. A DTD only describes the general type, not the specific instance. A DTD for person documents would say that a person element contains one name child element and zero or more profession child elements. It would further say that each name element contains a first_name child element and a last_name child element. Finally it would state that the first_name, last_name, and profession elements all contain text. Example 3-1 is a DTD that describes such a person element.

Example 3-1. A DTD for the person

<!ELEMENT person     (name, profession*)>
<!ELEMENT name       (first_name, last_name)>
<!ELEMENT first_name (#PCDATA)>
<!ELEMENT last_name  (#PCDATA)>
<!ELEMENT profession (#PCDATA)>

This DTD would probably be stored in a separate file from the documents it describes. This allows it to be easily referenced from multiple XML documents. However, it can be included inside the XML document if that's convenient, using the document type declaration we discuss later in this section. If it is stored in a separate file, then that file would most likely be named person.dtd, or something similar. The .dtd extension is fairly standard though not specifically required by the XML specification. If this file were served by a web server, it would be given the MIME media type application/xml-dtd.

Each line of Example 3-1 is an element declaration. The first line declares the person element; the second line declares the name element; the third line declares the first_name element; and so on. However, the line breaks aren't relevant except for legibility. Although it's customary to put only one declaration on each line, it's not required. Long declarations can even span multiple lines.

The first element declaration in Example 3-1 states that each person element must contain exactly one name child element followed by zero or more profession elements. The asterisk after profession stands for "zero or more." Thus, every person must have a name and may or may not have a profession or multiple professions. However, the name must come before all professions. For example, this person element is valid:

<person>
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
</person>

However, this person element is not valid because it omits the name:

<person>
  <profession>computer scientist</profession>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

This person element is not valid because a profession element comes before the name:

<person>
  <profession>computer scientist</profession>
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

The person element may not contain any element except those listed in its declaration. The only extra character data it can contain is whitespace. For example, this is an invalid person element because it adds a publication element:

<person>
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
  <publication>On Computable Numbers...</publication>
</person>

This is an invalid person element because it adds some text outside the allowed children:

<person>
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  was a <profession>computer scientist</profession>,
  a <profession>mathematician</profession>, and a
  <profession>cryptographer</profession>
</person>

In all these examples of invalid elements, you could change the DTD to make these elements valid. All the examples are well-formed, after all. However, with the DTD in Example 3-1, they are not valid.

The name declaration says that each name element must contain exactly one first_name element followed by exactly one last_name element. All other variations are forbidden.

The remaining three declarations--first_name, last_name, and profession--all say that their elements must contain #PCDATA. This is a DTD keyword standing for parsed character data --that is, raw text possibly containing entity references such as &amp; and &lt;, but not containing any tags or child elements.

Example 3-1 placed the most complicated and highest-level declaration at the top. However, that's not required. For instance, Example 3-2 is an equivalent DTD that simply reorders the declarations. DTDs allow forward, backward, and circular references to other declarations.

Example 3-2. An alternate DTD for the person element

<!ELEMENT first_name (#PCDATA)>
<!ELEMENT last_name  (#PCDATA)>
<!ELEMENT profession (#PCDATA)>
<!ELEMENT name       (first_name, last_name)>
<!ELEMENT person     (name, profession*)>

3.1.2. The Document Type Declaration

A valid document includes a reference to the DTD to which it should be compared. This is given in the document's single document type declaration. A document type declaration looks like this:

<!DOCTYPE person SYSTEM "http://www.cafeconleche.org/dtds/person.dtd">

This says that the root element of the document is person and that the DTD for this document can be found at the URI http://www.cafeconleche.org/dtds/person.dtd.

TIP: URI stands for Uniform Resource Identifier. URIs are a superset of URLs . They include not only URLs but also Uniform Resource Names (URNs). A URN allows you to identify a resource such as the DTD for SVGs irrespective of its location. Indeed, the resource might exist at multiple locations, all equally authoritative. In practice, the only URIs in wide use today are URLs.

The document type declaration is included in the prolog of the XML document after the XML declaration but before the root element. (The prolog is everything in the XML document before the root element start-tag.) Example 3-3 demonstrates.

Example 3-3. A valid person document

<?xml version="1.0" standalone="no"?>
<!DOCTYPE person SYSTEM "http://www.cafeconleche.org/dtds/person.dtd">
<person>
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>computer scientist</profession>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

If the document resides at the same base site as the DTD, you can use a relative URL instead of the absolute form. For example:

<!DOCTYPE person SYSTEM "/dtds/person.dtd">

You can even use just the filename if the DTD is in the same directory as the document:

<!DOCTYPE person SYSTEM "person.dtd">

3.1.2.1. Public IDs

Standard DTDs may actually be stored at multiple URLs. For example, if you're drawing an SVG picture on your laptop at the beach, you probably want to validate your drawing without opening a network connection to the W3C's web site where the official SVG DTD resides. Such DTDs may be associated with public IDs. The name of the public ID uniquely identifies the XML application in use. At the same time, a backup URI is also included in case the validator does not recognize the public ID. To indicate that you're specifying a public ID, use the keyword PUBLIC in place of SYSTEM. For example, this document type declaration refers to the Rich Site Summary (RSS) DTD standardized by Netscape:

<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
              "http://my.netscape.com/publish/formats/rss-0.91.dtd">

A local catalog server can be used to convert the public IDs into the most appropriate URLs for the local environment. The catalogs themselves can be written in XML, specifically, the OASIS XML catalog format (http://www.oasis-open.org/committees/entity/spec.html). In practice, however, PUBLIC IDs aren't used very much. Almost all validators rely on the URI to actually validate the document.

3.1.3. Internal DTD Subsets

When you're first developing a DTD, it's often useful to keep the DTD and the canonical example document in the same file so you can modify and check them simultaneously. Therefore, the document type declaration may actually contain the DTD between square brackets rather than referencing it at an external URI. Example 3-4 demonstrates.

Example 3-4. A valid person document with an internal DTD

<?xml version="1.0"?>
<!DOCTYPE person [
  <!ELEMENT first_name (#PCDATA)>
  <!ELEMENT last_name  (#PCDATA)>
  <!ELEMENT profession (#PCDATA)>
  <!ELEMENT name       (first_name, last_name)>
  <!ELEMENT person     (name, profession*)>
]>
<person>
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>computer scientist</profession>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

Some document type declarations contain some declarations directly but link in others using a SYSTEM or PUBLIC identifier. For example, this document type declaration declares the profession and person elements itself but relies on the file name.dtd to contain the declaration of the name element:

<!DOCTYPE person SYSTEM "name.dtd" [
  <!ELEMENT profession (#PCDATA)>
  <!ELEMENT person (name, profession*)>
]>

The part of the DTD between the brackets is called the internal DTD subset. All the parts that come from outside this document are called the external DTD subset. Together they make up the complete DTD. As a general rule, the two different subsets must be compatible. Neither can override the element declarations the other makes. For example, if name.dtd also declared the person element, then there would be a problem. However, entity declarations can be overridden with some important consequences for DTD structure and design, which we'll see shortly when we discuss entities.

When you use an external DTD subset, you should give the standalone attribute of the XML declaration the value no. For example:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
TIP: Actually, the XML specification includes four very detailed rules about exactly when the presence of an external DTD subset does and does not require the standalone attribute to have the value no. However, the net effect of these rules is that almost all XML documents that use external DTD subsets require standalone to have the value no. Since setting standalone to no is always permitted even when it's not required, it's simply not worth worrying about the uncommon cases.

A validating processor is required to read the external DTD subset. A nonvalidating processor may do so, but is not required to, even if standalone has the value no. This means that if the external subset makes declarations that have consequences for the content of a document (for instance, providing default values for attributes) then the content of the document depends on which parser you're using and how it's configured. This has led to no end of confusion. Although some of the earliest XML parsers did not resolve external entities, most of the parsers still being used can do so and generally will do so. You should read the external DTD subset unless efficiency is a major concern or you're very familiar with the structure of the documents you're parsing.

3.1.4. Validating a Document

As a general rule, web browsers do not validate documents but only check them for well-formedness. If you're writing your own programs to process XML, you can use the parser's API to validate documents. If you're writing documents by hand and you want to validate them, you can either use one of the online validators or run a local program to validate the document.

The online validators are probably the easiest way to validate your documents. There are two of note:

  • The Brown University Scholarly Technology Group's XML Validation Form at http://www.stg.brown.edu/service/xmlvalid/

  • Richard Tobin's XML well-formedness checker and validator at http://www.cogsci.ed.ac.uk/%7Erichard/xml-check.html

First, you have to place the document and associated DTDs on a publicly accessible web server. Next, load one of the previous URLs in a browser, and type the URL of the document you're checking into the online form. The validating server will retrieve your document and tell you what, if any, errors it found. Figure 3-1 shows the results of using the Brown validator on a simple invalid but well-formed document.

Figure 3-1. Validity errors detected by the Brown University online validator

Most XML parser class libraries include a simple program to validate documents you can use if you're comfortable installing and using command-line programs. In Xerces 1.x, that program is sax.SAXCount. (Xerces 2.x uses sax.Counter instead.) Use the -v flag to turn on validation. (By default, SAXCount only checks for well-formedness.) Then pass the URLs or filenames of the documents you wish to validate to the SAXCount program on the command line like this:

C:\>java sax.SAXCount -v invalid_fibonacci.xml
[Error] invalid_fibonacci.xml:8:10: Element type "title" must be declared.
[Error] invalid_fibonacci.xml:110:22: The content of element type
"Fibonacci_Numbers" must match "(fibonacci)*".
fibonacci.xml: 541 ms (103 elems, 101 attrs, 307 spaces, 1089 chars)

You can see from this output that the document invalid_fibonacci.xml has two validity errors that need to be fixed: the first in line 8 and the second in line 110.

There are also some simple GUI programs for validating XML documents, including the Topologi Schematron Validator for Windows (http://www.topologi.com) shown in Figure 3-2. Despite the name, this product can actually validate documents against schemas written in multiple languages, including DTDs and the W3C XML Schema Language, as well as Schematron.

Figure 3-2. Validity errors detected by the Topologi Schematron Validator

Previous | Home | Next
2.10. Checking Documents for Well-Formedness | Book Index | 3.2. Element Declarations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Element Declarations (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.2. Element Declarations

Every element used in a valid document must be declared in the document's DTD with an element declaration. Element declarations have this basic form:

<!ELEMENT element_name content_specification>

The name of the element can be any legal XML name. The content specification specifies what children the element may or must have in what order. Content specifications can be quite complex. They can say, for example, that an element must have three child elements of a given type, or two children of one type followed by another element of a second type, or any elements chosen from seven different types interspersed with text.

3.2.1. #PCDATA

About the simplest content specification is one that says an element may only contain parsed character data, but may not contain any child elements of any type. In this case the content specification consists of the keyword #PCDATA inside parentheses. For example, this declaration says that a phone_number element may contain text, but may not contain elements:

<!ELEMENT phone_number (#PCDATA)>

3.2.2. Child Elements

Another simple content specification is one that says the element must have exactly one child of a given type. In this case, the content specification simply consists of the name of the child element inside parentheses. For example, this declaration says that a fax element must contain exactly one phone_number element:

<!ELEMENT fax (phone_number)>

A fax element may not contain anything else except the phone_number element, and it may not contain more or less than one of those.

3.2.3. Sequences

In practice, however, a content specification that lists exactly one child element is rare. Most elements contain either parsed character data or (at least potentially) multiple child elements. The simplest way to indicate multiple child elements is to separate them with commas. This is called a sequence. It indicates that the named elements must appear in the specified order. For example, this element declaration says that a name element must contain exactly one first_name child element followed by exactly one last_name child element:

<!ELEMENT name (first_name, last_name)>

Given this declaration, this name element is valid:

<name>
  <first_name>Madonna</first_name>
  <last_name>Ciconne</last_name>
</name>

However, this one is not valid because it flips the order of two elements:

<name>
  <last_name>Ciconne</last_name>
  <first_name>Madonna</first_name>
</name>

This element is invalid because it omits the last_name element:

<name>
  <first_name>Madonna</first_name>
</name>

This one is invalid because it adds a middle_name element:

<name>
  <first_name>Madonna</first_name>
  <middle_name>Louise</middle_name>
  <last_name>Ciconne</last_name>
</name>

3.2.4. The Number of Children

As the previous examples indicate, not all instances of a given element necessarily have exactly the same children. You can affix one of three suffixes to an element name in a content specification to indicate how many of that element are expected at that position. These suffixes are:

?
Zero or one of the element is allowed.

*
Zero or more of the element is allowed.

+
One or more of the element is required.

For example, this declaration says that a name element must contain a first_name, may or may not contain a middle_name, and may or may not contain a last_name:

<!ELEMENT name (first_name, middle_name?, last_name?)>

Given this declaration, all these name elements are valid:

<name>
  <first_name>Madonna</first_name>
  <last_name>Ciconne</last_name>
</name>
<name>
  <first_name>Madonna</first_name>
  <middle_name>Louise</middle_name>
  <last_name>Ciconne</last_name>
</name>
<name>
  <first_name>Madonna</first_name>
</name>

However, these are not valid:

<name>
  <first_name>George</first_name>
  <!-- only one middle name is allowed -->
  <middle_name>Herbert</middle_name>
  <middle_name>Walker</middle_name>
  <last_name>Bush</last_name>
</name>
<name>
  <!-- first name must precede last name -->
  <last_name>Ciconne</last_name>
  <first_name>Madonna</first_name>
</name>

You can allow for multiple middle names by placing an asterisk after the middle_name:

<!ELEMENT name (first_name, middle_name*, last_name?)>

If you wanted to require a middle_name to be included, but still allow for multiple middle names, you'd use a plus sign instead, like this:

<!ELEMENT name (first_name, middle_name+, last_name?)>

3.2.5. Choices

Sometimes one instance of an element may contain one kind of child, and another instance may contain a different child. This can be indicated with a choice. A choice is a list of element names separated by vertical bars. For example, this declaration says that a methodResponse element contains either a params child or a fault child:

<!ELEMENT methodResponse (params | fault)>

However, it cannot contain both at once. Each methodResponse element must contain one or the other.

Choices can be extended to an indefinite number of possible elements. For example, this declaration says that each digit element can contain exactly one of the child elements named zero, one, two, three, four, five, six, seven, eight, or nine:

<!ELEMENT digit
 (zero | one | two | three | four | five | six | seven | eight | nine)
>

3.2.6. Parentheses

Individually, choices, sequences, and suffixes are fairly limited. However, they can be combined in arbitrarily complex fashions to describe most reasonable content models. Either a choice or a sequence can be enclosed in parentheses. When so enclosed, the choice or sequence can be suffixed with a ?, *, or +. Furthermore, the parenthesized item can be nested inside other choices or sequences.

For example, let's suppose you want to say that a circle element contains a center element and either a radius or a diameter element, but not both. This declaration does that:

<!ELEMENT circle (center, (radius | diameter))>

To continue with a geometry example, suppose a center element can either be defined in terms of Cartesian or polar coordinates. Then each center contains either an x and a y or an r and a Figure . We would declare this using two small sequences, each of which is parenthesized and combined in a choice:

<!ELEMENT center ((x, y) | (r, Figure ))>

Suppose you don't really care whether the x element comes before the y element or vice versa, nor do you care whether r comes before Figure . Then you can expand the choice to cover all four possibilities:

<!ELEMENT center ((x, y) | (y, x) | (r, Figure ) | ( Figure , r) )>

As the number of elements in the sequence grows, the number of permutations grows more than exponentially. Thus, this technique really isn't practical past two or three child elements. DTDs are not very good at saying you want n instances of A and m instances of B, but you don't really care which order they come in.

Suffixes can be applied to parenthesized elements too. For instance, let's suppose that a polygon is defined by individual coordinates for each vertex, given in order. For example, this is a right triangle:

What we want to say is that a polygon is composed of three or more pairs of x-y or r- Figure coordinates. An x is always followed by a y, and an r is always followed by a Figure . This declaration does that:

The plus sign is applied to ((x, y) | (r, Figure )).

To return to the name example, suppose you want to say that a name can contain just a first name, just a last name, or a first name and a last name with an indefinite number of middle names. This declaration achieves that:

<!ELEMENT name (last_name
               | (first_name, ( (middle_name+, last_name) | (last_name?) )
               ) >

3.2.7. Mixed Content

In narrative documents it's common for a single element to contain both child elements and un-marked up, nonwhitespace character data. For example, recall this definition element from Chapter 2:

<definition>The <term>Turing Machine</term> is an abstract finite
state automaton with infinite memory that can be proven equivalent
to any any other finite state automaton with arbitrarily large memory.
Thus what is true for a Turing machine is true for all equivalent
machines no matter how implemented.
</definition>

The definition element contains some nonwhitespace text and a term child. This is called mixed content. An element that contains mixed content is declared like this:

<!ELEMENT definition (#PCDATA | term)*>

This says that a definition element may contain parsed character data and term children. It does not specify in which order they appear, nor how many instances of each appear. This declaration allows a definition to have one term child, no term children, or twenty-three term children.

You can add any number of other child elements to the list of mixed content, though #PCDATA must always be the first child in the list. For example, this declaration says that a paragraph element may contain any number of name, profession, footnote, emphasize, and date elements in any order, interspersed with parsed character data:

<!ELEMENT paragraph
  (#PCDATA | name | profession | footnote | emphasize | date )*
>

This is the only way to indicate that an element contains mixed content. You cannot say, for example, that there must be exactly one term child of the definition element, as well as parsed character data. You cannot say that the parsed character data must all come after the term child. You cannot use parentheses around a mixed-content declaration to make it part of a larger grouping. You can only say that the element contains any number of any elements from a particular list in any order, as well as undifferentiated parsed character data.

3.2.8. Empty Elements

Some elements do not have any content at all. These are called empty elements and are sometimes written with a closing />. For example:

<image source="bus.jpg" width="152" height="345"
       alt="Alan Turing standing in front of a bus"
/>

These elements are declared by using the keyword EMPTY for the content specification. For example:

<!ELEMENT image EMPTY>

This merely says that the image element must be empty, not that it must be written with an empty-element tag. Given this declaration, this is also a valid image element:

<image source="bus.jpg" width="152" height="345"
       alt="Alan Turing standing in front of a bus"></image>

If an element is empty, then it can contain nothing, not even whitespace. For instance, this is an invalid image element:

<image source="bus.jpg" width="152" height="345"
       alt="Alan Turing standing in front of a bus">
</image>

3.2.9. ANY

Very loose DTDs occasionally want to say that an element exists without making any assertions about what it may or may not contain. In this case you can specify the keyword ANY as the content specification. For example, this declaration says that a page element can contain any content including mixed content, child elements, and even other page elements:

<!ELEMENT page ANY>

The children that actually appear in the page elements' content in the document must still be declared in element declarations of their own. ANY does not allow you to use undeclared elements.

ANY is sometimes useful when you're just beginning to design the DTD and document structure and you don't yet have a clear picture of how everything fits together. However, it's extremely bad form to use ANY in finished DTDs. About the only time you'll see it used is when external DTD subsets and entities may change in uncontrollable ways. However, this is actually quite rare. You'd really only need this if you were writing a DTD for an application like XSLT or RDF that wraps content from arbitrary, unknown XML applications.

Previous | Home | Next
3. Document Type Definitions | Book Index | 3.3. Attribute Declarations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Attribute Declarations (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.3. Attribute Declarations

As well as declaring its elements, a valid document must declare all the elements' attributes. This is done with ATTLIST declarations. A single ATTLIST can declare multiple attributes for a single element type. However, if the same attribute is repeated on multiple elements, then it must be declared separately for each element where it appears. (Later in this chapter you'll see how to use parameter entity references to make this repetition less burdensome.)

For example, this ATTLIST declaration declares the source attribute of the image element:

<!ATTLIST image source CDATA #REQUIRED>

It says that the image element has an attribute named source. The value of the source attribute is character data, and instances of the image element in the document are required to provide a value for the source attribute.

A single ATTLIST declaration can declare multiple attributes for the same element. For example, this ATTLIST declaration not only declares the source attribute of the image element, but also the width, height, and alt attributes:

<!ATTLIST image source CDATA #REQUIRED
                width  CDATA #REQUIRED
                height CDATA #REQUIRED
                alt    CDATA #IMPLIED
>

This declaration says the source, width, and height attributes are required. However, the alt attribute is optional and may be omitted from particular image elements. All four attributes are declared to contain character data, the most generic attribute type.

This declaration has the same effect and meaning as four separate ATTLIST declarations, one for each attribute. Whether to use one ATTLIST declaration per attribute is a matter of personal preference, but most experienced DTD designers prefer the multiple-attribute form. Given judicious application of whitespace, it's no less legible than the alternative.

3.3.1. Attribute Types

In merely well-formed XML, attribute values can be any string of text. The only restrictions are that any occurrences of < or & must be escaped as &lt; and &amp; and whichever kind of quotation mark, single or double, is used to delimit the value must also be escaped. However, a DTD allows you to make somewhat stronger statements about the content of an attribute value. Indeed, these are stronger statements than can be made about the contents of an element. For instance, you can say that an attribute value must be unique within the document, that it must be a legal XML name token, or that it must be chosen from a fixed list of values.

There are ten attribute types in XML. They are:

  • CDATA

  • NMTOKEN

  • NMTOKENS

  • Enumeration

  • ENTITY

  • ENTITIES

  • ID

  • IDREF

  • IDREFS

  • NOTATION

These are the only attribute types allowed. A DTD cannot say that an attribute value must be an integer or a date between 1966 and 2002, for example.

3.3.1.1. CDATA

A CDATA attribute value can contain any string of text acceptable in a well-formed XML attribute value. This is the most general attribute type. For example, you would use this type for an alt attribute of an image element because there's no particular form the text in such an attribute has to follow.

<!ATTLIST image alt CDATA #IMPLIED>

You would also use this for other kinds of data such as prices, URIs, email and snail mail addresses, citations, and other types that--while they have more structure than a simple string of text--don't match any of the other attribute types. For example:

<!ATTLIST sku
 list_price             CDATA #IMPLIED
 suggested_retail_price CDATA #IMPLIED
 actual_price           CDATA #IMPLIED
>
<!-- All three attributes should be in the form $XX.YY -->

3.3.1.2. NMTOKEN

An XML name token is very close to an XML name. It must consist of the same characters as an XML name, that is, alphanumeric and/or ideographic characters and the punctuation marks _, -, ., and :. Furthermore, like an XML name, an XML name token may not contain whitespace. However, a name token differs from an XML name in that any of the allowed characters can be the first character in a name token, while only letters, ideographs, and the underscore can be the first character of an XML name. Thus 12 and .cshrc are valid XML name tokens although they are not valid XML names. Every XML name is an XML name token, but not all XML name tokens are XML names.

The value of an attribute declared to have type NMTOKEN is an XML name token. For example, if you knew that the year attribute of a journal element should contain an integer such as 1990 or 2015, you might declare it to have NMTOKEN type, since all years are name tokens:

<!ATTLIST journal year NMTOKEN #REQUIRED>

This still doesn't prevent the document author from assigning the year attribute values like "99" or "March", but it at least eliminates some possible wrong values, especially those that contain whitespace such as "1990 C.E." or "Sally had a little lamb."

3.3.1.3. NMTOKENS

A NMTOKENS type attribute contains one or more XML name tokens separated by whitespace. For example, you might use this to describe the dates attribute of a performances element, if the dates were given in the form 08-26-2000, like this:

<performances dates="08-21-2001 08-23-2001 08-27-2001">
  Kat and the Kings
</performances>

The appropriate declaration is:

<!ATTLIST performances dates NMTOKENS #REQUIRED>

On the other hand, you could not use this for a list of dates in the form 08/27/2001 because the forward slash is not a legal name character.

3.3.1.4. Enumeration

An enumeration is the only attribute type that is not an XML keyword. Rather, it is a list of all possible values for the attribute, separated by vertical bars. Each possible value must be an XML name token. For example, the following declarations say that the value of the month attribute of a date element must be one of the twelve English month names, that the value of the day attribute must be a number between 1 and 31, and that the value of the year attribute must be an integer between 1970 and 2009:

<!ATTLIST date month (January | February | March | April | May | June
  | July | August | September | October | November | December) #REQUIRED
>
<!ATTLIST date day (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
  | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25
  | 26 | 27 | 28 | 29 | 30 | 31) #REQUIRED
>
<!ATTLIST date year (1970 | 1971 | 1972 | 1973 | 1974 | 1975 | 1976
  | 1977 | 1978 | 1979 | 1980 | 1981 | 1982 | 1983 | 1984 | 1985 | 1986
  | 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | 1993 | 1994 | 1995 | 1996
  | 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006
  | 2007 | 2008 | 2009 ) #REQUIRED
>
<!ELEMENT date EMPTY>

Given this DTD, this date element is valid:

<date month="January" day="22" year="2001"/>

However, these date elements are invalid:

<date month="01"      day="22" year="2001"/>
<date month="Jan"     day="22" year="2001"/>
<date month="January" day="02" year="2001"/>
<date month="January" day="2"  year="1969"/>
<date month="Janvier" day="22" year="2001"/>

This trick works here because all the desired values happen to be legal XML name tokens. However, we could not use the same trick if the possible values included whitespace or any punctuation besides the underscore, hyphen, colon, and period.

3.3.1.5. ID

An ID type attribute must contain an XML name (not a name token but a name) that is unique within the XML document. More precisely, no other ID type attribute in the document can have the same value. (Attributes of non-ID type are not considered.) Each element may have no more than one ID type attribute.

As the keyword suggests, ID type attributes assign unique identifiers to elements. ID type attributes do not have to have the name "ID" or "id", though they very commonly do. For example, this ATTLIST declaration says that every employee element must have a social_security_number ID attribute:

<!ATTLIST employee social_security_number ID #REQUIRED>

ID numbers are tricky because a number is not an XML name and therefore not a legal XML ID. The normal solution is to prefix the values with an underscore or a common letter. For example:

<employee social_security_label="_078-05-1120"/>

3.3.1.6. IDREF

An IDREF type attribute refers to the ID type attribute of some element in the document. Thus, it must be an XML name. IDREF attributes are commonly used to establish relationships between elements when simple containment won't suffice.

For example, imagine an XML document that contains a list of project elements and employee elements. Every project has a project_id ID type attribute, and every employee has a social_security_number ID type attribute. Furthermore, each project has team_member child elements that identify who's working on the project, and each employee element has assignment children that indicate to which projects that employee is assigned. Since each project is assigned to multiple employees and some employees are assigned to more than one project, it's not possible to make the employees children of the projects or the projects children of the employees. The solution is to use IDREF type attributes like this:

<project project_id="p1">
  <goal>Develop Strategic Plan</goal>
  <team_member person="ss078-05-1120"/>
  <team_member person="ss987-65-4320"/>
</project>
<project project_id="p2">
  <goal>Deploy Linux</goal>
  <team_member person="ss078-05-1120"/>
  <team_member person="ss9876-12-3456"/>
</project>
<employee social_security_label="ss078-05-1120">
  <name>Fred Smith</name>
  <assignment project_id="p1"/>
  <assignment project_id="p2"/>
</employee>
<employee social_security_label="ss987-65-4320">
  <name>Jill Jones</name>
  <assignment project_id="p1"/>
</employee>
<employee social_security_label="ss9876-12-3456">
  <name>Sydney Lee</name>
  <assignment project_id="p2"/>
</employee>

In this example, the project_id attribute of the project element and the social_security_number attribute of the employee element would be declared to have type ID. The person attribute of the team_member element and the project_id attribute of the assignment element would have type IDREF. The relevant ATTLIST declarations look like this:

<!ATTLIST employee social_security_number ID    #REQUIRED>
<!ATTLIST project  project_id             ID    #REQUIRED>
<!ATTLIST team_member person              IDREF #REQUIRED>
<!ATTLIST assignment  project_id          IDREF #REQUIRED>

These declarations constrain the person attribute of the team_member element and the project_id attribute of the assignment element to match the ID of something in the document. However, they do not constrain the person attribute of the team_member element to match only employee IDs or constrain the project_id attribute of the assignment element to match only project IDs. It would be valid (though not necessarily correct) for a team_member to hold the ID of another project or even the same project.

3.3.1.7. IDREFS

An IDREFS type attribute contains a whitespace-separated list of XML names, each of which must be the ID of an element in the document. This is used when one element needs to refer to multiple other elements. For instance, the previous project example could be rewritten so that the assignment children of the employee element were replaced by a single assignments attribute. Similarly, the team_member children of the project element could be replaced by a team attribute like this:

<project project_id="p1" team="ss078-05-1120 ss987-65-4320">
  <goal>Develop Strategic Plan</goal>
</project>
<project project_id="p2" team="ss078-05-1120 ss9876-12-3456">
  <goal>Deploy Linux</goal>
</project>
<employee social_security_label="ss078-05-1120" assignments="p1 p2">
  <name>Fred Smith</name>
</employee>
<employee social_security_label="ss987-65-4320" assignments="p1">
  <name>Jill Jones</name>
</employee>
<employee social_security_label="ss9876-12-3456" assignments="p2">
  <name>Sydney Lee</name>
</employee>

The appropriate declarations are:

<!ATTLIST employee social_security_number ID     #REQUIRED
                   assignments            IDREFS #REQUIRED>
<!ATTLIST project  project_id             ID     #REQUIRED
                   team                   IDREFS #REQUIRED>

3.3.1.8. ENTITY

An ENTITY type attribute contains the name of an unparsed entity declared elsewhere in the DTD. For instance, a movie element might have an entity attribute identifying the MPEG or QuickTime file to play when the movie was activated:

<!ATTLIST movie source ENTITY #REQUIRED>

If the DTD declared an unparsed entity named X-Men-trailer, then this movie element might be used to embed that video file in the XML document:

<movie source="X-Men-trailer"/>

We'll discuss unparsed entities in more detail later in this chapter.

3.3.1.9. ENTITIES

An ENTITIES type attribute contains the names of one or more unparsed entities declared elsewhere in the DTD, separated by whitespace. For instance, a slide_show element might have an ENTITIES attribute identifying the JPEG files to show and in which order they were to be shown:

<!ATTLIST slide_show slides ENTITIES #REQUIRED>

If the DTD declared unparsed entities named slide1, slide2, slide3, and so on through slide10, then this slide_show element might be used to embed the show in the XML document:

<slide_show slides="slide1 slide2 slide3 slide4 slide5 slide6
                    slide7 slide8 slide9 slide10"/>

3.3.1.10. NOTATION

A NOTATION type attribute contains the name of a notation declared in the document's DTD. This is perhaps the rarest attribute type and isn't much used in practice. In theory, it could be used to associate types with particular elements, as well as limiting the types associated with the element. For example, these declarations define four notations for different image types and then specify that each image element must have a type attribute that selects exactly one of them:

<!NOTATION gif  SYSTEM "image/gif">
<!NOTATION tiff SYSTEM "image/tiff">
<!NOTATION jpeg SYSTEM "image/jpeg">
<!NOTATION png  SYSTEM "image/png">
<!ATTLIST  image type NOTATION (gif | tiff | jpeg | png) #REQUIRED>

The type attribute of each image element can have one of the four values gif, tiff, jpeg, or png but not any other value. This has a slight advantage over the enumerated type in that the actual MIME media type of the notation is available, whereas an enumerated type could not specify image/png or image/gif as an allowed value because the forward slash is not a legal character in XML names.

3.3.2. Attribute Defaults

As well as providing a data type, each ATTLIST declaration includes a default declaration for that attribute. There are four possibilities for this default:

#IMPLIED
The attribute is optional. Each instance of the element may or may not provide a value for the attribute. No default value is provided.

#REQUIRED
The attribute is required. Each instance of the element must provide a value for the attribute. No default value is provided.

#FIXED
The attribute value is constant and immutable. This attribute has the specified value regardless of whether the attribute is explicitly noted on an individual instance of the element. If it is included, though, it must have the specified value.

Literal
The actual default value is given as a quoted string.

For example, this ATTLIST declaration says that person elements can have but do not have to have born and died attributes:

<!ATTLIST person born CDATA #IMPLIED
                 died CDATA #IMPLIED
>

This ATTLIST declaration says that every circle element must have center_x, center_y, and radius attributes:

<!ATTLIST circle center_x NMTOKEN #REQUIRED
                 center_y NMTOKEN #REQUIRED
                 radius   NMTOKEN #REQUIRED
>

This ATTLIST declaration says that every biography element has an xmlns:xlink attribute and that the value of that attribute is http://www.w3.org/1999/xlink, even if the start-tag of the element does not explicitly include an xmlns:xlink attribute.

<!ATTLIST biography
   xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">

This ATTLIST declaration says that every web_page element has a protocol attribute. If a particular web_page element doesn't have an explicit protocol attribute, then the parser will supply one with the value http:

<!ATTLIST web_page protocol NMTOKEN "http">
Previous | Home | Next
3.2. Element Declarations | Book Index | 3.4. General Entity Declarations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


General Entity Declarations (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.4. General Entity Declarations

As you learned in Chapter 2, XML predefines five entities for your convenience:

&lt;
The less-than sign; a.k.a. the opening angle bracket (<)

&amp;
The ampersand (&)

&gt;
The greater-than sign; a.k.a. the closing angle bracket (>)

&quot;
The straight, double quotation marks (")

&apos;
The apostrophe; a.k.a. the straight single quote (')

The DTD can define many more. This is useful not just in valid documents, but even in documents you don't plan to validate.

Entity references are defined with an ENTITY declaration in the DTD. This gives the name of the entity, which must be an XML name, and the replacement text of the entity. For example, this entity declaration defines &super; as an abbreviation for supercalifragilisticexpialidocious:

<!ENTITY super "supercalifragilisticexpialidocious">

Once that's done, you can use &super; anywhere you'd normally have to type the entire word (and probably misspell it).

Entities can contain markup as well as text. For example, this declaration defines &footer; as an abbreviation for a standard web-page footer that will be repeated on many pages:

<!ENTITY footer '<hr size="1" noshade="true"/>
<font CLASS="footer">
<a href="index.html">O&apos;Reilly Home</a> |
<a href="sales/bookstores/">O&apos;Reilly Bookstores</a> |
<a href="order_new/">How to Order</a> |
<a href="oreilly/contact.html">O&apos;Reilly Contacts</a><br>
<a href="http://international.oreilly.com/">International</a> |
<a href="oreilly/about.html">About O&apos;Reilly</a> |
<a href="affiliates.html">Affiliated Companies</a>
</font>
<p>
<font CLASS="copy">
Copyright 2000, O&apos;Reilly &amp; Associates, Inc.<br/>
<a href="mailto:webmaster@oreilly.com">webmaster@oreilly.com</a>
</font>
</p>
'>

The entity replacement text must be well-formed. For instance, you cannot put a start-tag in one entity and the corresponding end-tag in another entity.

The other thing you have to be careful about is that you need to use different quote marks inside the replacement text from the ones that delimit it. Here we've chosen single quotes to surround the replacement text and double quotes internally. However, we did have to change the single quote in "O'Reilly" to the predefined general entity reference &apos;. Replacement text may itself contain entity references that are resolved before the text is replaced. However, self-referential and circular references are forbidden.

General entities insert replacement text into the body of an XML document. They can also be used inside the DTD in places where they will eventually be included in the body of an XML document, for instance in an attribute default value or in the replacement text of another entity. However, they cannot be used to provide the text of the DTD itself. For instance, this is illegal:

Shortly, we'll see how to use a different kind of entity--the parameter entity--to achieve the desired result.

Previous | Home | Next
3.3. Attribute Declarations | Book Index | 3.5. External Parsed General Entities
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


External Parsed General Entities (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.5. External Parsed General Entities

The footer example is about at the limits of what you can comfortably fit in a DTD. In practice, web sites prefer to store repeated content like this in external files and load it into their pages using PHP, server-side includes, or some similar mechanism. XML supports this technique through external general entity references, though in this case the client, rather than the server, is responsible for integrating the different pieces of the document into a coherent whole.

An external parsed general entity reference is declared in the DTD using an ENTITY declaration. However, instead of the actual replacement text, the SYSTEM keyword and a URI to the replacement text is given. For example:

<!ENTITY footer SYSTEM "http://www.oreilly.com/boilerplate/footer.xml">

Of course, a relative URL will often be used instead. For example:

<!ENTITY footer SYSTEM "/boilerplate/footer.xml">

In either case when the general entity reference &footer; is seen in the character data of an element, the parser may replace it with the document found at http://www.oreilly.com/boilerplate/footer.xml. References to external parsed entities are not allowed in attribute values. Most of the time, this shouldn't be too big a hassle because attribute values tend to be small enough to be easily included in internal entities.

Notice we wrote that the parser may replace the entity reference with the document at the URL, not that it must. This is an area where parsers have some leeway in just how much of the XML specification they wish to implement. A validating parser must retrieve such an external entity. However, a nonvalidating parser may or may not choose to retrieve the entity.

Furthermore, not all text files can serve as external entities. In order to be loaded in by a general entity reference, the document must be potentially well-formed when inserted into an existing document. This does not mean the external entity itself must be well-formed. In particular, the external entity might not have a single root element. However, if such a root element were wrapped around the external entity, then the resulting document should be well-formed. This means, for example, that all elements that start inside the entity must finish inside the same entity. They cannot finish inside some other entity. Furthermore, the external entity does not have a prolog and, therefore, cannot have an XML declaration or a document type declaration.

3.5.1. Text Declarations

Instead of an XML declaration, an external entity may have a text declaration; this looks a lot like an XML declaration. The main difference is that in a text declaration the encoding declaration is required, while the version info is optional. Furthermore, there is no standalone declaration. The main purpose of the text declaration is to warn the parser if the external entity uses a different text encoding than the including document. For example, this is a common text declaration:

<?xml version="1.0" encoding="MacRoman"?>

However, you could also use this text declaration with no version attribute:

<?xml encoding="MacRoman"?>

Example 3-5 is a well-formed external entity that could be included from another document using an external general entity reference.

Example 3-5. An external parsed entity

<?xml encoding="ISO-8859-1"?>
<hr size="1" noshade="true"/>
<font CLASS="footer">
  <a href="index.html">O'Reilly Home</a> |
  <a href="sales/bookstores/">O'Reilly Bookstores</a> |
  <a href="order_new/">How to Order</a> |
  <a href="oreilly/contact.html">O'Reilly Contacts</a><br>
  <a href="http://international.oreilly.com/">International</a> |
  <a href="oreilly/about.html">About O'Reilly</a> |
  <a href="affiliates.html">Affiliated Companies</a>
</font>
<p>
  <font CLASS="copy">
    Copyright 2000, O'Reilly &amp; Associates, Inc.<br/>
    <a href="mailto:webmaster@oreilly.com">webmaster@oreilly.com</a>
  </font>
</p>
Previous | Home | Next
3.4. General Entity Declarations | Book Index | 3.6. External Unparsed Entities and Notations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


External Unparsed Entities and Notations (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.6. External Unparsed Entities and Notations

Not all data is XML. There are a lot of ASCII text files in the world that don't give two cents about escaping < as &lt; or adhering to the other constraints by which an XML document is limited. There are probably even more JPEG photographs, GIF line art, QuickTime movies, MIDI sound files, and so on. None of these are well-formed XML, yet all of them are necessary components of many documents.

The mechanism that XML suggests for embedding these things in your documents is the external unparsed entity. The DTD specifies a name and a URI for the entity containing the non-XML data. For example, this ENTITY declaration associates the name turing_getting_off_bus with the JPEG image at http://www.turing.org.uk/turing/pi1/bus.jpg:

<!ENTITY turing_getting_off_bus
         SYSTEM "http://www.turing.org.uk/turing/pi1/bus.jpg"
         NDATA jpeg>

3.6.1. Notations

Since the data is not in XML format, the NDATA declaration specifies the type of the data. Here the name jpeg is used. XML does not recognize this as meaning an image in a format defined by the Joint Photographs Experts Group. Rather this is the name of a notation declared elsewhere in the DTD using a NOTATION declaration like this:

<!NOTATION jpeg SYSTEM "image/jpeg">

Here we've used the MIME media type image/jpeg as the external identifier for the notation. However, there is absolutely no standard or even a suggestion for exactly what this identifier should be. Individual applications must define their own requirements for the contents and meaning of notations.

3.6.2. Embedding Unparsed Entities in Documents

The DTD only declares the existence, location, and type of the unparsed entity. To actually include the entity in the document at one or more locations, you insert an element with an ENTITY type attribute whose value is the name of an unparsed entity declared in the DTD. You do not use an entity reference like &turing_getting_off_bus;. Entity references can only refer to parsed entities.

Suppose the image element and its source attribute are declared like this:

<!ELEMENT image EMPTY>
<!ATTLIST image source ENTITY #REQUIRED>

Then, this image element would refer to the photograph at http://www.turing.org.uk/turing/pi1/bus.jpg:

<image source="turing_getting_off_bus"/>

We should warn you that XML doesn't guarantee any particular behavior from an application that encounters this type of unparsed entity. It very well may not display the image to the user. Indeed, the parser may be running in an environment where there's no user to display the image to. It may not even understand that this is an image. The parser may not load or make any sort of connection with the server where the actual image resides. At most, it will tell the application on whose behalf it's parsing that there is an unparsed entity at a particular URI with a particular notation and let the application decide what, if anything, it wants to do with that information.

TIP: Unparsed general entities are not the only plausible way to embed non-XML content in XML documents. In particular, a simple URL, possibly associated with an XLink, does a fine job for many purposes, just as it does in HTML (which gets along just fine without any unparsed entities). Including all the necessary information in a single empty element like <image source = "http://www.turing.org.uk/turing/pi1/bus.jpg" /> is arguably preferable to splitting the same information between the element where it's used and the DTD of the document in which it's used. The only thing an unparsed entity really adds is the notation, but that's too nonstandard to be of much use.

In fact, many experienced XML developers, including the authors of this book, feel strongly that unparsed entities are a complicated, confusing mistake that should never have been included in XML in the first place. Nonetheless, they are a part of the specification, so we describe them here.

3.6.3. Notations for Processing Instruction Targets

Notations can also be used to identify the exact target of a processing instruction. A processing instruction target must be an XML name, which means it can't be a full path like /usr/local/bin/tex. A notation can identify a short XML name like tex with a more complete specification of the external program for which the processing instruction is intended. For example, this notation associates the target tex with the more complete path /usr/local/bin/tex:

<!NOTATION tex SYSTEM "/usr/local/bin/tex">

In practice, this technique isn't much used or needed. Most applications that read XML files and pay attention to particular processing instructions simply recognize a particular target string like php or robots.

Previous | Home | Next
3.5. External Parsed General Entities | Book Index | 3.7. Parameter Entities
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Parameter Entities (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.7. Parameter Entities

It is not uncommon for multiple elements to share all or part of the same attribute lists and content specifications. For instance, any element that's a simple XLink will have xlink:type and xlink:href attributes, and perhaps xlink:show and xlink:actuate attributes. In XHTML, a th element and a td element contain more or less the same content. Repeating the same content specifications or attribute lists in multiple element declarations is tedious and error-prone. It's entirely possible to add a newly defined child element to the declaration of some of the elements but forget to include it in others.

For example, consider an XML application for residential real-estate listings that provides separate elements for apartments, sublets, coops for sale, condos for sale, and houses for sale. The element declarations might look like this:

<!ELEMENT apartment (address, footage, rooms, baths, rent)>
<!ELEMENT sublet    (address, footage, rooms, baths, rent)>
<!ELEMENT coop      (address, footage, rooms, baths, price)>
<!ELEMENT condo     (address, footage, rooms, baths, price)>
<!ELEMENT house     (address, footage, rooms, baths, price)>

There's a lot of overlap between the declarations, i.e., a lot of repeated text. And if you later decide you need to add an additional element, available_date for instance, then you need to add it to all five declarations. It would be preferable to define a constant that can hold the common parts of the content specification for all five kinds of listings and refer to that constant from inside the content specification of each element. Then to add or delete something from all the listings, you'd only need to change the definition of the constant.

An entity reference is the obvious candidate here. However, general entity references are not allowed to provide replacement text for a content specification or attribute list, only for parts of the DTD that will be included in the XML document itself. Instead, XML provides a new construct exclusively for use inside DTDs, the parameter entity, which is referred to by a parameter entity reference. Parameter entities behave like and are declared almost exactly like a general entity. However, they use a % instead of an &, and they can only be used in a DTD while general entities can only be used in the document content.

3.7.1. Parameter Entity Syntax

A parameter entity reference is declared much like a general entity reference. However, an extra percent sign is placed between the <!ENTITY and the name of the entity. For example:

<!ENTITY % residential_content "address, footage, rooms, baths">
<!ENTITY % rental_content      "rent">
<!ENTITY % purchase_content    "price">

Parameter entities are dereferenced in the same way as a general entity reference, only with a percent sign instead of an ampersand:

<!ELEMENT apartment (%residential_content;, %rental_content;)>
<!ELEMENT sublet    (%residential_content;, %rental_content;)>
<!ELEMENT coop      (%residential_content;, %purchase_content;)>
<!ELEMENT condo     (%residential_content;, %purchase_content;)>
<!ELEMENT house     (%residential_content;, %purchase_content;)>

When the parser reads these declarations, it substitutes the entity's replacement text for the entity reference. Now all you have to do to add an available_date element to the content specification of all five listing types is add it to the residential_content entity like this:

<!ENTITY % residential_content "address, footage, rooms,
                                baths, available_date">

The same technique works equally well for attribute types and element names. You'll see several examples of this in the next chapter on namespaces and in Chapter 9.

This trick is limited to external DTDs. Internal DTD subsets do not allow parameter entity references to be only part of a markup declaration. However, parameter entity references can be used in internal DTD subsets to insert one or more entire markup declarations, typically through external parameter entities.

3.7.2. Redefining Parameter Entities

What makes parameter entity references particularly powerful is that they can be redefined. If a document uses both internal and external DTD subsets, then the internal DTD subset can specify new replacement text for the entities. If ELEMENT and ATTLIST declarations in the external DTD subset are written indirectly with parameter entity references instead of directly with literal element names, the internal DTD subset can change the DTD for the document. For instance, a single document could add a bedrooms child element to the listings by redefining the residential_content entity like this:

<!ENTITY % residential_content "address, footage, rooms,
                                bedrooms, baths, available_date">

In the event of conflicting entity declarations, the first one encountered takes precedence. The parser reads the internal DTD subset first. Thus the internal definition of the residential_content entity is used. When the parser reads the external DTD subset, every declaration that uses the residential_content entity will contain a bedrooms child element it wouldn't otherwise have.

Modular XHTML, which we'll discuss in Chapter 7, makes heavy use of this technique to allow particular documents to select only the subset of HTML that they actually need.

3.7.3. External DTD Subsets

Real-world DTDs can be quite complex. The SVG DTD is over 1,000 lines long. The XHTML 1.0 strict DTD (the smallest of the three XHTML DTDs) is more than 1,500 lines long. And these are only medium-sized DTDs. The DocBook XML DTD is over 11,000 lines long. It can be hard to work with, comprehend, and modify such a large DTD when it's stored in a single monolithic file.

Fortunately, DTDs can be broken up into independent pieces. For instance, the DocBook DTD is distributed in 28 separate pieces covering different parts of the spec: one for tables, one for notations, one for entity declarations, and so on. These different pieces are then combined at validation time using external parameter entity references.

An external parameter entity is declared using a normal ENTITY declaration with a % sign just like a normal parameter entity. However, rather than including the replacement text directly, the declaration contains the SYSTEM keyword followed by a URI to the DTD piece it wants to include. For example, the following ENTITY declaration defines an external entity called names whose content is taken from the file at the relative URI names.dtd. Then the parameter entity reference %names; inserts the contents of that file into the current DTD.

<!ENTITY % names SYSTEM "names.dtd">
%names;

You can use either relative or absolute URIs as convenient. In most situations, relative URIs are more practical.

Previous | Home | Next
3.6. External Unparsed Entities and Notations | Book Index | 3.8. Conditional Inclusion
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Conditional Inclusion (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.8. Conditional Inclusion

XML offers the IGNORE directive for the purpose of "commenting out" a section of declarations. For example, a parser will ignore the following declaration of a production_note element, as if it weren't in the DTD at all:

<![IGNORE[
  <!ELEMENT production_note (#PCDATA)>
]]>

This may not seem particularly useful. After all, you could always simply use an XML comment to comment out the declarations you want to remove temporarily from the DTD. If you feel that way, the INCLUDE directive is going to seem even more pointless. Its purpose is to indicate that the given declarations are actually used in the DTD. For example:

<![INCLUDE[
  <!ELEMENT production_note (#PCDATA)>
]]>

This has exactly the same effect and meaning as if the INCLUDE directive were not present. However, now consider what happens if we don't use INCLUDE and IGNORE directly. Instead, suppose we define a parameter entity like this:

<!ENTITY % notes_allowed "INCLUDE">

Then we use a parameter entity reference instead of the keyword:

<![%notes_allowed;[
  <!ELEMENT production_note (#PCDATA)>
]]>

The notes_allowed parameter entity can be redefined from outside this DTD. In particular, it can be redefined in the internal DTD subset of a document. This provides a switch individual documents can use to turn the production_note declaration on or off. This technique allows document authors to select only the functionality they need from the DTD.

Previous | Home | Next
3.7. Parameter Entities | Book Index | 3.9. Two DTD Examples
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Two DTD Examples (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.9. Two DTD Examples

Some of the best techniques for DTD design only become apparent when you look at larger documents. In this section, we'll develop DTDs that cover the two different document formats for describing people that were presented in Example 2-4 and Example 2-5 of the last chapter.

3.9.1. Data-Oriented DTDs

Data- oriented DTDs are very straightforward. They make heavy use of sequences, occasional use of choices, and almost no use of mixed content. Example 3-6 shows such a DTD. Since this is a small example, and since it's easier to understand when both the document and the DTD are on the same page, we've made this an internal DTD included in the document. However, it would be easy to take it out and put it in a separate file.

Example 3-6. A flexible yet data-oriented DTD describing people

<?xml version="1.0"?>
<!DOCTYPE person  [
  <!ELEMENT person (name+, profession*)>
  <!ELEMENT name EMPTY>
  <!ATTLIST name first CDATA #REQUIRED
                 last  CDATA #REQUIRED>
  <!-- The first and last attributes are required to be present
       but they may be empty. For example,
       <name first="Cher" last=""> -->
  <!ELEMENT profession EMPTY>
  <!ATTLIST profession value CDATA #REQUIRED>
]>
<person>
  <name first="Alan" last="Turing"/>
  <profession value="computer scientist"/>
  <profession value="mathematician"/>
  <profession value="cryptographer"/>
</person>

The DTD here is contained completely inside the internal DTD subset. First a person ELEMENT declaration states that each person must have one or more name children, and zero or more profession children, in that order. This allows for the possibility that a person changes his name or uses aliases. It assumes that each person has at least one name but may not have a profession.

This declaration also requires that all name elements precede all profession elements. Here the DTD is less flexible than it ideally would be. There's no particular reason that the names have to come first. However, if we were to allow more random ordering, it would be hard to say that there must be at least one name. One of the weaknesses of DTDs is that it occasionally forces extra sequence order on you when all you really need is a constraint on the number of some element. Schemas are more flexible in this regard.

Both name and profession elements are empty so their declarations are very simple. The attribute declarations are a little more complex. In all three cases the form of the attribute is open, so all three attributes are declared to have type CDATA. All three are also required. However, note the use of comments to suggest a solution for edge cases such as celebrities with no last names. Comments are an essential tool for making sense of otherwise obfuscated DTDs.

3.9.2. Narrative-Oriented DTDs

Narrative-oriented DTDs tend be a lot looser and make much heavier use of mixed content than do DTDs that describe more database-like documents. Consequently, they tend to be written from the bottom up, starting with the smallest elements and building up to the largest. They also tend to use parameter entities to group together similar content specifications and attribute lists.

Example 3-7 is a standalone DTD for biographies like the one shown in Example 2-5 of the last chapter. Notice that not everything it declares is actually present in Example 2-5. That's often the case with narrative documents. For instance, not all web pages contain unordered lists, but the XHTML DTD still needs to declare the ul element for those XHTML documents that do include them. Also, notice that a few attributes present in Example 2-5 have been made into fixed defaults here. Thus, they could be omitted from the document itself, once it is attached to this DTD.

Example 3-7. A narrative-oriented DTD for biographies

<!ATTLIST biography xmlns:xlink CDATA #FIXED
                                       "http://www.w3.org/1999/xlink">

<!ELEMENT person (first_name, last_name)>
<!-- Birth and death dates are given in the form yyyy/mm/dd -->
<!ATTLIST person born CDATA #IMPLIED
                 died CDATA #IMPLIED>

<!ELEMENT date   (month, day, year)>
<!ELEMENT month  (#PCDATA)>
<!ELEMENT day    (#PCDATA)>
<!ELEMENT year   (#PCDATA)>

<!-- xlink:href must contain a URI.-->
<!ATTLIST emphasize xlink:type (simple) #IMPLIED
                    xlink:href CDATA   #IMPLIED>

<!ELEMENT profession (#PCDATA)>
<!ELEMENT footnote   (#PCDATA)>

<!-- The source is given according to the Chicago Manual of Style
     citation conventions -->
<!ATTLIST footnote source CDATA #REQUIRED>

<!ELEMENT first_name (#PCDATA)>
<!ELEMENT last_name  (#PCDATA)>

<!ELEMENT image EMPTY>
<!ATTLIST image source CDATA   #REQUIRED
                width  NMTOKEN #REQUIRED
                height NMTOKEN #REQUIRED
                ALT    CDATA   #IMPLIED
>
<!ENTITY % top_level "( #PCDATA | image | paragraph | definition
                      | person | profession | emphasize | last_name
                      | first_name | footnote | date )*">

<!ELEMENT paragraph  %top_level; >
<!ELEMENT definition %top_level; >
<!ELEMENT emphasize  %top_level; >
<!ELEMENT biography  %top_level; >

The root biography element has a classic mixed-content declaration. Since there are several elements that can contain other elements in a fairly unpredictable fashion, we group all the possible top-level elements (elements that appear as immediate children of the root element) in a single top_level entity reference. Then we can make all of them potential children of each other in a straightforward way. This also makes it much easier to add new elements in the future. That's important since this one small example is almost certainly not broad enough to cover all possible biographies.

Previous | Home | Next
3.8. Conditional Inclusion | Book Index | 3.10. Locating Standard DTDs
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Locating Standard DTDs (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

3.10. Locating Standard DTDs

DTDs and validity are most important when you're exchanging data with others ; they let you verify that you're sending what the receiver expects and vice versa. Of course, this works best if both ends of a conversation agree on which DTD and vocabulary they will use. There are many standard DTDs for different professions and disciplines and more are created every day. It is often better to use an established DTD and vocabulary than to design your own.

However, there is no agreed-upon, central repository that documents and links to such efforts. We know of at least three attempts to create such a central repository. These are:

  • James Tauber's schema.net at http://www.schema.net/

  • OASIS's xml.org at http://www.xml.org/xml/registry.jsp

  • Microsoft's Biztalk initiative at http://www.biztalk.org (registration required)

However, none of these has succeeded in establishing itself as the standard place to list DTDs and none of them cover more than a minority of the existing public DTDs. Indeed, probably the largest list of DTDs online does not even attempt to be a general repository, instead being simply the result of collecting XML and SGML news for several years. This is Robin Cover's list of XML applications at http://www.oasis-open.org/cover/siteIndex.html#toc-applications.

The W3C is one of the most prolific producers of standard XML DTDs. It has moved almost all of its future development to XML including SVG, the Platform for Internet Content Selection (PICS), the Resource Description Framework (RDF), the Mathematical Markup Language (MathML), and even HTML itself. DTDs for these XML applications are generally published as appendixes to the specifications for the applications. The specifications are all found at http://www.w3.org/TR/.

However, XML isn't just for the Web, and far more activity is going on outside the W3C than inside it. Generally, within any one field, you should look to that field's standards bodies for DTDs relating to that area of interest. For example, the American Institute of Certified Public Accountants has published a DTD for XFRML, the Extensible Financial Reporting Markup Language. The Object Management Group (OMG) has published a DTD for describing Unified Modeling Language (UML) diagrams in XML. The Society of Automotive Engineers has published an XML application for emissions information as required by the 1990 U.S. Clean Air Act. Chances are that in any industry that makes heavy use of information technology, some group or groups, either formal or informal, are already working on DTDs that cover parts of that industry.

Previous | Home | Next
3.9. Two DTD Examples | Book Index | 4. Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespaces (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 4. Namespaces

Contents:

The Need for Namespaces
Namespace Syntax
How Parsers Handle Namespaces
Namespaces and DTDs

Namespaces have two purposes in XML:

  1. To distinguish between elements and attributes from different vocabularies with different meanings and that happen to share the same name.

  2. To group all the related elements and attributes from a single XML application together so that software can easily recognize them.

The first purpose is easier to explain and to grasp, but the second purpose is more important in practice.

Namespaces are implemented by attaching a prefix to each element and attribute. Each prefix is mapped to a URI by an xmlns:prefix attribute. Default URIs can also be provided for elements that don't have a prefix by xmlns attributes. Elements and attributes that are attached to the same URI are in the same namespace. Elements from many XML applications are identified by standard URIs.

4.1. The Need for Namespaces

Some documents combine markup from multiple XML applications. For example, an XHTML document may contain both SVG pictures and MathML equations. An XSLT stylesheet will contain both XSLT instructions and elements from the result-tree vocabulary. And XLinks are always symbiotic with the elements of the document in which they appear since XLink itself doesn't define any elements, only attributes.

In some cases, these applications may use the same name to refer to different things. For example, in SVG a set element sets the value of an attribute for a specified duration of time, while in MathML a set element represents a mathematical set such as the set of all positive even numbers. It's essential to know when you're working with a MathML set and when you're working with an SVG set. Otherwise, validation, rendering, indexing, and many other tasks will get confused and fail.

Consider Example 4-1. This is a simple list of paintings including the title of each painting, the date each was painted, the artist who painted it, and a description of the painting.

Example 4-1. A list of paintings

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<catalog>

  <painting>
    <title>Memory of the Garden at Etten</title>
    <artist>Vincent Van Gogh</artist>
    <date>November, 1888</date>
    <description>
      Two women look to the left. A third works in her garden.
    </description>
  </painting>

  <painting>
    <title>The Swing</title>
    <artist>Pierre-Auguste Renoir</artist>
    <date>1876</date>
    <description>
      A young girl on a swing. Two men and a toddler watch.
    </description>
  </painting>

  <!-- Many more paintings... -->

</catalog>

Now suppose that Example 4-1 is to be served as a web page and you want to make it accessible to search engines. One possibility is to use the Resource Description Framework (RDF) to embed metadata in the page. This describes the page for any search engines or other robots that might come along. Using the Dublin Core metadata vocabulary (http://purl.oclc.org/dc/), a standard vocabulary for library-catalog-style information that can be encoded in XML or other syntaxes, an RDF description of this page might look something like this:

<RDF>
  <Description
     about="http://www.cafeconleche.org/examples/impressionists.xml">
    <title> Impressionist Paintings </title>
    <creator> Elliotte Rusty Harold </creator>
    <description>
      A list of famous impressionist paiintings organized
      by painter and date
    </description>
    <date>2000-08-22</date>
  </Description>
</RDF>

Here we've used the Description and RDF elements from RDF and the title, creator, description, and date elements from the Dublin Core. We have no choice about these names; they are established by their respective specifications. If we want standard software, which understands RDF and the Dublin Core, to understand our documents, then we have to use these names. Example 4-2 combines this description with the actual list of paintings.

Example 4-2. A list of paintings including catalog information about the list

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<catalog>

  <RDF>
    <Description
        about="http://www.cafeconleche.org/examples/impressionists.xml">
      <title> Impressionist Paintings </title>
      <creator> Elliotte Rusty Harold </creator>
      <description>
        A list of famous impressionist paintings organized
        by painter and date
      </description>
      <date>2000-08-22</date>
    </Description>
  </RDF>

  <painting>
    <title>Memory of the Garden at Etten</title>
    <artist>Vincent Van Gogh</artist>
    <date>November, 1888</date>
    <description>
      Two women look to the left. A third works in her garden.
    </description>
  </painting>

  <painting>
    <title>The Swing</title>
    <artist>Pierre-Auguste Renoir</artist>
    <date>1876</date>
    <description>
      A young girl on a swing. Two men and a toddler watch.
    </description>
  </painting>

  <!-- Many more paintings... -->

</catalog>

Now we have a problem. Several elements have been overloaded with different meanings in different parts of the document. The title element is used for both the title of the page and the title of a painting. The date element is used for both the date the page was written and the date the painting was painted. One description element describes pages, while another describes paintings.

This presents all sorts of problems. Validation is difficult because catalog and Dublin Core elements with the same name have different content specifications. Web browsers may want to hide the page description while showing the painting description, but not all stylesheet languages can tell the difference between the two. Processing software may understand the date format used in the Dublin Core date element, but not the more free-form format used in the painting date element.

We could change the names of the elements from our vocabulary, painting_title instead of title, date_painted instead of date, and so on. However, this is inconvenient if you already have a lot of documents marked up in the old version of the vocabulary. And it may not be possible to do this in all cases, especially if the name collisions occur not because of conflicts between your vocabulary and a standard vocabulary, but because of conflicts between two or more standard vocabularies. For instance, RDF just barely avoids a collision with the Dublin Core over the Description and description elements.

In other cases, there may not be any name conflicts, but it may still be important for software to determine quickly and decisively to which XML application a given element or attribute belongs. For instance, an XSLT processor needs to distinguish between XSLT instructions and literal result-tree elements.

Previous | Home | Next
3.10. Locating Standard DTDs | Book Index | 4.2. Namespace Syntax
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespace Syntax (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

4.2. Namespace Syntax

Namespaces disambiguate elements with the same name from each other by assigning elements and attributes to URIs. Generally, all the elements from one XML application are assigned to one URI, and all the elements from a different XML application are assigned to a different URI. These URIs are sometimes called namespace names. The URIs partition the elements and attributes into disjoint sets. Elements with the same name but different URIs are different elements. Elements with the same name and the same URIs are the same. Most of the time there's a one-to-one mapping between namespaces and XML applications, though a few applications use multiple namespaces to subdivide different parts of the application. For instance, XSL uses different namespaces for XSL Transformations (XSLT) and XSL Formatting Objects (XSL-FO).

4.2.1. Qualified Names, Prefixes, and Local Parts

Since URIs frequently contain characters such as /, %, and ~ that are not legal in XML names, short prefixes such as rdf and xsl stand in for them in element and attribute names. Each prefix is associated with a URI. Names whose prefixes are associated with the same URI are in the same namespace. Names whose prefixes are associated with different URIs are in different namespaces. Prefixed elements and attributes in namespaces have names that contain exactly one colon. They look like this:

rdf:description
xlink:type
xsl:template

Everything before the colon is called the prefix. Everything after the colon is called the local part. The complete name including the colon is called the qualified name, QName, or raw name. The prefix identifies the namespace to which the element or attribute belongs. The local part identifies the particular element or attribute within the namespace.

In a document that contains both SVG and MathML set elements, one could be an svg:set element, and the other could be a mathml:set element. Then there'd be no confusion between them. In an XSLT stylesheet that transforms documents into XSL formatting objects, the XSLT processor would recognize elements with the prefix xsl as XSLT instructions and elements with the prefix fo as literal result elements.

Prefixes may be composed from any legal XML name character except the colon. Prefixes beginning with the three letters xml (in any combination of case) are reserved for use by XML and its related specifications. Otherwise, you're free to name your prefixes in any way that's convenient. One further restriction namespaces add to XML 1.0 is that the local part may not contain any colons. In short, the only legal uses of a colon in XML are to separate a namespace prefix from the local part in a qualified name or for the attributes XML itself defines, such as xml:space and xml:lang.

4.2.2. Binding Prefixes to URIs

Each prefix in a qualified name must be associated with a URI. For example, all XSLT elements are associated with the http://www.w3.org/1999/XSL/Transform URI. The customary prefix xsl is used in place of the longer URI http://www.w3.org/1999/XSL/Transform.

TIP: You can't use the URI in the name directly. For one thing, the slashes in most URIs aren't legal characters in XML names. However, it's occasionally useful to refer to the full name without assuming a particular prefix. One convention used on many XML mailing lists and in XML documentation is to enclose the URI in curly braces and prefix it to the name. For example, the qualified name xsl:template might be written as the full name {http://www.w3.org/1999/XSL/Transform}template. Another convention is to append the local name to the namespace name after a sharp sign so that it becomes a URI fragment identifier. For example, http://www.w3.org/1999/XSL/Transform#template. However, both forms are only conveniences for communication among human beings when the URI is important but the prefix isn't. Neither an XML parser nor an XSLT processor will accept or understand the long forms.

Prefixes are bound to namespace URIs by attaching an xmlns:prefix attribute to the prefixed element or one of its ancestors. (The prefix should be replaced by the actual prefix used.) For example, the xmlns:rdf attribute of this rdf:RDF element binds the prefix rdf to the namespace URI http://www.w3.org/TR/REC-rdf-syntax#:

<rdf:RDF xmlns:rdf="http://www.w3.org/TR/REC-rdf-syntax#">
 <rdf:Description
      about="http://www.cafeconleche.org/examples/impressionists.xml">
    <title> Impressionist Paintings </title>
    <creator> Elliotte Rusty Harold </creator>
    <description>
      A list of famous impressionist paintings organized
      by painter and date
    </description>
    <date>2000-08-22</date>
  </rdf:Description>
</rdf:RDF>

Bindings have scope within the element where they're declared and within its contents. The xmlns:rdf attribute declares the rdf prefix for the rdf:RDF element, as well as its child elements. An RDF processor will recognize rdf:RDF and rdf:Description as RDF elements because both have prefixes bound to the particular URI specified by the RDF specification. It will not consider the title, creator, description, and date elements to be RDF elements because they do not have prefixes bound to the http://www.w3.org/TR/REC-rdf-syntax# URI.

The prefix can be declared in the topmost element that uses the prefix or in any ancestor thereof. This may be the root element of the document, or it may be an element at a lower level. For instance, the Dublin Core elements could be attached to the http://purl.org/dc/ namespace by adding an xmlns:dc attribute to the rdf:Description element, as shown in Example 4-3, since all Dublin Core elements in this document appear inside a single rdf:Description element. In other documents that spread the elements out more, it might be more convenient to put the namespace declaration on the root element. If necessary, a single element can include multiple namespace declarations for different namespaces.

Example 4-3. A document containing both SVG and XLinks

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<catalog>

  <rdf:RDF xmlns:rdf="http://www.w3.org/TR/REC-rdf-syntax#">
    <rdf:Description xmlns:dc="http://purl.org/dc/"
       about="http://www.cafeconleche.org/examples/impressionists.xml">
      <dc:title> Impressionist Paintings </dc:title>
      <dc:creator> Elliotte Rusty Harold </dc:creator>
      <dc:description>
        A list of famous impressionist paintings organized
        by painter and date
      </dc:description>
      <dc:date>2000-08-22</dc:date>
    </rdf:Description>
  </rdf:RDF>

  <painting>
    <title>Memory of the Garden at Etten</title>
    <artist>Vincent Van Gogh</artist>
    <date>November, 1888</date>
    <description>
      Two women look to the left. A third works in her garden.
    </description>
  </painting>

  <painting>
    <title>The Swing</title>
    <artist>Pierre-Auguste Renoir</artist>
    <date>1876</date>
    <description>
      A young girl on a swing. Two men and a toddler watch.
    </description>
  </painting>

  <!-- Many more paintings... -->

</catalog>

A DTD for this document can include different content specifications for the dc:description and description elements. A stylesheet can attach different styles to dc:title and title. Software that sorts the catalog by date can pay attention to the date elements and ignore the dc:date elements.

In this example, the elements without prefixes, such as catalog, painting, description, artist, and title, are not in any namespace. Furthermore, unprefixed attributes (such as the about attribute of rdf:Description in the previous example) are never in any namespace. Being an attribute of an element in the http://www.w3.org/TR/REC-rdf-syntax# namespace is not sufficient to put the attribute in the http://www.w3.org/TR/REC-rdf-syntax# namespace. The only way an attribute belongs to a namespace is if it has a declared prefix, like xlink:type and xlink:href.

It is possible to redefine a prefix within a document so that in one element the prefix refers to one namespace URI, while in another element it refers to a different namespace URI. In this case, the closest ancestor element that declares the prefix takes precedence. However, in most cases redefining prefixes is a very bad idea that only leads to confusion and is not something you should actually do.

4.2.3. Namespace URIs

Many XML applications have customary prefixes. For example, SVG elements often use the prefix svg, and RDF elements often have the prefix rdf. However, these prefixes are simply conventions and can be changed based on necessity, convenience, or whim. Before a prefix can be used, it must be bound to a URI like http://www.w3.org/2000/svg or http://www.w3.org/1999/02/22-rdf-syntax-ns#. It is these URIs that are standardized, not the prefixes. The prefix can change as long as the URI stays the same. An RDF processor looks for the RDF URI, not any particular prefix. As long as nobody outside the w3.org domain uses namespace URIs in the w3.org domain, and as long as the W3C can keep a careful eye on what its people are using for namespaces, all conflicts can be avoided.

Namespace URIs do not necessarily point to any actual document or page. In fact, they don't have to use the http scheme. They might even use some other protocol like mailto in which URIs don't even point to documents. However, if you're defining your own namespace using an http URI, it would not be a bad idea to place some documentation for the specification at the namespace URI. The W3C got tired of receiving broken-link reports for the namespace URIs in their specifications, so they added some simple pages at their namespace URIs. For more formal purposes that offer some hope of automated resolution and other features, you can place a Resource Directory Description Language (RDDL) document at the namespace URI. This possibility will be discussed further in Chapter 14. You are by no means required to do this, though. Many namespace URIs lead to 404-Not Found errors when you actually plug them into a web browser. Namespace URIs are purely formal identifiers. They are not the addresses of a page, and they are not meant to be followed as links.

Parsers compare namespace URIs on a character-by-character basis. If the URIs differ in even a single normally insignificant place, then they define separate namespaces. For instance, http://www.w3.org/1999/02/22-rdf-syntax-ns#, http://WWW.W3.ORG/1999/02/22-rdf-syntax-ns#, http://www.w3.org/1999/02/22-rdf-syntax-ns/, and http://www.w3.org/1999/02/22-rdf-syntax-ns/index.rdf all point to the same page. However, only the first is the correct namespace name for the RDF. These four URLs identify four separate namespaces.

4.2.4. Setting a Default Namespace with the xmlns Attribute

You often know that all the content of a particular element will come from a particular XML application. For instance, inside an SVG svg element, you're only likely to find other SVG elements. You can indicate that an unprefixed element and all its unprefixed descendant elements belong to a particular namespace by attaching an xmlns attribute with no prefix to the top element. For example:

<svg xmlns="http://www.w3.org/2000/svg"
     width="12cm" height="10cm">
  <ellipse rx="110" ry="130" />
  <rect x="4cm" y="1cm" width="3cm" height="6cm" />
</svg>

Here, although no elements have any prefixes, the svg, ellipse, and rect elements are in the http://www.w3.org/2000/svg namespace.

The attributes are a different story. Default namespaces only apply to elements, not to attributes. Thus in the previous example the width, height, rx, ry, x, and y attributes are not in any namespace.

You can change the default namespace within a particular element by adding an xmlns attribute to the element. Example 4-4 is an XML document that initially sets the default namespace to http://www.w3.org/1999/xhtml for all the XHTML elements. This namespace declaration applies within most of the document. However, the svg element has an xmlns attribute that resets the default namespace to http://www.w3.org/2000/svg for itself and its content. The XLink information is included in attributes, however, so these must be placed in the XLink namespace using explicit prefixes.

Example 4-4. An XML document that uses default namespaces

<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xlink="http://www.w3.org/1999/xlink">
  <head><title>Three Namespaces</title></head>
  <body>
    <h1 align="center">An Ellipse and a Rectangle</h1>
    <svg xmlns="http://www.w3.org/2000/svg"
         width="12cm" height="10cm">
      <ellipse rx="110" ry="130" />
      <rect x="4cm" y="1cm" width="3cm" height="6cm" />
    </svg>
    <p xlink:type="simple" xlink:href="ellipses.html">
      More about ellipses
    </p>
    <p xlink:type="simple" xlink:href="rectangles.html">
      More about rectangles
    </p>
    <hr/>
    <p>Last Modified May 13, 2000</p>
  </body>
</html>

The default namespace does not apply to any elements or attributes with prefixes. These still belong to whatever namespace to which their prefix is bound. However, an unprefixed child element of a prefixed element still belongs to the default namespace.

4.2.5. Attribute Declarations for xmlns

When namespaces are only being used to identify the elements and attributes from a particular XML application, and not to distinguish different elements with the same name, a DTD can attach a fixed xmlns attribute to the primary container elements for an application so that everything is placed in the right namespace without explicit xmlns attributes in the document. For example, this ATTLIST declaration fixes the default namespace of all svg elements as http://www.w3.org/2000/:

<!ATTLIST svg xmlns CDATA #FIXED "http://www.w3.org/2000/">

This allows you to omit xmlns attributes from all your svg elements.

A document does not need to be valid to take advantage of this. All that's required is that the parser read the DTD. All parsers will read the internal DTD subset and process any such ATTLIST declarations they find there. A few nonvalidating parsers may skip external DTD subsets, and thus get confused. Ideally, you should use a parser that can validate so that it will at least be able to read the external DTD subset, though you might use it with validation turned off.

Previous | Home | Next
4. Namespaces | Book Index | 4.3. How Parsers Handle Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Parsers Handle Namespaces (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

4.3. How Parsers Handle Namespaces

Namespaces are not part of XML 1.0. They were invented about a year after the original XML specification was released. However, care was taken to ensure backwards compatibility. Thus, an XML 1.0 parser that does not know about namespaces should not have any trouble reading a document that uses namespaces. Colons are legal characters in XML 1.0 element and attribute names. The parser will simply report that some of the names contain colons. Possible problems arise in the rare cases where different qualified names resolve to the same full name or where the same qualified name indicates a different full name in different parts of a document.

A namespace-aware parser does add a couple of checks to the normal well-formedness checks that a parser performs. Specifically, it checks to see that all prefixes are mapped to URIs. It will reject documents that use unmapped prefixes (except for xml and xmlns when used as specified in the XML 1.0 or Namespaces in XML specifications.) It will further reject any element or attribute names that contain more than one colon. Otherwise, it behaves almost exactly like a non-namespace-aware parser. Other software that sits on top of the raw XML parser, an XSLT engine for example, may treat elements differently depending on to which namespace they belong. However, the XML parser itself mostly doesn't care as long as all well-formedness and namespace constraints are met.

A possible exception occurs in the unlikely event that elements with different prefixes belong to the same namespace. In this case, a namespace-aware parser will report the elements as being the same, while a non-namespace-aware parser will report them as different. About equally unlikely is the case where two elements or attributes with the same qualified name are in different namespaces because the common prefix is bound to different URIs in different places in the document. Slightly more likely is the case where two unprefixed names are placed in different default namespaces. In both these cases, a namespace-aware processor will report them as different, whereas a non-namespace-aware processor will treat them the same. Many parsers let you turn namespace processing on or off as you see fit.

Previous | Home | Next
4.2. Namespace Syntax | Book Index | 4.4. Namespaces and DTDs
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespaces and DTDs (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

4.4. Namespaces and DTDs

Namespaces are completely independent of DTDs and can be used in both valid and invalid documents. A document can have a DTD but not use namespaces or use namespaces but not have a DTD. It can use both namespaces and DTDs or neither namespaces nor DTDs. Namespaces do not in any way change DTD syntax nor do they change the definition of validity. For instance, the DTD of a valid document that uses an element named dc:title must include an ELEMENT declaration properly specifying the content of the dc:title element. For example:

<!ELEMENT dc:title (#PCDATA)>

The name of the element in the document must exactly match the name of the element in the DTD including the prefix. The DTD cannot omit the prefix and simply declare a title element. The same is true of prefixed attributes. For instance, if an element used in the document has xlink:type and xlink:href attributes, then the DTD must declare the xlink:type and xlink:href attributes, not simply type and href.

Conversely, if an element uses an xmlns attribute to set the default namespace and does not attach prefixes to elements, then the names of the elements must be declared without prefixes in the DTD. The validator neither knows nor cares about the existence of namespaces. All it sees is that some element and attribute names happen to contain colons; as far as it's concerned, such names are perfectly valid as long as they're declared.

4.4.1. Parameter Entity References for Namespace Prefixes

Requiring DTDs to declare the prefixed names instead of the raw names or some combination of local part and namespace URI makes it difficult to change the prefix in valid documents. The problem is that changing the prefix requires changing all declarations that use that prefix in the DTD as well. However, with a little forethought, parameter entity references can alleviate the pain quite a bit.

The trick is to define as parameter entities both the namespace prefix and the colon that separates the prefix from the local name, like this:

<!ENTITY % dc-prefix "dc">
<!ENTITY % dc-colon ":">

The second step is to define the qualified names as more parameter entity references, like these:

<!ENTITY % dc-title       "%dc-prefix;%dc-colon;title">
<!ENTITY % dc-creator     "%dc-prefix;%dc-colon;creator">
<!ENTITY % dc-description "%dc-prefix;%dc-colon;description">
<!ENTITY % dc-date        "%dc-prefix;%dc-colon;date">
WARNING: Do not omit this step and try to use the dc-prefix and dc-colon parameter entities directly in ELEMENT and ATTLIST declarations. This will fail because XML parsers add extra space around the entity's replacement text when they're used outside another entity's replacement text.

Then you use the entity references for the qualified name in all declarations, like this:

<!ELEMENT %dc-title; (#PCDATA)>
<!ELEMENT %dc-creator; (#PCDATA)>
<!ELEMENT %dc-description; (#PCDATA)>
<!ELEMENT %dc-date; (#PCDATA)>
<!ELEMENT rdf:Description
  ((%dc-title; | %dc-creator; | %dc-description; | %dc-date; )*)
>

Now a document that needs to change the prefix simply changes the parameter entity definitions. In some cases, this will be done by editing the DTD directly. In others, it may be done by overriding the definitions in the document's own internal DTD subset. For example, to change the prefix from dc to dublin, you'd add this entity definition somewhere in the DTD before the normal definition:

<!ENTITY % dc-prefix "dublin">

If you wanted to use the default namespace instead of explicit prefixes, you'd redefine both the dc-prefix and dc-colon entities as the empty string, like this:

<!ENTITY % dc-prefix "">
<!ENTITY % dc-colon  "">
Previous | Home | Next
4.3. How Parsers Handle Namespaces | Book Index | 5. Internationalization
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Internationalization (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 5. Internationalization

Contents:

Character-Set Metadata
The Encoding Declaration
Text Declarations
XML-Defined Character Sets
Unicode
ISO Character Sets
Platform-Dependent Character Sets
Converting Between Character Sets
The Default Character Set for XML Documents
Character References
xml:lang

We've told you that XML documents contain text, but we haven't yet told you what kind of text they contain. In this chapter we rectify that omission. XML documents contain Unicode text. Unicode is a character set large enough to include all the world's living languages and a few dead ones. It can be written in a variety of encodings, including UCS-2 and the ASCII superset UTF-8. However, since Unicode text editors are relatively uncommon, XML documents may also be written in other character sets and encodings, which are converted to Unicode when the document is parsed. The encoding declaration specifies which character set a document uses. You can use character references, such as &#x03B8;, to insert Unicode characters like Figure that aren't available in the legacy character set in which a document is written.

Computers don't really understand text. They don't recognize the Latin letter Z, the Greek letter Figure , or the Han ideograph Figure . All a computer understands are numbers such as 90, 947, or 40,821. A character set maps particular characters, like Z, to particular numbers, like 90. These numbers are called code points. A character encoding determines how those code points are represented in bytes. For instance, the code point 90 can be encoded as a signed byte, a little-endian unsigned short, a 4-byte, two's complement, big-endian integer, or in some still more complicated fashion.

A human script like Cyrillic may be written in multiple character sets, such as KOI8-R, Unicode, or ISO-8859-5. A character set like Unicode may then be encoded in multiple encodings, such as UTF-8, UCS-2, or UTF-16. In general, however, simpler character sets like ASCII and KOI8-R have only one encoding.

5.1. Character-Set Metadata

Some environments keep track of which encodings in which particular documents are written. For instance, web servers that transmit XML documents precede them with an HTTP header that looks something like this:

HTTP/1.1 200 OK
Date: Sun, 28 Oct 2001 11:05:42 GMT
Server: Apache/1.3.19 (Unix) mod_jk mod_perl/1.25 mod_fastcgi/2.2.10 Connection: close
Transfer-Encoding: chunked
Content-Type: text/xml; charset=iso-8859-1

The Content-Type field of the HTTP header provides the MIME media type of the document. This may, as shown here, specify in which character set the document is written. An XML parser reading this document from a web server should use this information to determine the document's character encoding.

Many web servers omit the charset parameter from the MIME media type. In this case, if the MIME media type is text/xml , then the document is assumed to be in the us-ascii encoding. If the MIME media type is application/xml, then the parser attempts to guess the character set by reading the first few bytes of the document.

TIP: Since ASCII is almost never an appropriate character set for an XML document, application/xml is much preferred over text/xml. Unfortunately, most web servers including Apache 2.0.36 and earlier are configured to use text/xml by default. It's worth editing your mime.types file to fix this. Alternately, at least with Apache, if you don't have root access to your web server, you can use the AddType and AddCharset directives in your .htaccess files to override the server-wide defaults.

We've focused on MIME types in HTTP headers because that's the most common place where character-set metadata is applied to XML documents. However, MIME types are also used in some filesystems (e.g., the BeOS), in email, and in other environments. Other systems may provide other forms of character-set metadata. If such metadata is available for a document, whatever form it takes, the parser should use it, though in practice this is an area where not all parsers and programs are as conformant as they should be.

Previous | Home | Next
4.4. Namespaces and DTDs | Book Index | 5.2. The Encoding Declaration
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Encoding Declaration (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.2. The Encoding Declaration

Every XML document should have an encoding declaration as part of its XML declaration. The encoding declaration tells the parser in which character set the document is written. It's used only when other metadata from outside the file is not available. For example, this XML declaration says that the document uses the character encoding US-ASCII:

<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>

This one states that the document uses the Latin-1 character set, though it uses the more official name ISO-8859-1:

<?xml version="1.0" encoding="ISO-8859-1"?>

Even if metadata is not available, the encoding declaration can be omitted if the document is written in either the UTF-8 or UTF-16 encodings of Unicode. UTF-8 is a strict superset of ASCII, so ASCII files can be legal XML documents without an encoding declaration. Note, however, that this only applies to genuine, pure 7-bit ASCII files. It does not include the extended ASCII character sets that some editors produce with characters like ©, ç, or ".

Even if character-set metadata is available, many parsers ignore it. Thus, we highly recommend including an encoding declaration in all your XML documents that are not written in UTF-8 or UTF-16. It certainly never hurts to do so.

Previous | Home | Next
5. Internationalization | Book Index | 5.3. Text Declarations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Text Declarations (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.3. Text Declarations

XML documents may be composed of multiple parsed entities, as you learned in Chapter 3. These external parsed entities may be DTD fragments or chunks of XML that will be inserted into the master document using external general entity references. In either case, the external parsed entity does not necessarily use the same character set as the master document. Indeed, one external parsed entity may be referenced in several different files, each of which is written in a different character set. Therefore, it is important to specify the character set for an external parsed entity independently of the character set that the including document uses.

To accomplish this task, each external parsed entity should have a text declaration. If present, the text declaration must be the very first thing in the external parsed entity. For example, this text declaration says that the associated entity is encoded in the KOI8-R character set:

<?xml version="1.0" encoding="KOI8-R"?>

The text declaration looks like an XML declaration. It has version info and an encoding declaration. However, a text declaration may not have a standalone declaration. Furthermore, the version info may be omitted. A legal text declaration that specifies the encoding as KOI8-R might look like this:

<?xml encoding="KOI8-R"?>

However, it is not a legal XML declaration.

Example 5-1 shows an external parsed entity containing several verses from Pushkin's The Bronze Horseman in a Cyrillic script. The text declaration identifies the encoding as KOI8-R. Example 5-1 is not itself a well-formed XML document because it has no root element. It exists only for inclusion in other documents.

Example 5-1. An external parsed entity with a text declaration identifying the character set as KOI8-R

External DTD subsets reside in external parsed entities and, thus, may have text declarations. Indeed, they should have text declarations if they're written in a character set other than one of the Unicode's variants. Example 5-2 shows a DTD fragment written in KOI8-R that might be used to validate Example 5-1 after it is included as part of a larger document.

Example 5-2. A DTD with a text declaration identifying the character set as KOI8-R

Previous | Home | Next
5.2. The Encoding Declaration | Book Index | 5.4. XML-Defined Character Sets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML-Defined Character Sets (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.4. XML-Defined Character Sets

An XML parser is required to handle the UTF-16 and UTF-8 encodings or Unicode (about which more follows). However, XML parsers are allowed to understand and process many other character sets. In particular, the specification recommends that processors recognize and be able to read these encodings:

UTF-8 | UTF-16
ISO-10646-UCS-2 | ISO-10646-UCS-4
ISO-8859-1 | ISO-8859-2
ISO-8859-3 | ISO-8859-4
ISO-8859-5 | ISO-8859-6
ISO-8859-7 | ISO-8859-8
ISO-8859-9 | ISO-2022-JP
Shift_JIS | EUC-JP

Many XML processors understand other legacy encodings. For instance, processors written in Java often understand all character sets available in a typical Java virtual machine. For a list, see http://java.sun.com/products/jdk/1.3/docs/guide/intl/encoding.doc.html. Furthermore, some processors may recognize aliases for these encodings; both Latin-1 and 8859_1 are sometimes used as synonyms for ISO-8859-1. However, using these names limits your document's portability. We recommend that you use standard names for standard encodings. For encodings whose standard name isn't given by the XML 1.0 specification, use one of the names registered with the Internet Assigned Numbers Authority (IANA) and listed at ftp://ftp.isi.edu/in-notes/iana/assignments/character-sets. However, knowing the name of a character set and saving a file in that set does not mean that your XML parser can read such a file. XML parsers are only required to support UTF-8 and UTF-16. They are not required to support the hundreds of different legacy encodings used around the world.

Previous | Home | Next
5.3. Text Declarations | Book Index | 5.5. Unicode
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Unicode (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.5. Unicode

Unicode is an international standard character set that can be used to write documents in almost any language you're likely to speak, learn, or encounter in your lifetime, barring alien abduction. Version 3.2, the current version as of May, 2002, contains 95,156 characters from most of Earth's living languages as well as several dead ones. Unicode easily covers the Latin alphabet, in which most of this book is written. Unicode also covers Greek-derived scripts, including ancient and modern Greek and the Cyrillic scripts used in Serbia and much of the former Soviet Union. Unicode covers several ideographic scripts, including the Han character set used for Chinese and Japanese, the Korean Hangul syllabary, and phonetic representations of these languages, including Katakana and Hiragana. It covers the right-to-left Arabic and Hebrew scripts. It covers various scripts native to the Indian subcontinent, including Devanagari, Thai, Bengali, Tibetan, and many more. And that's still less than half of the scripts in Unicode 3.1.1. Probably less than one person in a thousand today speaks a language that cannot be reasonably represented in Unicode. In the future, Unicode will add still more characters, making this fraction even smaller. Unicode can potentially hold more than a million characters, but no one is willing to say in public where they think most of the remaining million characters will come from.[4]

[4]Privately, some developers are willing to admit that they're preparing for a day when we're part of a Galactic Federation of thousands of intelligent species.

The Unicode character set assigns characters to code points, that is, numbers. These numbers can then be encoded in a variety of schemes, including:

  • UCS-2

  • UCS-4

  • UTF-8

  • UTF-16

5.5.1. UCS-2 and UTF-16

UCS-2, also known as ISO-10646-UCS-2, is perhaps the most natural encoding of Unicode. It represents each character as a 2-byte, unsigned integer between 0 and 65,535. Thus the capital letter A, code point 65 in Unicode, is represented by the 2 bytes 00 and 41 (in hexadecimal). The capital letter B, code point 66, is represented by the 2 bytes 00 and 42. The 2 bytes 03 and A3 represent the capital Greek letter Figure , code point 931.

UCS-2 comes in two variations, big endian and little endian. In big-endian UCS-2, the most significant byte of the character comes first. In little-endian UCS-2, the order is reversed. Thus, in big-endian UCS-2, the letter A is #x0041.[5] In little-endian UCS-2, the bytes are swapped, and A is #x4100. In big-endian UCS-2, the letter B is #x0042; in little-endian UCS-2, it's #x4200. In big-endian UCS-2, the letter Figure is #x03A3; in little-endian UCS-2, it's #xA303. In this book we use big-endian notation, but parsers cannot assume this. They must be able to determine the endianness from the document itself.

[5]For reasons that will become apparent shortly, this book has adopted the convention that #x precedes hexadecimal numbers. Every two hexadecimal digits map to one byte.

To distinguish between big-endian and little-endian UCS-2, a document encoded in UCS-2 customarily begins with Unicode character #xFEFF, the zero-width nonbreaking space, more commonly called the byte-order mark. This character has the advantage of being invisible. Furthermore, if its bytes are swapped, the resulting #xFFFE character doesn't actually exist. Thus, a program can look at the first two bytes of a UCS-2 document and tell immediately whether the document is big endian, depending on whether those bytes are #xFEFF or #xFFFE.

UCS-2 has three major disadvantages, however:

  • Files containing mostly Latin text are about twice as large in UCS-2 as they are in a single-byte character set, such as ASCII or Latin-1.

  • UCS-2 is not backward or forward compatible with ASCII. Tools that are accustomed to single-byte character sets often can't process a UCS-2 file in a reasonable way, even if the file only contains characters from the ASCII character set. For instance, a program written in C that expects the zero byte to terminate strings will choke on a UCS-2 file containing mostly English text because almost every other byte is zero.

  • UCS-2 is limited to 65,536 characters.

The last problem isn't so important in practice, since the first 65,536 code points of Unicode nonetheless manage to cover most people's needs except for dead languages like Ugaritic and fictional scripts like Tengwar. Mathematical symbols are also encountering these issues. Unicode does, however, provide a means of representing code points beyond 65,535 by recognizing certain two-byte sequences as half of a surrogate pair. A Unicode document that uses UCS-2 plus surrogate pairs is said to be in the UTF-16 encoding.

The other two problems, however, are more likely to affect most developers. UTF-8 is an alternative encoding for Unicode that addresses both.

5.5.2. UTF-8

UTF-8 is a variable-length encoding of Unicode. Characters 0 through 127, that is, the ASCII character set, are encoded in 1 byte each, exactly as they would be in ASCII. In ASCII, the byte with value 65 represents the letter A. In UTF-8, the byte with the value 65 also represents the letter A. There is a one-to-one identity mapping from ASCII characters to UTF-8 bytes. Thus, pure ASCII files are also acceptable UTF-8 files.

UTF-8 represents the characters from 128 to 2047, a range that covers the most common non-ideographic scripts, in two bytes each. Characters from 2048 to 65,535, mostly from Chinese, Japanese, and Korean, are represented in three bytes each. Characters with code points above 65,535 are represented in four bytes each. For a file that's mostly Latin text, this effectively halves the file size from what it would be in UCS-2. However, for a file that's primarily Japanese, Chinese, or Korean, the file size can grow by 50%. For most other living languages, the file size is close to the same.

UTF-8 is probably the most broadly supported encoding of Unicode. For instance, it's how Java .class files store strings; it's the native encoding of the BeOS, and it's the default encoding an XML processor assumes unless told otherwise by a byte-order mark or an encoding declaration. Chances are pretty good that if a program tells you it's saving Unicode, it's really saving UTF-8.

Previous | Home | Next
5.4. XML-Defined Character Sets | Book Index | 5.6. ISO Character Sets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


ISO Character Sets (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.6. ISO Character Sets

Unicode has only recently become popular. Previously, the space and processing costs associated with Unicode files prompted vendors to prefer smaller, single-byte character sets that could only handle English and a few other languages of interest, but not the full panoply of human language. The International Standards Organization (ISO) has standardized 14 of these character sets as ISO standard 8859. For all of these single-byte character sets, characters 0 through 127 are identical to the ASCII character set; characters 128 through 159 are the C1 controls; and characters 160 through 255 are the additional characters needed for scripts such as Greek, Cyrillic, and Turkish.

ISO-8859-1 (Latin-1)
ASCII plus the accented letters and other characters needed for most Latin-alphabet Western European languages, including Danish, Dutch, Finnish, French, German, Icelandic, Italian, Norwegian, Portuguese, Spanish, and Swedish.

ISO-8859-2 (Latin-2)
ASCII plus the accented letters and other characters needed to write most Latin-alphabet Central and Eastern European languages, including Czech, English, German, Hungarian, Polish, Romanian, Croatian, Slovak, Slovenian, and Sorbian.

ISO-8859-3 (Latin-3)
ASCII plus the accented letters and other characters needed to write Esperanto, Maltese, and Turkish.

ISO-8859-4 (Latin-4)
ASCII plus the accented letters and other characters needed to write most Baltic languages including Estonian, Latvian, Lithuanian, Greenlandic, and Lappish. Now deprecated. New applications should use 8859-10 (Latin-6) or 8859-13 (Latin-7) instead.

ISO-8859-5
ASCII plus the Cyrillic alphabet used for Russian and many other languages of the former Soviet Union and other Slavic countries, including Bulgarian, Byelorussian, Macedonian, Serbian, and Ukrainian.

ISO-8859-6
ASCII plus basic Arabic. However, the character set doesn't have the extra letters needed for non-Arabic languages written in the Arabic script, such as Farsi and Urdu.

ISO-8859-7
ASCII plus modern Greek. This set does not have the extra letters and accents necessary for ancient and Byzantine Greek.

ISO-8859-8
ASCII plus the Hebrew script used for Hebrew and Yiddish.

ISO-8859-9 (Latin-5)
Essentially the same as Latin-1, except six letters used in Icelandic have been replaced with six letters used in Turkish.

ISO-8859-10 (Latin-6)
ASCII plus accented letters and other characters needed to write most Baltic languages, including Estonian, Icelandic, Latvian, Lithuanian, Greenlandic, and Lappish.

ISO-8859-11
ASCII plus Thai.

ISO-8859-13 (Latin-7)
Yet another attempt to cover the Baltic region properly. Very similar to Latin-6, except for some question marks.

ISO-8859-14 (Latin-8)
ASCII plus the Celtic languages, including Gaelic and Welsh.

ISO-8859-15 (Latin-9, Latin-0)
A revised version of Latin-1 that replaces some unnecessary symbols, such as 1/4, with extra French and Finnish letters. Instead of the international currency sign, these sets include the Euro sign Figure .

ISO-8859-16, (Latin-10)
A revised version of Latin-2 that works better for Romanian. Other languages supported by this character set include Albanian, Croatian, English, Finnish, French, German, Hungarian, Italian, Polish, and Slovenian.

Various national standards bodies have produced other character sets to cover scripts and languages of interest within their geographic and political boundaries. For example, the Korea Industrial Standards Association developed the KS C 5601-1992 standard for encoding Korean. These national standard character sets can be used in XML documents as well, provided that you include the proper encoding declaration in the document and your parser knows how to translate these character sets into Unicode.

Previous | Home | Next
5.5. Unicode | Book Index | 5.7. Platform-Dependent Character Sets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Platform-Dependent Character Sets (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.7. Platform-Dependent Character Sets

In addition to the standard character sets discussed previously, many vendors have at one time or another produced proprietary character sets to meet the needs of their specific platform. Often, they contain special characters the vendor saw a need for, such as Apple's trademarked open apple Figure or the box-drawing characters such as Figure and Figure used for cell boundaries in early DOS spreadsheets. Microsoft, IBM, and Apple are the three most prolific inventors of character sets. The single most common such set is probably Microsoft's Cp1252, a variant of Latin-1 that replaces the C1 controls with more graphic characters. Hundreds of such platform-dependent character sets are in use today. Documentation for these ranges from excellent to nonexistent.

Platform-specific character sets like these should be used only within a single system. They should never be placed on the wire or used to transfer data between systems. Doing so can lead to nasty surprises in unexpected places. For example, displaying a file that contains some of the extra Cp1252 characters Figure , Figure , ^, Figure , ", Figure , ..., Figure , Figure , Figure , ·, `, ', ", ", -, --, Figure , Figure , ™, Figure , and ~ on a VT-220 terminal can effectively disable the screen. Nonetheless, these character sets are in common use and often seen on the Web even when they don't belong there. There's no absolute rule that says you can't use them for an XML document, provided that you include the proper encoding declaration and your parser understands it. The one advantage to using these sets is that existing text editors are likely to be much more comfortable with them than with Unicode and its friends. Nonetheless, we strongly recommend that you don't use them and stick to the documented standards that are much more broadly supported across platforms.

5.7.1. Cp1252

The most common platform-dependent character set, and the one you're most likely to encounter on the Internet, is Cp1252, also (and incorrectly) known as Windows ANSI. This is the default character set used by most American and Western European Windows PCs, which explains its ubiquity. Cp1252 is a single-byte character set almost identical to the standard ISO-8859-1 character set--indeed, many Cp1252 documents are often incorrectly labeled as being Latin-1 documents. However, this set replaces the C1 controls between code points 128 and 159 with additional graphics characters, such as Figure , Figure , and Figure . These characters won't cause problems on other Windows systems. However, other platforms will have difficulty viewing them properly and may even crash in extreme cases. Cp1252 (and its siblings used in non-Western Windows systems) should be avoided.

5.7.2. MacRoman

The Mac OS uses a different nonstandard, single-byte character set that's a superset of ASCII. The version used in the Americas and most of Western Europe is called MacRoman. Variants for other countries include MacGreek, MacHebrew, MacIceland, and so forth. Most Java-based XML processors can make sense out of these encodings if they're properly labeled, but most other non-Macintosh tools cannot.

For instance, if the French sentence "Au cours des dernières années, XML a été adapte dans des domaines aussi diverse que l'aéronautique, le multimédia, la gestion de hôpitaux, les télécommunications, la théologie, la vente au détail et la littérature médiévale" is written on a Macintosh and then read on a PC, what the PC user will see is "Au cours des derni?res annžes, XML a žtž adapte dans des domaines aussi diverse que l'ažronautique, le multimždia, la gestion de h™pitaux, les tžlžcommunications, la thžologie, la vente au džtail et la littžrature mždižvale," not the same thing at all. Generally, the result is at least marginally intelligible if most of the text is ASCII, but it certainly doesn't lend itself to high fidelity or quality. Mac-specific character sets should also be avoided.

Previous | Home | Next
5.6. ISO Character Sets | Book Index | 5.8. Converting Between Character Sets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Converting Between Character Sets (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.8. Converting Between Character Sets

The ultimate solution to this character set morass is to use Unicode in either UTF-16 or UTF-8 format for all your XML documents. An increasing number of tools support one of these two formats natively; even the unassuming Notepad offers an option to save files in Unicode in Windows NT 4.0 and 2000. Microsoft Word 97 and later saves the text of its documents in Unicode, though unlike XML documents, Word files are hardly pure text. Much of the binary data in a Word file is not Unicode or any other kind of text. However, Word 2000 can actually save plain text files into Unicode and is the authors' Unicode editor of choice these days when we need to type a document in several different, complex scripts. To save Word 2000 as plain Unicode text, select the format Encoded Text from the Save As Type: Choice menu in Word's Save As dialog box. Then select one of the four Unicode formats in the resulting File Conversion dialog box.

Nonetheless, most of our current tools are still adapted primarily for vendor-specific character sets that can't handle more than a few languages at one time. Thus, learning how to convert your documents from proprietary to more standard character sets is crucial.

Some of the better XML and HTML editors let you choose the character set you wish to save in and perform automatic conversions from the native character set you use for editing. On Unix, the native character set is likely one of the standard ISO character sets, and you can save into that format directly. On the Mac, you can avoid problems if you stick to pure ASCII documents. On Windows, you can go a little further and use Latin-1, if you're careful to stay away from the extra characters that aren't part of the official ISO-8859-1 specification. Otherwise, you'll have to convert your document from its native, platform-dependent encoding to one of the standard platform-independent character sets.

François Pinard has written an open source character-set conversion tool called recode for Linux and Unix, which you can download from http://www.iro.umontreal.ca/contrib/recode/, as well as the usual GNU mirror sites. Wojciech Galazka has ported recode to DOS. See ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/rcode34b.zip. You can also use the Java Development Kit's native2ascii tool at http://java.sun.com/products/jdk/1.3/docs/tooldocs/win32/native2ascii.html. First convert the file from its native encoding to Java's special ASCII-encoded Unicode format, then use the same tool in reverse to convert from the Java format to the encoding you actually want. For example, to convert the file myfile.xml from the Windows Cp1252 encoding to UTF-8, execute these two commands in sequence:

% native2ascii -encoding Cp1252 myfile.xml myfile.jtx
% native2ascii -reverse -encoding UTF-8 myfile.jtx myfile.xml
Previous | Home | Next
5.7. Platform-Dependent Character Sets | Book Index | 5.9. The Default Character Set for XML Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Default Character Set for XML Documents (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.9. The Default Character Set for XML Documents

Before an XML parser can read a document, it must know which character set and encoding the document uses. In some cases, external metainformation tells the parser what encoding the document uses. For instance, an HTTP header may include a Content-type header like this:

Content-type: text/html; charset=ISO-8859-1

However, XML parsers generally can't count on the availability of such information. Even if they can, they can't necessarily assume that it's accurate. Therefore, an XML parser will attempt to guess the character set based on the first several bytes of the document. The main checks the parser makes include the following:

  • If the first two bytes of the document bytes are #xFEFF, then the parser recognizes the bytes as the Unicode byte-order mark. It then guesses that the document is written in the big-endian, UCS-2 encoding of Unicode. With that knowledge, it can read the rest of the document.

  • If the first two bytes of the document are #xFFFE, then the parser recognizes the little-endian form of the Unicode byte-order mark. It now knows that the document is written in the little-endian, UCS-2 encoding of Unicode, and with that knowledge it can read the rest of the document.

  • If the first four bytes of the document are #x3C3F786D, that is, the ASCII characters <?xm, then it guesses that the file is written in a superset of ASCII. In particular, it assumes that the file is written in the UTF-8 encoding of Unicode. Even if it's wrong, this information is sufficient to continue reading the document until it gets to the encoding declaration and finds out what the character set really is.

Parsers that understand EBCDIC or UCS-4 may also apply similar heuristics to detect those encodings. However, UCS-4 isn't really used yet and is mostly of theoretical interest, and EBCDIC is a legacy family of character sets that shouldn't be used in new documents. Neither of these sets are important in practice.

Previous | Home | Next
5.8. Converting Between Character Sets | Book Index | 5.10. Character References
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Character References (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.10. Character References

Unicode contains more than 95,000 different characters covering almost all of the world's written languages. Predefining entity references for each of these characters, most of which will never be used in any one document, would impose an excessive burden on XML parsers. Rather than pick and choose which characters are worthy of being encoded as entities, XML goes to the other extreme. It predefines entity references only for characters that have special meaning as markup in an XML document: <, >, &, ", and '. All these are ASCII characters that are easy to type in any text editor.

For other characters that may not be accessible from an ASCII text editor, XML lets you use character references. A character reference gives the number of the particular Unicode character it stands for, in either decimal or hexadecimal. Decimal character references look like &#1114;; hexadecimal character references have an extra x after the &#;, that is, they look like &#x45A;. Both of these references refer to the same character, Figure , the Cyrillic small letter "nje" used in Serbian and Macedonian. For example, suppose you want to include the Greek maxim " Figure Figure Figure Ó Figure Figure Figure Figure Figure Ó Figure Figure Figure Figure Figure Figure Figure Figure Figure Figure " ("The wise man knows himself") in your XML document. However, you only have an ASCII text editor at your disposal. You can replace each Greek letter with the correct character reference, like this:

<maxim>
  &#x3C3;&#x3BF;&#x3C6;&#x3CC;&#x3C2;
  &#x3AD;&#x3B1;&#x3C5;&#x3C4;&#x3CC;&#x3BD;
  &#x3B3;&#x3B9;&#x3B3;&#x3BD;&#x3CE;&#x3C3;&#x3BA;&#x3B5;&#x3B9;
</maxim>

To the XML processor, a document using character entity references referring to Unicode characters that don't exist in the current encoding is equivalent to a Unicode document in which all character references are replaced by the actual characters to which they refer. In other words, this XML document is the same as the previous one:

<maxim> Figure Figure Figure Ó Figure Figure Figure Figure Figure Ó Figure Figure Figure Figure Figure Figure Figure Figure Figure Figure </maxim>

Character references may be used in element content, attribute values, and comments. They may not be used in element and attribute names, processing instruction targets, or XML keywords, such as DOCTYPE or ELEMENT. They may be used in the DTD in attribute default values and entity replacement text. Tag and attribute names may be written in languages such as Greek, Russian, Arabic, or Chinese, but you must use a character set that allows you to include the appropriate characters natively. You can't insert these characters with character references. For instance, this is well-formed:

< Figure Figure Figure Figure Figure > &#x3C3;&#x3BF;&#x3C6;&#x3CC;&#x3C2; < Figure Figure Figure Figure Figure >

This is not well-formed:

<&#x3BB;&#x3BF;&#x3B3;&#x3BF;&#x3C2;>
  &#x3C3;&#x3BF;&#x3C6;&#x3CC;&#x3C2;
</&#x3BB;&#x3BF;&#x3B3;&#x3BF;&#x3C2;>

There are more than 90,000 Unicode characters that you can include in your XML documents with character entity references. Chapter 26 provides character codes in both decimal and hexadecimal for some of the most useful and widely used alphabetic scripts. The interested reader will find the complete set in The Unicode Standard Version 3.0 by the Unicode Consortium (Addison Wesley, 2000). You can also view the code charts online at http://www.unicode.org/charts/.

If you use a particular group of character references frequently, you may find it easier to define them as entities, then refer to the entities instead. Example 5-3 shows a DTD defining the entities you might use to spell out the Greek words in the previous several examples.

Example 5-3. A DTD defining general entity references for several Greek letters

<!ENTITY sigma              "&#x3C3;">
<!ENTITY omicron_with_tonos "&#x3CC;">
<!ENTITY phi                "&#x3C6;">
<!ENTITY omicron            "&#x3BF;">
<!ENTITY final_sigma        "&#x3C2;">
<!ENTITY epsilon_with_tonos "&#x3AD;">
<!ENTITY alpha              "&#x3B1;">
<!ENTITY lambda             "&#x3C3;">
<!ENTITY upsilon            "&#x3C5;">
<!ENTITY tau                "&#x3C4;">
<!ENTITY nu                 "&#x3BD;">
<!ENTITY gamma              "&#x3B3;">
<!ENTITY iota               "&#x3B9;">
<!ENTITY omega_with_tonos   "&#x3CE;">
<!ENTITY kappa              "&#x3BA;">
<!ENTITY epsilon            "&#x3B5;">

These entities can even be used in invalid documents, provided either that the declarations are made in the document's internal DTD subset, which all XML parsers are required to process, or that your parser reads the external DTD subset. By convention, DTD fragments that do nothing but define entities have the three-letter suffix, .ent. Generally, these fragments are imported into the document's DTD, using external parameter entity references. Example 5-4 shows how the maxim might be written using these entities, assuming they can be found at the relative URL greek.ent.

Example 5-4. The maxim using entity references instead of character references

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE maxim [

 <!ENTITY % greek_alphabet SYSTEM "greek.ent">
 %greek_alphabet;

]>
<maxim>
  &sigma;&omicron_with_tonos;&phi;&omicron;&final_sigma;
  &epsilon_with_tonos;&alpha;&upsilon;&tau;&omicron_with_tonos;&nu;
  &gamma;&iota;&gamma;&nu;&omega_with_tonos;&sigma;&kappa;&epsilon;&iota;
</maxim>

A few standard entity subsets are widely available for your own use. The XHTML 1.0 DTD includes three useful entity sets you can adopt in your own work:

Latin-1 characters, http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent
The non-ASCII characters from 160 up in ISO-8859-1

Special characters, http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent
Letters from ISO-8859-2 (Latin-2) that aren't also in Latin-1, such as Figure and various punctuation marks, such as the dagger, the Euro sign, and the em dash

Symbols, http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent
The Greek alphabet (though accented characters are missing) and various punctuation marks, mathematical operators, and other symbols commonly used in mathematics

Chapter 26 provides complete charts showing of all characters in these entity sets. You can either use these directly from their relatively stable URLs at the W3C or copy them onto your own systems. For example, to use entities from the symbol set in a document, add the following to the document's DTD:

<!ENTITY % HTMLsymbol PUBLIC
   "-//W3C//ENTITIES Symbols for XHTML//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent">
%HTMLsymbol;

Since these are fairly standard DTDs, they have both Public IDs and URLs. Other groups and individuals have written entity sets you can use similarly, though no canonical collection of entity sets that covers all of Unicode exists. SGML included almost 20 separate entity sets covering Greek, Cyrillic, extended Latin, mathematical symbols, diacritical marks, box-drawing characters, and publishing marks. These aren't a standard part of XML, but several applications including DocBook (http://www.docbook.org/) and MathML (http://www.w3.org/TR/MathML2/chapter6.html#chars_entity-tables) have ported them to XML. MathML also has several useful entity sets containing more mathematical symbols.

Previous | Home | Next
5.9. The Default Character Set for XML Documents | Book Index | 5.11. xml:lang
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


xml:lang (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

5.11. xml:lang

Since XML documents are written in Unicode, XML is an excellent choice for multilingual documents, such as an Arabic commentary on a Greek text (something that couldn't be done with almost any other character set). In such multilingual documents, it's useful to identify in which language a particular section of text is written. For instance, a spellchecker that only knows English shouldn't try to check a French quote.

Each XML element may have an xml:lang attribute that specifies the language in which the content of that element is written. For example, the previous maxim might look like this:

<maxim xml:lang="el">
  &#x3C3;&#x3CC;&#3C6;&#3BF;&#3C2; &#x3AD;&#3B1;&#3C5;&#3C4;&#x3CC;&#x3BD;
  &#x3B3;&#x3B9;&#x3B3;&#x3BD;&#X3CE;&#x3C3;&#x3BA;&#x3B5;&#x3B9;
</maxim>

This identifies it as Greek. The specific code used, el, comes from the Greek word for Greek, Figure Figure Figure Figure Figure Figure Figure Figure .

5.11.1. Language Codes

The value of the xml:lang language attribute should be one of the two-letter language codes defined in ISO-639, Codes for the Representation of Names of Languages, found at http://lcweb.loc.gov/standards/iso639-2/langhome.html, if such a code exists for the language in question.

For languages that aren't listed in ISO-639, you can use a language identifier registered with IANA; currently, about 20 of these identifiers exist, including i-navajo, i-klingon, and i-lux. The complete list can be found at ftp://ftp.isi.edu/in-notes/iana/assignments/languages/tags. All identifiers begin with i-. For example:

<maxim xml:lang="i-klingon">Heghlu'meH QaQ jajvam</maxim>

If the language you need still isn't present in these two lists, you can create your own language tag, as long as it begins with the prefix x- or X- to identify it as a user-defined language code. For example, the title of this journal is written in J. R. R. Tolkien's fictional Quenya language:

<journal xml:lang="x-quenya">Tyalië Tyelelliéva</journal>

5.11.2. Subcodes

For some purposes, knowing the language is not enough. You also need to know the region where the language is spoken. For instance, French has slightly different vocabulary, spelling, and pronunciation in France, Quebec, Belgium, and Switzerland. Although written identically with an ideographic character set, Mandarin and Cantonese are actually quite different, mutually unintelligible dialects of Chinese. The United States and the United Kingdom are jocularly referred to as "two countries separated by a common language."

To handle these distinctions, the language code may be followed by any number of subcodes that further specify the language. Hyphens separate the language code from the subcode and subcodes from each other. If the language code is an ISO-639 code, the first subcode should be one of the two-letter country codes defined by ISO-3166, Codes for the Representation of Names of Countries, found at http://www.ics.uci.edu/pub/ietf/http/related/iso3166.txt. This xml:lang attribute indicates Canadian French:

<p xml:lang="fr-CA">Marie vient pour le fin de semaine.</p>

The language code is usually written in lowercase, and the country code is written in uppercase. However, this is just a convention, not a requirement.

5.11.3. ATTLIST Declarations of xml:lang

Although the XML 1.0 specification defines the xml:lang attribute, you still have to declare it in the DTDs of your valid documents. For example, this information declares the maxim element used several times in this chapter:

<!ELEMENT maxim (#PCDATA)>
<!ATTLIST maxim xml:lang NMTOKEN #IMPLIED>

Here I've used the NMTOKEN type, since all legal language codes are well-formed XML name tokens.

You may declare the xml:lang attribute in any convenient way. For instance, if you want to require its presence on the maxim element, you could make it #REQUIRED:

<!ATTLIST maxim xml:lang NMTOKEN #REQUIRED>

Or, if you wanted to allow only French and English text in your documents, you might specify it as an enumerated type with a default of English like this:

<!ATTLIST maxim xml:lang (en | fr) 'en'>

Unless you use an enumerated type, the parser will not check that the value you give it follows the rules outlined here. It's your responsibility to make sure you use appropriate language codes and subcodes.

Previous | Home | Next
5.10. Character References | Book Index | II. Narrative-Centric Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Narrative-Centric Documents (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Part II. Narrative-Centric Documents

Previous | Home | Next
5.11. xml:lang | Book Index | 6. XML as a Document Format
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML as a Document Format (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 6. XML as a Document Format

Contents:

SGML's Legacy
Narrative Document Structures
TEI
DocBook
Document Permanence
Transformation and Presentation

XML is first and foremost a document format. It was always intended for web pages, books, scholarly articles, poems, short stories, reference manuals, tutorials, textbooks, legal pleadings, contracts, instruction sheets, and other documents that human beings would read. Its use as a syntax for computer data in applications such as order processing, object serialization, database exchange and backup, and electronic data interchange is mostly a happy accident.

Most computer programmers are better trained in working with the rigid structures one encounters in data-oriented applications than in the more free-form environment of an article or story. Most writers are more accustomed to the more free-form format of a book, story, or article. XML is perhaps unique in addressing the needs of both communities equally well. This chapter describes by both elucidation and example the structures encountered in documents that are meant to be read by people instead of computers. Subsequent chapters will look at web pages in particular, then address technologies such as XSLT, XLinks, and stylesheets that are primarily intended for use with documents that will be read by human beings. Once we've done that, we'll look at XML as a format for more or less transitory data meant to be read by computers, rather than semipermanent documents intended for human consumption.

6.1. SGML's Legacy

XML is a simplified form of the Standardized General Markup Language (SGML). The language that would eventually become SGML was invented by Charles F. Goldfarb, Ed Mosher, and Ray Lorie at IBM in the 1970s and developed by several hundred people around the world until its eventual adoption as ISO standard 8879 in 1986. SGML was intended to solve many of the same problems XML solves in much the same way as XML solves them. It was and is a semantic and structural markup language for text documents. SGML is extremely powerful and achieved some success in the U.S. military and government, in the aerospace sector, and in other domains that needed ways of efficiently managing technical documents that were tens of thousands of pages long.

SGML's biggest success was HTML, which was and is an SGML application. However, HTML is just one SGML application. It does not have anything close to the full power of SGML itself. SGML has also been used to define many other document formats, including DocBook and TEI, both of which we'll discuss shortly.

However, SGML is complicated--very, very complicated. The official SGML specification is over 150 very technical pages. It covers many special cases and unlikely scenarios. It is so complex that almost no software has ever implemented it fully. Programs that implement or rely on different subsets of SGML are often incompatible. The special feature that one program considers essential is all too often considered extraneous fluff and omitted by the next program. Nonetheless, experience with SGML taught developers a lot about the proper design, implementation, and use of markup languages for a wide variety of documents. Much of that general knowledge applies equally well to XML.

One thing all this should make clear is that XML documents aren't just used on the Web. XML can easily handle the needs of publishing in a variety of media, including books, magazines, journals, newspapers, and pamphlets. XML is particularly useful when you need to publish the same information in several of these formats. By applying different stylesheets to the same source document, you can produce web pages, speaker's notes, camera-ready copy for printing, and more.

Previous | Home | Next
II. Narrative-Centric Documents | Book Index | 6.2. Narrative Document Structures
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Narrative Document Structures (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

6.2. Narrative Document Structures

All XML documents are trees. However, trees are very general-purpose data structures. If you've been formally trained in computer science (and very possibly even if you haven't been), you've encountered binary trees, red-black trees, balanced trees, B-trees, ordered trees, and more. However, when working with XML, it's highly unlikely that any given document matches any of these structures. Instead, XML documents are the most general sort of tree, with no particular restrictions on how nodes are ordered or how or which nodes are connected to which other nodes. Narrative XML documents are even less likely than data-oriented XML documents to have an identifiable structure beyond their mere treeness.

So what does a narrative-oriented XML document look like? Of course, there's a root element. All XML documents have one. Generally speaking, this root element represents the document itself. That is, if the document is a book, the root element is book. If the document is an article, the root element is article, and so on.

Beyond that, large documents are generally broken up into sections of some kind, perhaps chapters for a book, parts for an article, or claims for a legal brief. Most of the document consists of these primary sections. In some cases, there'll be several different kinds of sections; for instance, one for the table of contents, one for the index, and one for the chapters of a book.

Generally, the root element also contains one or more elements providing metainformation about the document, for example, the title of the work, the author of the document, the dates the document was written and last modified, and so forth. One common pattern is to place the metainformation in one child of the root element and the main content of the work in another. This is how HTML documents are written. The root element is html. The metainformation goes in a head element, and the main content goes in the body element. TEI and DocBook also follow this pattern.

Sections of the document can be further divided into subsections. The subsections themselves may be further divided. How many levels of subsection appear generally depends on how large the document is. An encyclopedia will have many levels of sectioning--a pamphlet or flier almost none. Each section and subsection normally has a title. It may also have elements or attributes that indicate metainformation about the section, such as the author or date it was last modified.

Up to this point, mixed content is mostly avoided. Elements contain child elements and whitespace, and that's likely all they contain. However, at some level it becomes necessary to insert the actual text of the document--the words that people will read. In most Western languages these will probably be divided into paragraphs and other block-level elements like headlines, figures, sidebars, and footnotes. Generic document DTDs like DocBook won't be able to say more about these items than this.

The paragraphs and other block-level items will mostly contain words in a row, that is, text. Some of this text may be marked up with inline elements. For instance, you may wish to indicate that a particular string of text inside the block-level element is a date, a person, or simply important. However, most of the text will not be so annotated.

One area in which different XML applications diverge is the question of whether block-level items may contain other block-level items. For instance, can a paragraph contain a list? Or can a list item contain a paragraph? It's probably easier to work with more structured documents in which blocks can't contain other blocks (particularly other instances of the same kind). However, it's very often the case that a block has a very good reason to contain other blocks. For instance, a long list item or quotation may contain several paragraphs.

For the most part, this entire structure from the root down to the most deeply nested inline item tends to be quite linear; that is, you expect that a person will read the words in pretty much the same order they appear in the document. If all the markup were suddenly removed and you were left with nothing but the raw text, the result should be more or less legible. The markup can be used to index or format the document, but it's not a fundamental part of the content.

Another important point about these sorts of XML documents: not only are they composed of words in a row; they're composed of words. What they contain is text intended for human beings to read. They're not numbers or dates or money, except insofar as these things occur as part of the normal flow of the narrative. The #PCDATA content of the lowest-level elements of the tree mostly have one type: string. If anything has a real type beyond string it's likely metainformation about the document (figure number, date last modified, and so on) rather than the content of the document itself.

This explains, in detail, why DTDs don't provide strong (or really any) data typing. The documents for which SGML was designed didn't need it. XML documents are doing jobs for which SGML wasn't designed, such as tracking inventories or census data, do need data typing; that's why various people and organizations have invented a plethora of schema languages. However, schemas really don't improve on DTDs for narrative documents.

Not all XML documents are like those we've described here. Not even all narrative-oriented XML documents are like this. However, a surprising number of narrative-oriented XML applications do follow this basic pattern, perhaps with a nip here or a tuck there. The reason is that this is the basic structure narratives follow, and that has proven its usefulness in the thousands of years since writing was invented. If you were to define your own DTDs for general narrative-oriented documents, you'd probably come up with something a lot like this. If you define your own DTDs for more specialized narrative-oriented documents, then the names of your elements may change to reflect your domain--for instance, if you were writing the next edition of the Boy Scout handbook, one of your subsections might be called MeritBadge--however, the basic hierarchy of document, metainformation, sections and subsections, block-level elements, and marked-up text would likely remain.

Previous | Home | Next
6. XML as a Document Format | Book Index | 6.3. TEI
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


TEI (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

6.3. TEI

The Text Encoding Initiative (TEI, http://www.tei-c.org/) is an SGML application designed for the markup of classic literature, such as Virgil's Aeneid or the collected works of Thomas Jefferson. It's a prime example of a narrative-oriented DTD. Since TEI is designed for scholarly analysis of text rather than more casual reading or publishing, it includes elements not only for common document structures (chapter, scene, stanza, etc.) but also for typographical elements, grammatical structure, the position of illustrations on the page, and so forth. These aren't important to most readers, but they are important to TEI's intended audience of humanities scholars. For many academic purposes, one manuscript of the Aeneid is not necessarily the same as the next. Transcription errors and emendations made by various monks in the Middle Ages can be crucial.

TEI is an SGML application. It uses several features of SGML not found in XML, including the & connector and tag minimization. However, XML is clearly the wave of the future. Therefore, like most evolving SGML applications, TEI is moving toward XML. A light version of the TEI DTD is available for authors who prefer to work in pure XML. It's not exactly the same as the SGML version, but it's very close for many practical uses.

Example 6-1 shows a fairly simple TEI Lite document that uses the XML version of the TEI DTD. The content comes from the book you're reading now. Although a complete TEI-encoded copy of this manuscript would be much longer, this simple example demonstrates the basic features of most TEI documents that represent books. (As well as prose, TEI can also be used for plays, poems, missals, and essentially any written form of literature.)

Example 6-1. A TEI document

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE TEI.2 SYSTEM "xteilite.dtd">
<TEI.2>

  <teiHeader>
    <fileDesc>
      <titleStmt>
        <title>XML in a Nutshell</title>
        <author>Harold, Elliotte Rusty</author>
        <author>Means, W. Scott</author>
      </titleStmt>
      <publicationStmt><p></p></publicationStmt>
      <sourceDesc><p>Early manuscript draft</p></sourceDesc>
    </fileDesc>
  </teiHeader>

  <text id="HarXMLi">

    <front>
      <div type='toc'>
        <head>Table Of Contents</head>
        <list>
          <item>Introducing XML</item>
          <item>XML as a Document Format</item>
          <item>XML on the Web</item>
        </list>
      </div>

    </front>

    <body>

      <div1 type="chapter">
        <head>Introducing XML</head>
        <p></p>
      </div1>

      <div1 type="chapter">
        <head>XML as a Document Format</head>
        <p>
          XML is first and foremost a document format. It was always
          intended for web pages, books, scholarly articles, poems,
          short stories, reference manuals, tutorials, texts, legal
          pleadings, contracts, instruction sheets, and other documents
          that human beings would read. Its use as a syntax for computer
          data in applications like syndication, order processing,
          object serialization, database exchange and backup, electronic
          data interchange, and so forth is mostly a happy accident.
       </p>

       <div2 type="section">
         <head>SGML's Legacy</head>
         <p></p>
       </div2>

       <div2 type="section">
         <head>TEI</head>
         <p></p>
       </div2>

       <div2 type="section">
         <head>DocBook</head>
         <p>
           DocBook (<hi>http://www.docbook.org/</hi>) is an
           SGML application designed for new documents, not old ones.
           It's especially common in computer documentation. Several
           O'Reilly books have been written in DocBook including
           <bibl><author>Norm Walsh</author>'s <title>DocBook: The
           Definitive Guide</title></bibl>. Much of the <abbr
           expan='Linux Documentation Project'>LDP</abbr>
           (<hi>http://www.linuxdoc.org/</hi>) corpus is written in
           DocBook.
         </p>
       </div2>

      </div1>

      <div1 type="chapter">
        <head>XML on the Web</head>
        <p></p>
      </div1>

    </body>

    <back>
      <div1 type="index">
        <list>
          <head>INDEX</head>
          <item>SGML, 8, 9, 91, 92, 94</item>
          <item>DocBook, 97-101</item>
          <item>TEI, 94-97, 101</item>
          <item>Text Encoding Initiative, See TEI</item>
        </list>
      </div1>
    </back>

  </text>
</TEI.2>

The root element of this and all TEI documents is TEI.2. This root element is always divided into two parts, a header represented by a teiHeader element and the main content of the document represented by a text element. The header contains information about the source document (for instance, exactly which medieval manuscript the text was copied from), the encoding of the document, some keywords describing the document, and so forth.

The text element is itself divided into three parts:

Front matter in the front element
The preface, table of contents, dedication page, pictures of the cover, and so forth. Each of these is represented by a div element with a type attribute whose value identifies the division as a table of contents, preface, title page, and so forth. Each of these divisions contains other elements laying out the content of that division.

The body of the work in the body element
The individual chapters, acts, and so forth that make up the document. Each of these is represented by a div1 element with a type attribute that identifies this particular division as a volume, book, part, chapter, poem, act, and so forth. Each div1 element has a header child giving the title of the volume, book, part, chapter, etc.

Back matter in the back element
The index, glossary, etc.

The divisions may be further subdivided; div1 s can contain div2s, div2 s can contain div3s, div3 s can contain div4 s, and so on up to div7. However, for any given work, there is a smallest division. This division contains paragraphs represented by p elements for prose or stanzas represented by lg elements for poetry. Stanzas are further broken up into individual lines represented by l elements.

Both lines and paragraphs contain mixed content; that is, they contain plain text. However, parts of this text may be marked up further by elements indicating that particular words or characters are peoples' names (name), corrections (corr), illegible (unclear), misspellings (sic), and so on.

This structure fairly closely reflects the structure of the actual documents that are being encoded in TEI. This is true of most narrative-oriented XML applications that need to handle fairly generic documents. TEI is a very representative example of typical XML document structure.

Previous | Home | Next
6.2. Narrative Document Structures | Book Index | 6.4. DocBook
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DocBook (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

6.4. DocBook

DocBook (http://www.docbook.org/) is an SGML application designed for new documents, not old ones. It's especially common in computer documentation. Several O'Reilly books have been written in DocBook, including Norm Walsh and Leonard Muellner's DocBook: The Definitive Guide. Much of the Linux Documentation Project (LDP, http://www.linuxdoc.org/) corpus is written in DocBook.

The current version of DocBook, 4.1.2, is available as both an SGML and an XML application. The XML version is not quite the same as the SGML version, but it's very close for most practical uses. The DocBook maintainers have announced plans to move to a single DTD that is completely compatible with both SGML and XML in version 5.0. Example 6-2 shows a simple DocBook XML document based on the book you're reading now. Needless to say, the full version of this document would be much longer.

Example 6-2. A DocBook document

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBk XML V4.1.2//EN"
                      "docbook/docbookx.dtd">
<book>
  <title>XML in a Nutshell</title>
  <bookinfo>
    <author>
      <firstname>Elliotte Rusty</firstname>
      <surname>Harold</surname>
    </author>
    <author>
      <firstname>W. Scott</firstname>
      <surname>Means</surname>
    </author>
  </bookinfo>

  <toc>
    <tocchap><tocentry>Introducing XML</tocentry></tocchap>
    <tocchap><tocentry>XML as a Document Format</tocentry></tocchap>
    <tocchap><tocentry>XML as a "better" HTML</tocentry></tocchap>
  </toc>

  <chapter>
    <title>Introducing XML</title>
    <para></para>
  </chapter>

  <chapter>
    <title>XML as a Document Format</title>

   <para>
     XML is first and foremost a document format. It was always intended
     for web pages, books, scholarly articles, poems, short stories,
     reference manuals, tutorials, texts, legal pleadings, contracts,
     instruction sheets, and other documents that human beings would
     read. Its use as a syntax for computer data in applications like
     syndication, order processing, object serialization, database
     exchange and backup, electronic data interchange, and so forth is
     mostly a happy accident.
   </para>

   <sect1>
     <title>SGML's Legacy</title>
     <para></para>
   </sect1>
   <sect1>
     <title>TEI</title>
     <para></para>
   </sect1>

   <sect1>
     <title>DocBook</title>
     <para>
       <ulink url="http://www.docbook.org/">DocBook</ulink>
       is an SGML application designed for new documents, not old ones.
       It's especially common in computer documentation. Several
       O'Reilly books have been written in DocBook including
       <citation>Norm Walsh and Leonard Muellner's
       <citetitle>DocBook: The Definitive
       Guide</citetitle></citation>. Much of the <ulink
       url="http://www.linuxdoc.org/">Linux Documentation Project
       (LDP)</ulink> corpus is written in DocBook. </para>
   </sect1>

  </chapter>

  <chapter>
    <title>XML on the Web</title>
    <para></para>
  </chapter>

  <index>
    <indexentry>
      <primaryie>SGML, 8, 9, 91, 92, 94</primaryie>
    </indexentry>
    <indexentry>
      <primaryie>DocBook, 97-101</primaryie>
    </indexentry>
    <indexentry>
      <primaryie>TEI, 94-97, 101</primaryie>
    </indexentry>
    <indexentry>
      <primaryie>Text Encoding Initiative</primaryie>
      <seeie>TEI</seeie>
    </indexentry>
  </index>

</book>

DocBook offers many advantages to technical authors. First and foremost, it's open, nonproprietary, and can be created with any text editor. It would feel a little silly to write open source documentation for open source software with closed and proprietary tools like Microsoft Word (which is not to say this hasn't been done). If your documents are written in DocBook, they aren't tied to any one platform, vendor, or application software. They're portable across essentially any plausible environment you can imagine.

Not only is DocBook theoretically editable with basic text editors; it's simple enough that such editing is practical as well. Of course, if you'd like a little help, there are a number of free tools available, including an Emacs major mode (http://www.nwalsh.com/emacs/docbookide/index.html). Furthermore, like many good XML applications, DocBook is modular. You can use the pieces you need and ignore the rest. If you need tables, there's a very complete tables module. If you don't need tables, you don't need to know about or use this module. Other modules cover various entity sets and equations.

DocBook is an authoring format, not a format for finished presentation. Before a DocBook document is read by a person, it should be converted to any of several formats, including the following:

  • HTML

  • XSL Formatting Objects

  • Rich Text Format (RTF)

  • TEX

For example, if you want high-quality printed documentation for a program, you can convert a DocBook document to TEX, then use the standard TEX tools to convert the resulting TEX file to a DVI and/or PostScript file and print that. If you just want to read it on your computer, then you'd probably convert it to HTML and load it into your web browser. For other purposes, you'd pick something else. With DocBook all these formats come essentially for free. It's very easy to produce multiple output documents in different formats from a single DocBook source document. Indeed, this benefit isn't just limited to DocBook. Most well-thought-out XML input formats are just as easy to publish in other formats.

Previous | Home | Next
6.3. TEI | Book Index | 6.5. Document Permanence
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Document Permanence (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

6.5. Document Permanence

XML documents that are intended for computers to read are often transitory. For instance, if you create a SOAP document that represents a request to Windows server running .NET, then that document exists for just as long as it takes the client to send it to the server and for the server to parse it into its internal data structures. After that's done, the document will be discarded. It probably won't be around for two minutes, much less two years. It's an ephemeral communication between two systems, with no more permanence than any of billions of other messages that computers exchange on a daily basis, most of which are never even written to disk, much less archived for posterity.

Some applications do store more permanent computer-oriented data in XML. For instance, XML is the native file format of the Gnumeric spreadsheet. On the other hand, this format is really only understood by Gnumeric and perhaps the other Gnome applications. It's designed to meet the specific needs of that one program. Exchanging data with other applications, including ones that haven't even been invented yet, is a secondary concern.

XML documents meant for humans tend to be more permanent and less software bound, however. If you encode the Declaration of Independence in XML, you want people to be able to read it in two, two hundred, or two thousand years. You also want them to be able to read it with any convenient tool, including ones not invented yet. These requirements have some important implications for both the XML applications you design to hold the data and the tools you use to read and write them.

The first rule is that the format should be very well documented. There should be a DTD, and that DTD should be very well commented. Furthermore, there should be a significant amount of prose documentation as well. Prose documentation can't substitute for the formal documentation of a DTD, but it's an invaluable asset in understanding the DTD.

Standard formats like DocBook and TEI should be preferred to custom, one-off XML applications. You should avoid proprietary DTDs that are owned by any one person or company and whose future may depend on the fortunes of that company or individual. Even DTDs that come from nonprofit consortia like OASIS or TEI should be licensed sufficiently liberally so that intellectual property restrictions won't let anyone throw up road blocks in your path. At least one DTD purveyor has gone so far as to file for patents on its DTDs. These DTDs should be avoided like the plague. Stick to DTDs that may be freely copied and shared and that can be retrieved from many different locations.

Once you've settled on a standard DTD, try to avoid modifying it if you possibly can. If you absolutely must modify it, then document your changes in excruciating, redundant detail. Include comments in both your DTDs and documents, explaining what you've done. Use the parameter entities built into the DTDs to add new element types or subtract old ones, rather than modifying the DTD files themselves.

Conversely, the format shouldn't be too hard to reverse engineer if the documentation is lost. Make sure full names are used throughout for element and attribute names. DocBook's para element is superior to TEI's p element. Paragraph would be better still.

All of the inherent structure of the document should be indicated by markup and markup alone. It should not be left for the user to infer, nor should it be encoded using whitespace or other separators. For instance, here's an example of what not to do from SVG:

<polygon style="fill: blue; stroke: green; stroke-width: 12"
         points="350,75  379,161 469,161 397,215 423,301 350,250
                 277,301 303,215 231,161 321,161" />

The style attribute contains three separate and barely related items. Understanding this element requires parsing the non-XML CSS format. The points attribute is even worse. It's a long list of numbers, but there's no information about what each number is. You can't, for instance, see which are the x and which are the y coordinates. An approach like this is preferable:

<polygon fill="blue" stroke="green" stroke-width="12">
  <point x="350" y="75"/>
  <point x="379" y="161"/>
  <point x="469" y="161"/>
  <point x="397" y="215"/>
  <point x="423" y="301"/>
  <point x="350" y="250"/>
  <point x="277" y="301"/>
  <point x="303" y="215"/>
  <point x="231" y="161"/>
  <point x="321" y="161"/>
</polygon>

The attribute-based style syntax is actually allowed in SVG. However, the debate over which form to use for coordinates was quite heated in the W3C SVG working group. In the end the working group decided (wrongly, in our opinion) that the more verbose form would never be adopted because of its size, even though most members felt it was more in keeping with the spirit of XML. We think the working group overemphasized the importance of document size in an era of exponentially growing hard disks and network bandwidth, not to mention ignoring the ease with which the second format could be compressed for transport or storage.

Stylesheets are important. We're all familiar with the injunction to separate presentation from content. You've heard enough warnings about not including mere style information like italics and font choices in your XML documents. However, be careful not to go the other way and include content in your stylesheets either. Author names, titles, copyrights and other such information that changes from document to document belongs in the document, not the stylesheet, even if it's metainformation about the document rather than the actual content of the document.

Always keep in mind that you're not just writing for the next couple months or years, but possibly for the next couple thousand of years. Have pity on the poor historians who are going to have to decipher your markup with limited tools to help them.

Previous | Home | Next
6.4. DocBook | Book Index | 6.6. Transformation and Presentation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Transformation and Presentation (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

6.6. Transformation and Presentation

The markup in a typical XML document describes the document's structure, but it tends not to describe the document's presentation. That is, it says how the document is organized but not how it looks. Although XML documents are text, and a person could read them in native form if they really wanted to, much more commonly an XML document is rendered into some other format before being presented to a human audience. One of the key ideas of markup languages in general and XML in particular is that the input format need not be the same as the output format. To put it another way, what you see is not what you get, nor is it what you want to get. The input markup language is designed for the convenience of the writer. The output language is designed for the convenience of the reader.

Of course this requires a means of transforming the input format into the output format. Most XML documents undergo some kind of transformation before being presented to the reader. The transformation may be to a different XML vocabulary like XHTML or XSL-FO, or it may be to a non-XML format like PostScript or RTF.

XML's semiofficial transformation language is Extensible Stylesheet Language Transformations (XSLT). An XSLT document contains a list of template rules. Each template rule has a pattern noting which elements and other nodes it matches. An XSLT processor reads the input document. When it sees something in the input document that matches a template rule in the stylesheet, it outputs the template rule's template. Part of the template is normally an instruction that tells the processor to include content from the input in the output. This allows, for example, the text of the output document to be the same while all the markup is changed. For instance, you could write a stylesheet that would transform DocBook documents into TEI documents. XSLT will be discussed in much more detail in Chapter 8.

However, XSLT is not the only transformation language you can use with your XML documents. Other stylesheet languages such as the Document Style Sheet and Semantics Language (DSSSL, http://www.jclark.com/dsssl/) are also available. So are a variety of proprietary tools like OmniMark (http://www.omnimark.com/). Most of these have particular strengths and weaknesses for particular kinds of documents. Custom programs written in a variety of programming languages, such as Java, C++, Perl, and Python, can use a plethora of APIs, such as SAX, DOM, and JDOM, to transform documents. This is sometimes useful when you need something more than a mere transformation--for instance, interpreting certain elements as database queries and actually inserting the results of those queries into the output document, or asking the user to answer questions in the middle of the transformation. However, the biggest single factor when choosing which tool to use is simply which language and syntax you're most comfortable with. De linguis non disputandum est.

There are many different choices for the output format from a transformation. A PostScript file can be printed on paper, overhead transparencies, slides, or even T-shirts. A PDF document can be viewed in all these ways and shown on the screen as well. However, for screen display, PDF is vastly inferior to simple HTML, which has the advantages of being very broadly accessible across platforms and being very easy to generate via XSLT from source XML documents. Generating a PDF or a PostScript file normally requires an additional conversion step in which special software converts some custom XML output format like XSL-FO to what you actually want.

An alternative to a transformation-based presentation is to provide a descriptive stylesheet that simply states how each element in the original document should be formatted. This is the realm of Cascading Style Sheets (CSS). This works particularly well for narrative documents where all that's needed is a list of the fonts, styles, sizes, and so on to apply to the content of each element. The key is that when all markup is stripped from the document, what remains is more or less a plain-text version of what you want to see. No reordering or rearrangement is necessary. This approach works less well for data-oriented documents where the raw content may be nothing more than an undifferentiated mass of numbers, dates, or other information that's hard to understand without the context and annotations provided by the markup. However, in this case a combination of the two approaches works well. First a transformation can produce a new document containing rearranged and annotated information. Then a CSS stylesheet can apply style rules to the elements in this transformed document.

Previous | Home | Next
6.5. Document Permanence | Book Index | 7. XML on the Web
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML on the Web (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 7. XML on the Web

Contents:

XHTML
Direct Display of XML in Browsers
Authoring Compound Documents with Modular XHTML
Prospects for Improved Web-Search Methods

XML began as an effort to bring the full power and structure of SGML to the Web in a form that was simple enough for nonexperts to use. Like most great inventions, XML turned out to have uses far beyond what its creators originally envisioned. Indeed, there's a lot more XML off the Web than on it. Nonetheless, XML is still a very attractive language in which to write and serve web pages. Since XML documents must be well-formed and parsers must reject malformed documents, XML pages are less likely to have annoying cross-browser incompatibilities. Since XML documents are highly structured, they're much easier for robots to parse. Since XML tag and attribute names reflect the nature of the content they hold, search-engine spiders can more easily determine the true meaning of a page.

XML on the Web comes in three flavors. The first is XHTML, an XMLized variant of HTML 4.0 that tightens up HTML to match XML's syntax. For instance, XHTML requires that all start-tags correspond to a matching end-tag and that all attribute values be quoted. XHTML also adds a few bits of syntax to HTML, such as the XML declaration and empty-element tags that end with />. Most of XHTML can be displayed quite well in legacy browsers, with a few notable exceptions.

The second flavor of XML on the Web is direct display of XML documents that use arbitrary vocabularies in web browsers. Generally, the formatting of the document is supplied either by a CSS stylesheet or by an XSLT stylesheet that transforms the document into HTML (perhaps XHTML). This flavor requires an XML-aware browser and is only beginning to be supported by the installed base of web clients.

A third option is to mix raw XML vocabularies such as MathML and SVG with XHTML using Modular XHTML. Modular XHTML lets you embed RDF cataloging information, MathML equations, SVG pictures, and more inside your XHTML documents. Namespaces sort out which elements belong to which applications.

7.1. XHTML

XHTML is an official W3C recommendation. It defines an XML-compatible version of HTML, or rather it redefines HTML as an XML application instead of as an SGML application. Just looking at an XHTML document, you might not even realize that there's anything different about it. It still uses the same <p>, <li>, <table>, <h1>, and other tags with which you're familiar. Elements and attributes have the same, familiar names they have in HTML. The syntax is still basically the same.

The difference is not so much what's allowed but what's not allowed. <p> is a legal XHTML tag, but <P> is not. <table border="0" width="515"> is legal XHTML; <table border=0 width=515> is not. A paragraph prefixed with a <p> and suffixed with a </p> is legal XHTML, but a paragraph that omits the closing </p> tag is not. Most existing HTML documents require substantial editing before they become well-formed and valid XHTML documents. However, once they are valid XHTML documents, they are automatically valid XML documents that can be manipulated with the same editors, parsers, and other tools you use to work with any XML document.

7.1.1. Moving from HTML to XHTML

Most of the changes required to turn an existing HTML document into an XHTML document involve making the document well-formed. For instance, given a legacy HTML document, you'll probably have to make at least some of these changes to turn it into XHTML:

  • Add missing end-tags like </p> and </li>.

  • Rewrite elements so that they nest rather than overlap. For example, change <p><em>an emphasized paragraph</p></em> to <p><em>an emphasized paragraph</em></p>.

  • Put double or single quotes around your attribute values. For example, change <p align=center> to <p align="center">.

  • Add values (which are the same as the name) to all minimized Boolean attributes. For example, change <input type="checkbox" checked> to <input type="checkbox" checked="checked">.

  • Replace any occurrences of & or < in character data or attribute values with &amp; and &lt;. For instance, change A&P to A&amp;P and <a href="http://www.google.com/search?client=googlet&q=Java%20XML"> to <a href="http://www.google.com/search?client=googlet&amp;q=Java%20XML">.

  • Make sure the document has a single root html element.

  • Change empty elements like <hr> to <hr/> or <hr></hr>.

  • Add hyphens to comments so that <! this is a comment> becomes <!-- this is a comment -->.

  • Encode the document in UTF-8 or UTF-16, or add an XML declaration that specifies in which character set it is encoded.

However, XHTML doesn't merely require well-formedness; it requires validity. In order to create a valid XHTML document, you'll need to make these changes as well:

  • Add a DOCTYPE declaration to the document pointing to one of the three XHTML DTDs.

  • Make all element and attribute names lowercase.

  • Make any other changes you have to make to your markup so that the document validates against the DTD: for example, eliminating nonstandard elements like marquee, adding required attributes like the alt attribute of img, or moving child elements out from inside elements where they're not allowed such as a blockquote inside a p.

In addition, the XHTML specification imposes several requirements that, strictly speaking, are not required for either well-formedness or validity. However, they do make parsing XHTML documents a little easier. These are:

  • The root element of the document must be html.

  • There must be a DOCTYPE declaration that uses a PUBLIC ID to identify one of the three XHTML DTDs.

  • The root element of the document must have an xmlns attribute identifying the default namespace as http://www.w3.org/1999/xhtml.

Finally, if you wish, you may--but do not have to--add an XML declaration or an xml-stylesheet processing instruction to the prolog of your document.

Example 7-1 shows an HTML document from the O'Reilly web site that exhibits many of the validity problems you'll find on the Web today. In fact, this is a much neater page than most. Nonetheless, not all attribute values are quoted. The noshade attribute of the HR element doesn't even have a value. There's no document type declaration. Tags are a mix of upper- and lowercase, mostly uppercase. The DD elements are missing end-tags, and there's some character data inside the second definition that's not part of a DT or a DD.

Example 7-1. A typical HTML document

<HTML><HEAD>
  <TITLE>O'Reilly Shipping Information</TITLE>
</HEAD>
<BODY BGCOLOR="#ffffff" VLINK="#0000CC" LINK="#990000" TEXT="#000000">
<table border=0 width=515>
<tr>
<td>
<IMG SRC="/www/graphics_new/generic_ora_header_wide.gif" BORDER=0>
<H2>U.S. Shipping Information </H2>
<HR size="1" align=left noshade>
<DL>
<DT> <B>UPS Ground Service (Continental US only -- 5-7 business
days):</B></DT>
<DD>
<PRE>
$  5.95 - $ 49.99 ......................... $ 4.50
$ 50.00 - $ 99.99 ......................... $ 6.50
$100.00 - $149.99 ......................... $ 8.50
$150.00 - $199.99 ......................... $10.50
$200.00 - $249.99 ......................... $12.50
$250.00 - $299.99 ......................... $14.50

</PRE>
<DT> <B>Federal Express:</B></DT>
(Shipping within 24 hours of receipt of order by O'Reilly)
<DD>
<PRE>
<EM>1 or 2 books</EM>:
Economy 2-day ............................. $ 8.75
Overnight Standard (Afternoon Delivery) ... $12.75
Overnight Priority (Morning Delivery) ..... $16.50
</PRE>
</DL>
<b>Alaska and Hawaii:</b> add $10 to Federal Express rates.
<P>
<A HREF="int-ship.html"><b>International Shipping Information</b></A>
<P>
<CENTER>
<HR SIZE="1" NOSHADE>
<FONT SIZE="1" FACE="Verdana, Arial, Helvetica">
<A HREF="http://www.oreilly.com/">
<B>O'Reilly Home</B></A> <B> | </B>
<A HREF="http://www.oreilly.com/sales/bookstores">
<B>O'Reilly Bookstores</B></A> <B> | </B>
<A HREF="http://www.oreilly.com/order_new/">
<B>How to Order</B></A> <B> | </B>
<A HREF="http://www.oreilly.com/oreilly/contact.html">
<B>O'Reilly Contacts<BR></B></A>
<A HREF="http://www.oreilly.com/international/">
<B>International</B></A> <B> | </B>
<A HREF="http://www.oreilly.com/oreilly/about.html">
<B>About O'Reilly</B></A> <B> | </B>
<A HREF="http://www.oreilly.com/affiliates.html">
<B>Affiliated Companies</B></A><p>
<EM>&copy; 2000, O'Reilly &amp; Associates, Inc.</EM>
</FONT>
</CENTER>
</td>
</tr>
</table>

</BODY>
</HTML>

Example 7-2 shows this document after it's been converted to XHTML. All the previously noted problems and a few more besides have been fixed. A number of deprecated presentational attributes, such as the size and noshade attributes of hr, had to be replaced with CSS styles. We've also added the necessary document type and namespace declarations. This document can now be read by both HTML and XML browsers and parsers.

Example 7-2. A valid XHTML document

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content="HTML Tidy, see www.w3.org" />
<style type="text/css">
  body      {backgroundColor: #FFFFFF; color: #000000}
  a:visited {color: #0000CC}
  a:link    {color: #990000}
</style>
<title>O'Reilly Shipping Information</title>
</head>
<body>
<table border="0" width="515">
<tr>
<td><img src="/www/graphics_new/generic_ora_header_wide.gif"
style="border-width: 0" alt="O'Reilly"/>
<h2>U.S. Shipping Information</h2>

<hr style="height: 1; text-align: left"/>
<dl>
<dt><b>UPS Ground Service (Continental US only -- 5-7 business
days):</b></dt>

<dd>
<pre>
$  5.95 - $ 49.99 ......................... $ 4.50
$ 50.00 - $ 99.99 ......................... $ 6.50
$100.00 - $149.99 ......................... $ 8.50
$150.00 - $199.99 ......................... $10.50
$200.00 - $249.99 ......................... $12.50
$250.00 - $299.99 ......................... $14.50
</pre>
</dd>

<dt><b>Federal Express:</b></dt>

<dd>(Shipping within 24 hours of receipt of order by O'Reilly)</dd>

<dd>
<pre>
<em>1 or 2 books</em>:
Economy 2-day ............................. $ 8.75
Overnight Standard (Afternoon Delivery) ... $12.75
Overnight Priority (Morning Delivery) ..... $16.50

</pre>
</dd>
</dl>

<b>Alaska and Hawaii:</b> add $10 to Federal Express rates.

<p><a href="int-ship.html"><b>International Shipping
Information</b></a></p>

<div style="font-size: xx-small; font-face: Verdana, Arial, Helvetica;
            text-align: center">
<hr style="height: 1"/>
<a
href="http://www.oreilly.com/"><b>O'Reilly Home</b></a> <b>|</b> <a
href="http://www.oreilly.com/sales/bookstores"><b>O'Reilly
Bookstores</b></a> <b>|</b> <a
href="http://www.oreilly.com/order_new/"><b>How to Order</b></a>
<b>|</b> <a href="http://www.oreilly.com/oreilly/contact.html"><b>
O'Reilly Contacts<br />
</b></a> <a href="http://www.oreilly.com/international/"><b>
International</b></a> <b>|</b> <a
href="http://www.oreilly.com/oreilly/about.html"><b>About
O'Reilly</b></a> <b>|</b> <a
href="http://www.oreilly.com/affiliates.html"><b>Affiliated
Companies</b></a></div>

<p style="font-size: xx-small;
          font-family: Verdana, Arial, Helvetica"><em>&copy; 2000,
O'Reilly &amp; Associates, Inc.</em></p>
</td>
</tr>
</table>
</body>
</html>
TIP: Making all these changes can be quite tedious for large documents or collections of many documents. Fortunately, there's an open source tool that can do most of the work for you. Dave Ragget's Tidy, http://tidy.sourceforge.net, is a C program that has been ported to most major operating systems and can convert some pretty nasty HTML into valid XHTML. For example, to convert the file bad.html to good.xml, you would type:

% tidy --output-xhtml yes bad.html good.xml

Tidy fixes as much as it can and warns you about what it can't fix so you can fix it manually--for instance, telling you that a required alt attribute is missing from an img element.

7.1.2. Three DTDs for XHTML

XHTML comes in three flavors, depending on which DTD you choose:

Strict
This is the W3C's recommended form of XHTML. This includes all the basic elements and attributes such as p and class. However, it does not include deprecated elements and attributes such as applet and center. It also forbids the use of presentational attributes such as the body element's bgcolor, vlink, link, and text. These capabilities are provided by CSS instead. Strict XHTML is identified with this DOCTYPE declaration:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                      "DTD/xhtml1-strict.dtd" >

Example 7-2 used this DTD.

Transitional
This is a looser form of XHTML for when you can't easily do without deprecated elements and attributes such as applet and bgcolor. It is identified with this DOCTYPE declaration:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "DTD/xhtml1-transitional.dtd" >
Frameset
This is the same as the transitional DTD except that it also allows frame-related elements such as frameset and iframe. It is identified with this DOCTYPE declaration:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
                      "DTD/xhtml1-frameset.dtd" >

All three DTDs use the same http://www.w3.org/1999/xhtml namespace. You should choose the strict DTD unless you've got a specific reason to use another one.

7.1.3. Browser Support for XHTML

Many current web browsers, especially Internet Explorer 5.0 and earlier and Netscape 4.79 and earlier, deal inconsistently with XHTML. Certainly they don't require it, accepting as they do such a wide variety of malformed, invalid, and out-and-out mistaken HTML. However, beyond that they do have some problems when they encounter certain common XHTML constructs.

7.1.3.1. The XML declaration and processing instructions

Some browsers display processing instructions and the XML declaration inline. These should be omitted if possible.

Few, if any, browsers recognize or respect the encoding declaration in the XML declaration. Furthermore, many browsers won't automatically recognize UTF-8 or UCS-2 Unicode text. If you use a non-ASCII character set, you should also include a meta element in the header identifying the character set. For example:

<meta http-equiv="Content-type"
      content='text/html; charset=UTF-8'></meta>

7.1.3.2. Empty elements

Browsers deal inconsistently with both forms of empty element syntax. That is, some browsers understand <hr/> but not <hr></hr> (typically rendering it as two horizontal lines rather than one), while others recognize <hr></hr> but not <hr/> (typically omitting the horizontal line completely). The most consistent rendering seems to be achieved by using an empty-element tag with an optional attribute such as class or id, for example, <hr class="empty" />. There's no real reason for the class attribute here, except that its presence keeps browsers from choking on the />. Any other attribute the DTD allows would serve equally well.

On the other hand, if a particular instance of an element happens to be empty, but not all instances of the element have to be empty--for instance, a p that doesn't contain any text--you should use two tags like <p></p> rather than one empty-element tag <p/>.

7.1.3.3. Entity references

Embedded scripts often contain reserved characters like & or < so the document that contains them is not well-formed. However, most JavaScript and VBScript interpreters won't recognize &amp; or &lt; in place of the operators they represent. If the script can't be rewritten without these operators (for instance, by changing a less-than comparison to a greater-than-or-equal-to comparison with the arguments flipped), then you should move to external scripts instead of embedded ones.

Furthermore, most non-XML-aware browsers don't recognize the &apos; predefined entity reference. You should avoid this if possible and just use the literal ' character instead. The only place this might be a problem is inside attribute values that are enclosed in single quotes because they contain double quotes. However, most browsers do recognize the &quot; entity reference for the " character so you can enclose the attribute value in double quotes and escape the double quotes that are part of the attribute value as &quot;.

7.1.3.4. Other unsupported features

There are a few other subtle differences between how browsers handle XHTML and how XHTML expects to be handled. For instance, XHTML allows character references and CDATA sections although almost no current browsers understand these constructs. However, you're unlikely to encounter these when converting from HTML to XHTML, and you can generally do without them if you're writing XHTML from scratch.

Mozilla, Opera 5.0 and later, Internet Explorer 5.5 and later, and Netscape 6.0 and later can parse and display valid XHTML without any difficulties and without requiring page authors to jump through these hoops. However, since many users have not upgraded their browsers to the level XHTML requires, user-friendly web designers will be jumping through these hoops for some years to come.

Previous | Home | Next
6.6. Transformation and Presentation | Book Index | 7.2. Direct Display of XML in Browsers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Direct Display of XML in Browsers (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

7.2. Direct Display of XML in Browsers

Ultimately, one hopes that browsers will be able to display not just XHTML documents but any XML document as well. Since it's too much to ask that browsers provide semantics for all XML applications both current and yet-to-be-invented, stylesheets will be attached to each document to provide instructions about how each element will be rendered.

The current major stylesheet languages are:

  • Cascading Style Sheets Level 1 (CSS1)

  • Cascading Style Sheets Level 2 (CSS2)

  • XSL Transformations 1.0

Eventually, there will be still more versions of these, including at least CSS3 and XSLT 2.0. However, let's begin by looking at how and how well existing style languages are supported by existing browsers.

7.2.1. The xml-stylesheet Processing Instruction

The stylesheet associated with a document is indicated by an xml-stylesheet processing instruction in the document's prolog, which comes after the XML declaration but before the root element start-tag. This processing instruction uses pseudoattributes to describe the stylesheet (that is, they look like attributes but are not attributes because xml-stylesheet is a processing instruction and not an element).

7.2.1.1. The required href and type pseudoattributes

There are two required pseudoattributes for xml-stylesheet processing instructions. The value of the href pseudoattribute gives the URL, possibly relative, where the stylesheet can be found. The type pseudoattribute value specifies the MIME media type of the stylesheet, text/css for cascading stylesheets, application/xml for XSLT stylesheets. In Example 7-3, the xml-stylesheet processing instruction tells browsers to apply the CSS stylesheet person.css to this document before showing it to the reader.

Example 7-3. A very simple yet complete XML document

<?xml version="1.0"?>
<?xml-stylesheet href="person.css" type="text/css"?>
<person>
  Alan Turing
</person>
TIP: Microsoft Internet Explorer uses type="text/xsl" for XSLT stylesheets. However, the text/xsl MIME media type has not been and will not be registered with the IANA. It is a figment of Microsoft's imagination. In the future, application/xslt+xml will probably be registered to identify XSLT stylesheets specifically.

In addition to these two required pseudoattributes, there are four optional pseudoattributes:

  • media

  • charset

  • alternate

  • title

7.2.1.2. The media pseudoattribute

The media pseudoattribute contains a short string identifying for which medium this stylesheet should be used, for example, paper, onscreen display, television, and so forth. It can specify either a single medium or a comma-separated list of mediums. The recognized values include:

screen
Computer monitors

tty
Teletypes, terminals, xterms, and other monospaced, text-only devices

tv
Televisions, WebTVs, video game consoles, and the like

projection
Slides, transparencies, and direct-from-laptop presentations that will be shown to an audience on a large screen

handheld
PDAs, cell phones, GameBoys, and the like

print
Paper

braille
Tactile feedback devices for the sight-impaired

aural
Screen readers and speech synthesizers

all
All of the previously mentioned plus any that haven't been invented yet

For example, this xml-stylesheet processing instruction says that the CSS stylesheet at the URL http://www.cafeconleche.org/style/titus.css should be used for television, projection, and print:

<?xml-stylesheet href="http://www.cafeconleche.org/style/titus.css"
                 type="text/css" media="tv, projection, print"?>

7.2.1.3. The charset pseudoattribute

The charset pseudoattribute specifies in which character set the stylesheet is written, using the same values as the encoding declaration uses. For example, to say that the CSS stylesheet koran.css is written in the ISO-8859-6 character set, you'd use this processing instruction:

<?xml-stylesheet href="koran.css" type="text/css" charset="ISO-8859-6"?>

7.2.1.4. The alternate and title pseudoattributes

The alternate pseudoattribute specifies whether this is the primary stylesheet for its media type or an alternate one for special cases. The default value is no, which indicates that it is the primary stylesheet. If alternate has the value yes, then the browser may (but does not have to) present the user a choice from among the alternate stylesheets. If it does offer a choice, then it uses the value of the title pseudoattribute to tell the user how the stylesheets differ. For example, these three xml-stylesheet processing instructions offer the user a choice between large, small, and medium text:

<?xml-stylesheet href="big.css" type="text/css"
                 alternate="yes" title="Large fonts"?>
<?xml-stylesheet href="small.css" type="text/css"
                 alternate="yes" title="Small fonts"?>
<?xml-stylesheet href="medium.css" type="text/css" title="Normal fonts"?>

Browsers that aren't able to ask the user to choose a stylesheet will simply pick the first nonalternate sheet that most closely matches its media-type (screen for a typical web browser).

7.2.2. Internet Explorer

Microsoft Internet Explorer 4.0 (IE4) includes an XML parser that can be accessed from VBScript or JavaScript. This is used internally to support channels and the Active Desktop. Your own JavaScript and VBScript programs can use this parser to read XML data and insert it into the web page. However, anything more straightforward, like simply displaying a page of XML from a specified URL, is beyond IE4's capabilities. Furthermore, IE4 doesn't understand any stylesheet language when applied to XML.

Internet Explorer 5 (IE5) and 5.5 (IE 5.5) do understand XML, though their parser is more than a little buggy; it rejects a number of documents it shouldn't reject, most embarrassingly the XML 1.0 specification itself. Internet Explorer 6 (IE6) has improved XML support somewhat, but is still not completely conformant.

IE5 and later can directly display XML files, with or without an associated stylesheet. If no stylesheet is provided, then IE5 uses a default, built-in XSLT stylesheet that displays the tree structure of the XML document along with a little DHTML to allow the user to collapse and expand nodes in the tree. Figure 7-1 shows IE5 displaying Example 6-1 from the last chapter.

Figure 7-1. A document that uses IE5's built-in stylesheet

IE5 also supports parts of CSS Level 1 and a little of CSS Level 2. However, the support is spotty and inconsistent. Even some aspects of CSS that work for HTML documents fail when applied to XML documents. IE 5.5 and IE6 slightly improve coverage of CSS, but don't support all CSS properties and selectors. In fact, many CSS features that work in IE6 for HTML still don't work when applied to XML documents.

IE5 and IE 5.5 support their own custom version of XSLT, based on a very early working draft of the XSLT specification. They do not support XSLT 1.0. You can tell the difference by looking at the namespace of the stylesheet. A stylesheet written for IE5 uses the http://www.w3.org/TR/WD-xsl namespace, whereas a stylesheet designed for standard-compliant XSLT processors uses the http://www.w3.org/1999/XSL/Transform namespace. Despite superficial similarities, these two languages are not compatible. A stylesheet written for IE5 will not work with any other XSLT processor, and a stylesheet written using standard XSLT 1.0 will not work in IE5. IE6 supports both real XSLT and Microsoft's nonstandard dialect.

7.2.3. Netscape and Mozilla

Netscape 4.x and earlier do not provide any significant support for displaying XML in the browser. Netscape 4.0.6 and later do use XML internally for some features such as "What's Related." However, the parser used isn't accessible to the page author, even through JavaScript.

Mozilla and Netscape 6.0 do fully support display of XML in the browser. CSS Level 2 is completely supported. Mozilla can read an XML web page, download the associated CSS stylesheet, apply it to the document, and display the result to the end user, all completely automatically and more or less exactly as XML on the Web was always meant to work.

Netscape 6.2 also supports XSLT 1.0, but at the time of this writing the support is fairly buggy, and really hard to get working. You have to serve the files from a web server (not the local disk) and the web server must supply the exactly right MIME media types for the XML document and its stylesheet. Even then the transform fails about half the time. Mozilla 1.0 does have the best XSLT support of any current browser, so it seems likely that future versions of Netscape 6 (which is based on earlier betas of Mozilla) will do a better job. Mozilla also partially supports MathML; and there's a third party effort underway to support SVG graphics. However, this probably won't be ready for Mozilla 1.0.

7.2.4. Alternative Approaches

Authoring your web pages in XML does not necessarily require serving them in XML. Fourth-generation and earlier browsers that don't support XML in any significant way will be with us for years to come. Servicing users with these browsers requires standard, ordinary HTML that works in any browser back to Mosaic 1.0.

One popular option is to write the pages in XML, but serve them in HTML. When the server receives a request for an XML document, it automatically converts the document to HTML and sends the converted document instead. More sophisticated servers can cache the converted documents. They can also recognize browsers that support XML and send them the raw XML instead.

The preferred way to perform the conversion is with an XSLT stylesheet and a Java servlet. Indeed, most XSLT engines such as Xalan-J and SAXON include servlets that do exactly this. However, other schemes are possible, for instance, using PHP or CGI instead of a servlet. The key is to make sure that browsers only receive what they know how to read and display. We'll talk more about XSLT in the next chapter.

Previous | Home | Next
7. XML on the Web | Book Index | 7.3. Authoring Compound Documents with Modular XHTML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Authoring Compound Documents with Modular XHTML (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

7.3. Authoring Compound Documents with Modular XHTML

XHTML 1.1 divides the three XHTML DTDs into individual modules. Parameter entities connect the modules by including or leaving out particular modules. Modules include:

Structure Module, %xhtml-struct.module;
The absolute bare minimum of elements needed for an HTML document: html, head, title, and body

Text Module, %xhtml-text.module;
The basic elements that contain text and other inline elements: abbr, acronym, address, blockquote, br, cite, code, dfn, div, em, h1, h2, h3, h4, h5, h6, kbd, p, pre, q, samp, span, strong, and var

Hypertext Module, %xhtml-hypertext.module;
Elements used for linking, that is, the a element

List Module, %xhtml-list.module;
Elements used for the three kinds of lists: dl, dt, dd, ul, ol, and li

Applet Module, %xhtml-applet.module;
Elements needed for Java applets: applet and param

Presentation Module, %xhtml-pres.module;
Presentation oriented markup, that is, the b, big, hr, i, small, sub, sup, and tt elements

Edit Module, %xhtml-edit.module;
Elements for revision tracking: del and ins

Bi-directional Text Module, %xhtml-bdo.module;
An indication of directionality when text in left-to-right languages, like English and French, is mixed with text in right-to-left languages, like Hebrew and Arabic

Basic Forms Module, %xhtml-basic-form.module;
Forms as defined in HTML 3.2 using the form, input, select, option, and textarea elements

Forms Module, %xhtml-form.module;
Forms as defined in HTML 4.0 using the form, input, select, option, textarea, button, fieldset, label, legend, and optgroup elements

Basic Tables Module, %xhtml-basic-table.module;
Minimal table support including only the table, caption, th, tr, and td elements

Tables Module, %xhtml-table.module;
More complete table support including not only the table, caption, th, tr, and td elements, but also the col, colgroup, tbody, thead, and tfoot elements

Image Module, %xhtml-image.module;
The img element

Client-side Image Map Module, %xhtml-csismap.module;
The map and area elements, as well as extra attributes for several other elements needed to support client-side image maps

Server-side Image Map Module, %xhtml-ssismap.module;
Doesn't provide any new elements, but adds the ismap attribute to the img element

Object Module, %xhtml-object.module;
The object element used to embed executable content like Java applets and ActiveX controls in web pages

Param Module, %xhtml-param.module;
Used to pass parameters from web pages to their embedded executable content like Java applets and ActiveX controls

Frames Module, %xhtml-frames.module;
The elements needed to implement frames including frame, frameset, and noframes

Iframe Module %xhtml-iframe.mod;
The iframe element used for inline frames

Intrinsic Events, %xhtml-events.module;
Attributes to support scripting like onsubmit and onfocus that are attached to elements declared in other modules

Metainformation Module, %xhtml-meta.module;
The meta element used in headers

Scripting Module, %xhtml-script.module;
Elements that support JavaScript and VBScript: script and noscript

Stylesheet Module, %xhtml-style.module;
The style element used to define Cascading Style Sheets

Link Module, %xhtml-link.module;
The link element that specifies relationships to various external documents such as translations, glossaries, and previous and next pages

Base Module, %xhtml-base.module;
The base element that specifies a URL against which relative URLs are resolved

Target Module, %xhtml-target.module;
The target attribute used to specify the destination frame or window of a link

Style Attribute Module, %xhtml-inlstyle.module;
The style attribute used to attach CSS styles to individual elements in the document

Name Identification Module, %xhtml-nameident.module;
The name attribute that is a deprecated earlier version of the id attribute

Legacy Module, %xhtml-legacy.module;
Deprecated elements and attributes including the basefont, center, font, s, strike, and u elements

Ruby Module, %xhtml11-ruby.module;
The ruby, rbc, rtc, rb, rt, and rp elements used in East Asian text to place small amounts of text next to the body text, generally indicating pronunciation

7.3.1. Mixing XHTML into Your Applications

The advantage to dividing HTML into all these different modules is that you can pick and choose the pieces you want. If your documents use tables, you include the table module. If your documents don't use tables, then you can leave it out. You get only the functionality you actually need.

For example, let's suppose you're designing a DTD for a catalog. Each item in the catalog is a catalog_entry element. Each catalog_entry contains a name, a price, an item_number, a color, a size, and various other common elements you're likely to find in catalogs. Furthermore, each catalog_entry contains a description of the item. The description contains formatted narrative text. In other words, it looks something like this:

  <catalog_entry>
    <name>Aluminum Duck Drainer</name>
    <price>34.99</price>
    <item_number>54X8</item_number>
    <color>silver</color>
    <size>XL</size>
    <description>
      <p>
       This sturdy <strong>silver</strong> colored
       sink stopper dignifies the <em>finest
       kitchens</em>. It makes a great gift for
      </p>
      <ul>
        <li>Christmas</li>
        <li>Birthdays</li>
        <li>Mother's Day</li>
      </ul>
      <p>and all other occasions!</p>
    </description>
  </catalog_entry>

It's easy enough to write this markup. The tricky part is validating it. Rather than reinventing a complete DTD to describe all the formatting that's needed in straightforward narrative descriptions, you can reuse XHTML. The XHTML 1.1 DTD makes heavy use of parameter entity references to define content specifications and attribute lists for the different elements. Three entity references are of particular note:

%Inline.mix;
A choice containing all the elements that don't generally require a line break such as em, a, and q. That is, it resolves to:

br | span | em | strong | dfn | code | samp | kbd | var | cite | abbr |
acronym | q | tt | i | b | big | small | sub | sup | bdo | a | img | map
| applet | ruby | input | select | textarea | label | button | ins | del
| script | noscript
%Block.mix;
A choice containing all the elements that generally require a line break like p, blockquote, and ul. That is, it resolves to:

h1 | h2 | h3| h4 | h5 | h6| ul| ol| dl| p | div | pre| blockquote
| address | hr | table | form | fieldset | ins | del | script | noscript
%Flow.mix;
A choice containing both of the previous; that is, it resolves to:

h1 | h2 | h3 | h4 | h5 | h6 | ul | ol | dl | p | div | pre | blockquote
| address | hr | table | form | fieldset | br | span | em | strong | dfn
| code | samp | kbd | var | cite | abbr | acronym | q | tt | i | b | big
| small | sub | sup | bdo | a | img | map | applet | ruby | input |
select | textarea | label | button | ins | del | script | noscript

You can declare that the description element contains essentially any legal XHTML fragment, like this:

<!ENTITY % xhtml PUBLIC "-//W3C//DTD XHTML 1.1//EN" "xhtml11.dtd">
%xhtml;
<!ELEMENT description (#PCDATA | %Flow.mix;)*>

If you wanted to require description to contain only block elements at the top level, you'd instead declare it like this:

<!ENTITY % xhtml PUBLIC "-//W3C//DTD XHTML 1.1//EN" "xhtml11.dtd">
%xhtml;
<!ELEMENT description ((%Block.mix;)*)>

The first two lines import the XHTML driver DTD from a relative URL. You can get this DTD and the other local files it depends on from the zip archive at http://www.w3.org/TR/xhtml11/xhtml11.zip. The second line uses an entity reference defined in that DTD to set the content specification for the description element.

TIP: The XHTML 1.1 driver DTD imports modules from two other W3C specifications, Modularization of XHTML (http://www.w3.org/TR/xhtml-modularization) and Ruby Annotation (http://www.w3.org/TR/ruby), using absolute URLs that point to the W3C's web site. If you're not reliably connected to the Internet at high speed, you might want to use the flat version of this DTD, xhtml11-flat.dtd, instead. This bundles all the different modules in a single file.

Unfortunately, this goes a little too far. It includes not only the pieces of HTML you want, such as p, em, and ul, but also a lot of elements you don't want in a printed catalog, such as a, applet, map, and a lot more. However, you can omit these. The main XHTML DTD imports each module inside an INCLUDE/IGNORE block, such as this one for the hypertext module:

<!-- Hypertext Module (required) ................................. -->
<!ENTITY % xhtml-hypertext.module "INCLUDE" >
<![%xhtml-hypertext.module;[
<!ENTITY % xhtml-hypertext.mod
 PUBLIC "-//W3C//ELEMENTS XHTML Hypertext 1.0//EN"
   "http://www.w3.org/TR/xhtml-modularization/DTD/xhtml-hypertext-1.mod" >
%xhtml-hypertext.mod;]]>

If the %xhtml-hypertext.module; parameter entity reference has previously been defined as IGNORE instead of INCLUDE, that declaration takes precedence; all the elements and attributes defined in the hypertext module (specifically, the a element) are left out of the resulting DTD.

Let's say you just want the Structure, Basic Text, and List modules. Then you use a driver DTD that redefines the parameter entity references for the other modules as IGNORE. Example 7-4 demonstrates.

Example 7-4. A catalog DTD that uses basic XHTML but omits a lot of elements

<!ELEMENT catalog (catalog_entry*)>
<!ELEMENT catalog_entry (name, price, item_number, color, size, description)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT size (#PCDATA)>
<!ELEMENT price (#PCDATA)>
<!ELEMENT item_number (#PCDATA)>
<!ELEMENT color (#PCDATA)>

<!-- throw away the modules we don't need -->
<!ENTITY % xhtml-hypertext.module "IGNORE" >
<!ENTITY % xhtml-ruby.module      "IGNORE" >
<!ENTITY % xhtml-edit.module      "IGNORE" >
<!ENTITY % xhtml-pres.module      "IGNORE" >
<!ENTITY % xhtml-applet.module    "IGNORE" >
<!ENTITY % xhtml-param.module     "IGNORE" >
<!ENTITY % xhtml-bidi.module      "IGNORE" >
<!ENTITY % xhtml-form.module      "IGNORE" >
<!ENTITY % xhtml-table.module     "IGNORE" >
<!ENTITY % xhtml-image.module     "IGNORE" >
<!ENTITY % xhtml-csismap.module   "IGNORE" >
<!ENTITY % xhtml-ssismap.module   "IGNORE" >
<!ENTITY % xhtml-meta.module      "IGNORE" >
<!ENTITY % xhtml-script.module    "IGNORE" >
<!ENTITY % xhtml-style.module     "IGNORE" >
<!ENTITY % xhtml-link.module      "IGNORE" >
<!ENTITY % xhtml-base.module      "IGNORE" >

<!-- import the XHTML DTD, at least those parts we aren't ignoring.
     You will probably need to change the system ID to point to
     whatever directory you've stored the DTD in.
-->
<!ENTITY % xhtml11.mod PUBLIC "-//W3C//DTD XHTML 1.1//EN"
                              "xhtml11/DTD/xhtml11.dtd">
%xhtml11.mod;

<!ELEMENT description ( %Block.mix; )+>

7.3.2. Mixing Your Applications into XHTML

An even more important feature of Modular XHTML is the option to add new elements that HTML doesn't support. For instance, to include SVG pictures in your documents, you just have to import the SVG DTD and redefine the Misc.extra parameter entity to allow the SVG root element svg. (This only lets you validate XHTML document that contain SVG markup. It doesn't magically give the browser the ability to render these pictures.) You accomplish this by redefining any of three parameter entity references:

%Inline.extra;
Place the root elements of your application here if you want them to be added to the content specifications of inline elements such as span, em, code, and textarea.

%Block.extra;
Place the root elements of your application here if you want them to be added to the content specifications of block elements such as div, h1, p, and pre.

%Misc.extra;
Place the root elements of your application here if you want them to be added to the content specifications of both block and inline elements.

The definition of each of these parameter entities should be a list of the elements you want to add to the content specification separated by vertical bars and beginning with a vertical bar. For instance, to include MathML equations as both inline and block elements, you'd import the MathML DTD and redefine the Misc.extra parameter entity to include the MathML root element math like this:

<!ENTITY % Misc.extra "| math">

If you wanted to allow block-level MathML equations and SVG pictures, you'd import their respective DTDs and redefine the Block.extra parameter entity like this:

<!ENTITY % Block.extra "| math | svg">

Order is important here. The MathML DTD and the Block.extra declaration both have to be parsed before the XHTML DTD is parsed. Example 7-5 demonstrates with a DTD that mixes MathML 1.0 and XHTML, throwing in a namespace declaration for good measure.

Example 7-5. A DTD that mixes MathML into XHTML and MathML

<!ENTITY % mathml SYSTEM "mathml/mathml.dtd">
%mathml;

<!ATTLIST math xmlns CDATA #FIXED "http://www.w3.org/1998/Math/MathML">

<!ENTITY % Misc.extra "| math">

<!ENTITY % xhtml PUBLIC "-//W3C//DTD XHTML 1.1//EN" "xhtml11/DTD/xhtml11.dtd">
%xhtml;

You can also mix new elements like math into individual elements like p without changing all the other block elements. The content specification for each XHTML element is defined by a parameter entity named Element.content, for example, %p.content;, %em.content;, %td.content; and so forth. The standard definition of p.content looks like this:

<!ENTITY % p.content
     "( #PCDATA | %Inline.mix; )*" >

To allow the math element to be a child of p elements, but not of every other block element, you would redefine p.content like this:

<!ENTITY % p.content "( #PCDATA | %Inline.mix; | math )*" >

The XHTML 1.1 DTD is quite sophisticated. There are a lot more tricks you can play by mixing and matching different parts of the DTD, mostly by defining and redefining different parameter entity references. The easiest way to learn about these is by reading the raw DTDs. In many cases, the comments in the DTD are more descriptive and accurate than the prose specification.

7.3.3. Mixing Your Own XHTML

The XHTML 1.1 DTD does not include all of the modules that are available. For instance, frames and the legacy presentational elements are deliberately omitted and cannot easily be turned on. This is the W3C's not-so-subtle way of telling you that you shouldn't be using these elements in the first place. If you do want to use them, you'll need to create your own complete DTD using the individual modules you require.

To do this, first you must define the namespace URI and prefixed names for your elements and attributes. The W3C provides a template you can adapt for this purpose at http://www.w3.org/TR/xhtml-modularization/DTD/templates/template-qname-1.mod. Example 7-6 demonstrates with a DTD fragment that defines the names for the today and quoteoftheday elements that one of the authors uses on his web sites. The module is based on the W3C-provided template.

Example 7-6. A DTD module to define the today and quoteoftheday elements' names and namespaces

<!-- ........................................................... -->
<!-- CafeML Qualified Names Module ............................. -->
<!-- file: cafe-qname-1.mod

     This is an extension of XHTML, a reformulation of HTML as
     a modular XML application.

     This DTD module is identified by the PUBLIC and SYSTEM identifiers:

PUBLIC "-//Elliotte Rusty Harold//ELEMENTS CafeML Qualified Names 1.0//EN"
       "cafe-qname-1.mod"

     Revisions:
     (none)
     ........................................................... -->

<!-- NOTES:  Using the CafeML Qualified Names Extension

     This is a module for a markup language 'CafeML',
     which currently declares two extension elements, quoteoftheday
     and today. The parameter entity naming convention uses uppercase
     for the entity name and lowercase for namespace prefixes, hence
     this example uses 'CAFEML' and 'cafeml' respectively.

     Please note the three case variants:

         'CafeML'   the human-readable markup language name
         'CAFEML'   used as a parameter entity name prefix
         'cafeml'   used as the default namespace prefix

     The %NS.prefixed; conditional section keyword must be declared
     as "INCLUDE" in order to allow prefixing be used.
-->

<!-- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -->

<!-- CafeML Qualified Names

     This module is contained in two parts, labeled Section 'A' and 'B':

       Section A declares parameter entities to support namespace-
       qualified names, namespace declarations, and name prefixing
       for CafeML.

       Section B declares parameter entities used to provide
       namespace-qualified names for all CafeML element types.

     The recommended step-by-step program for creating conforming
     modules is enumerated below, and spans both the CafeML Qualified
     Names Template and CafeML Extension Template modules.
-->
<!-- Section A: CafeML XML Namespace Framework :::::::::::::::::::: -->

<!-- 1. Declare a %CAFEML.prefixed; conditional section keyword, used
        to activate namespace prefixing. The default value should
        inherit '%NS.prefixed;' from the DTD driver, so that unless
        overridden, the default behavior follows the overall DTD
        prefixing scheme.
-->
<!ENTITY % NS.prefixed "IGNORE" >
<!ENTITY % CAFEML.prefixed "%NS.prefixed;" >

<!-- 2. Declare a parameter entity (e.g., %CAFEML.xmlns;) containing
        the URI reference used to identify the Module namespace:
-->
<!ENTITY % CAFEML.xmlns  "http://www.cafeconleche.org/xmlns/cafeml" >

<!-- 3. Declare parameter entities (eg., %CAFEML.prefix;) containing
        the default namespace prefix string(s) to use when prefixing
        is enabled. This may be overridden in the DTD driver or the
        internal subset of a document instance. If no default prefix
        is desired, this may be declared as an empty string.

     NOTE: As specified in [XMLNAMES], the namespace prefix serves
     as a proxy for the URI reference, and is not in itself significant.
-->
<!ENTITY % CAFEML.prefix  "cafeml" >

<!-- 4. Declare parameter entities (eg., %CAFEML.pfx;) containing the
        colonized prefix(es) (eg., '%CAFEML.prefix;:') used when
        prefixing is active, an empty string when it is not.
-->
<![%CAFEML.prefixed;[
<!ENTITY % CAFEML.pfx  "%CAFEML.prefix;:" >
]]>
<!ENTITY % CAFEML.pfx  "" >

<!-- 5. The parameter entity %CAFEML.xmlns.extra.attrib; may be
        redeclared to contain any non-CafeML namespace declaration
        attributes for namespaces embedded in CafeML. When prefixing
        is active it contains the prefixed xmlns attribute and any
        namespace declarations embedded in CafeML, otherwise an empty
        string.
-->
<![%CAFEML.prefixed;[
<!ENTITY % CAFEML.xmlns.extra.attrib
      "xmlns:%CAFEML.prefix; %URI.datatype;   #FIXED '%CAFEML.xmlns;'" >
]]>
<!ENTITY % CAFEML.xmlns.extra.attrib "" >

<!ENTITY % XHTML.xmlns.extra.attrib
      "%CAFEML.xmlns.extra.attrib;"
>

<!-- Section B: CafeML Qualified Names ::::::::::::::::::::::::::::: -->

<!-- This section declares parameter entities used to provide
     namespace-qualified names for all CafeML element types.
-->
<!-- module:  cafe-1.mod -->
<!ENTITY % CAFEML.quoteoftheday.qname  "%CAFEML.pfx;quoteoftheday" >
<!ENTITY % CAFEML.today.qname   "%CAFEML.pfx;today" >

<!-- end of cafe-qname-1.mod -->

Next you have to define the elements and attributes with these names in a module of your own creation. The W3C provides a template, which you can adapt for this purpose, at http://www.w3.org/TR/xhtml-modularization/DTD/templates/template-1.mod. This template uses the same techniques and follows the same patterns as XHTML's built-in modules, for example, parameter entity references that resolve to INCLUDE or IGNORE.

Example 7-7 demonstrates with a DTD fragment that defines the today and quoteoftheday elements. The today element can contain any block-level content through the Block.mix parameter entity and has a required date attribute. The quoteoftheday element always contains exactly one blockquote element followed by exactly one p element with no attributes.

Example 7-7. A DTD module to define the today and quoteoftheday elements

<!-- ............................................................ -->
<!-- CAFEML Extension Template Module ........................... -->
<!-- file: CafeML-1.mod

     This is an extension of XHTML, a reformulation of HTML as
     a modular XML application.

     This DTD module is identified by the PUBLIC and SYSTEM identifiers:

       PUBLIC "Elliotte Rusty Harold//ELEMENTS CafeML Qualified Names 1.0//EN"
       SYSTEM "CafeML-1.mod"

     Revisions:
     (none)
     ........................................................... -->

<!-- Extension Template

     This sample template module declares two extension elements,
     today and quoteoftheday. The parameter entity naming
     convention uses uppercase for the entity name and lowercase
     for namespace prefixes. Hence this example uses 'CAFEML' and
     'cafe' respectively.

     This module declares parameter entities used to provide
     namespace-qualified names for all CAFEML element types,
     as well as an extensible framework for attribute-based
     namespace declarations on all element types.

     The %NS.prefixed; conditional section keyword must be
     declared as "INCLUDE" in order to allow prefixing to be used.
     By default, foreign (i.e., non-XHTML) namespace modules should
     inherit %NS.prefixed; from XHTML, but this can be overridden
     when prefixing of only the non-XHTML markup is desired.

     XHTML's default value for the 'namespace prefix' is an empty
     string. The Prefix value can be redeclared either in a DTD
     driver or in a document's internal subset as appropriate.

     NOTE: As specified in [XMLNAMES], the namespace prefix serves as
     a proxy for the URI reference, and is not in itself significant.
-->

<!-- ................................................................  -->

<!-- 1. Declare the xmlns attributes used by CAFEML dependent on whether
        CAFEML's prefixing is active. This should be used on all CAFEML
        element types as part of CAFEML's common attributes.

        If the entire DTD is namespace-prefixed, CAFEML should inherit
        %NS.decl.attrib;. Otherwise it should declare %NS.decl.attrib;
        plus a default xmlns attribute on its own element types.
-->
<![%CAFEML.prefixed;[
<!ENTITY % CAFEML.xmlns.attrib
     "%NS.decl.attrib;"
>
]]>
<!ENTITY % CAFEML.xmlns.attrib
     "xmlns        %URI.datatype;           #FIXED '%CAFEML.xmlns;'"
>

<!-- now include the module's various markup declarations ........ -->

<!ENTITY % CAFEML.Common.attrib
     "%CAFEML.xmlns.attrib;
      id           ID                       #IMPLIED"
>

<!-- 2. In the attribute list for each element, declare the XML Namespace
        declarations that are legal in the document instance by including
        the %NamespaceDecl.attrib; parameter entity in the ATTLIST of
        each element type.
-->

<!ENTITY % CAFEML.today.qname  "today" >
<!ELEMENT %CAFEML.today.qname;  ( %Flow.mix; )* >
<!ATTLIST %CAFEML.today.qname;
      %CAFEML.Common.attrib;
      date CDATA #REQUIRED
>

<!ENTITY % CAFEML.quoteoftheday.qname  "quoteoftheday" >
<!ELEMENT %CAFEML.quoteoftheday.qname;  ( %blockquote.qname;,
                                          %p.qname; ) >
<!ATTLIST %CAFEML.quoteoftheday.qname;
      %CAFEML.Common.attrib;
>

<!-- 3. If the module adds attributes to elements defined in modules that
        do not share the namespace of this module, declare those
        attributes so that they use the %CAFEML.pfx; prefix. For example:

<!ENTITY % CAFEML.img.myattr.qname "%CAFEML.pfx;myattr" >
<!ATTLIST %img.qname;
      %CAFEML.img.myattr.qname;  CDATA      #IMPLIED
>

 This would add a myattr attribute to the img element of the Image Module,
 but the attribute's name will be the qualified name, including prefix,
 when prefixes are selected for a document instance.

 We do not need to do this for this module.

-->

<!-- end of CafeML-1.mod -->

Next you need to write a document model module that defines the parameter entities used for content specifications in the various modules--not only the CafeML modules, but the XHTML modules as well. (This is how your elements become part of the various XHTML elements.) The W3C does not provide a template for this purpose. However, it's normally easy to adapt the document model module from either XHTML 1.1 or XHTML Basic to include your new elements. Example 7-8 is a document model module based on the XHTML 1.1 document model module.

Example 7-8. A document model module for CafeML

<!-- ............................................................ -->
<!-- CafeML Model Module  ....................................... -->
<!-- file: CafeML-model-1.mod

  PUBLIC "-//Elliotte Rusty Harold//ELEMENTS XHTML CafeML Model 1.0//EN"
  SYSTEM "CafeML-model-1.mod"

     xmlns:cafeml="http://www.cafeconleche.org/xmlns/cafeml"
     ............................................................ -->

<!-- Define the content model for Misc.extra -->
<!ENTITY % Misc.extra
     "| %CAFEML.today.qname; | %CAFEML.quoteoftheday.qname; ">

<!-- ....................  Inline Elements  ..................... -->

<!ENTITY % HeadOpts.mix
     "( %meta.qname; )*" >

<!ENTITY % I18n.class "" >

<!ENTITY % InlStruct.class "%br.qname; | %span.qname;" >

<!ENTITY % InlPhras.class
     "| %em.qname; | %strong.qname; | %dfn.qname; | %code.qname;
      | %samp.qname; | %kbd.qname; | %var.qname; | %cite.qname;
      | %abbr.qname; | %acronym.qname; | %q.qname;" >

<!ENTITY % InlPres.class "" >

<!ENTITY % Anchor.class "| %a.qname;" >

<!ENTITY % InlSpecial.class "| %img.qname; " >

<!ENTITY % Inline.extra "" >

<!-- %Inline.class; includes all inline elements,
     used as a component in mixes
-->
<!ENTITY % Inline.class
     "%InlStruct.class;
      %InlPhras.class;
      %InlPres.class;
      %Anchor.class;
      %InlSpecial.class;"
>

<!-- %InlNoAnchor.class; includes all non-anchor inlines,
     used as a component in mixes
-->

<!ENTITY % InlNoAnchor.class
     "%InlStruct.class;
      %InlPhras.class;
      %InlPres.class;
      %InlSpecial.class;"
>

<!-- %InlNoAnchor.mix; includes all non-anchor inlines
-->
<!ENTITY % InlNoAnchor.mix
     "%InlNoAnchor.class;
      %Misc.class;"
>

<!-- %Inline.mix; includes all inline elements, including %Misc.class;
-->
<!ENTITY % Inline.mix
     "%Inline.class;
      %Misc.class;"
>

<!-- .....................  Block Elements  ...................... -->
<!ENTITY % Heading.class
     "%h1.qname; | %h2.qname; | %h3.qname;
      | %h4.qname; | %h5.qname; | %h6.qname;" >

<!ENTITY % List.class "%ul.qname; | %ol.qname; | %dl.qname;" >

<!ENTITY % BlkStruct.class "%p.qname; | %div.qname;" >

<!ENTITY % BlkPhras.class
     "| %pre.qname; | %blockquote.qname; | %address.qname;" >

<!ENTITY % BlkPres.class "| %hr.qname;" >

<!ENTITY % Block.extra "" >

<!ENTITY % Table.class "| %table.qname;" >

<!ENTITY % BlkSpecial.class
     "%Table.class;"
>

<!-- %Block.class; includes all block elements,
     used as an component in mixes
-->
<!ENTITY % Block.class
     "%BlkStruct.class;
      %BlkPhras.class;
      %BlkPres.class;
      %BlkSpecial.class;
      %Block.extra;"
>

<!-- %Block.mix; includes all block elements plus %Misc.class;
-->
<!ENTITY % Block.mix
     "%Heading.class;
      | %List.class;
      | %Block.class;
      %Misc.class;"
>

<!-- ................  All Content Elements  .................. -->

<!-- %Flow.mix; includes all text content, block and inline
-->
<!ENTITY % Flow.mix
     "%Heading.class;
      | %List.class;
      | %Block.class;
      | %Inline.class;
      %Misc.class;"
>

<!-- special content model for pre element -->
<!ENTITY % pre.content
    "( #PCDATA
     | %Inline.class; )*"
>

<!-- end of CafeML-model-1.mod -->

Finally, replace the standard XHTML DTD, which only imports the normal XHTML modules, with a new one that imports the standard modules you want, as well as any new modules you've defined. Again, the W3C offers a template for this purpose, which you can download from http://www.w3.org/TR/xhtml-modularization/DTD/templates/template.dtd. This template is a minimal DTD that makes the necessary imports and declares the necessary parameter entity references upon which all the other modules depend. Example 7-9 is a DTD based on this template. It merges in the element module defined in Example 7-7, as well as the standard XHTML tables, images, meta, and block presentation modules.

Example 7-9. An XHTML DTD that mixes in the Cafe DTD

<!-- ................................................................. -->
<!-- XHTML + CafeML DTD  ............................................. -->
<!-- file: CafeML.dtd -->

<!-- CafeML DTD -->
<!-- Please use this formal public identifier to identify it:
         "-//Elliotte Rusty Harold//DTD XHTML CafeDTD//EN"
-->
<!ENTITY % XHTML.version  "-//W3C//DTD XHTML CafeDTD//EN" >

<!-- Bring in any qualified name modules outside of XHTML -->
<!ENTITY % CAFEML-qname.mod SYSTEM "cafe-qname-1.mod">
%CAFEML-qname.mod;

<!-- Define any extra prefixed namespaces that this DTD relies upon -->
<!ENTITY NS.prefixed.extras.attrib "" >

<!-- Define the Content Model file for the framework to use -->
<!ENTITY % xhtml-model.mod SYSTEM "CafeML-model-1.mod" >

<!-- reserved for future use with document profiles -->
<!ENTITY % XHTML.profile  "" >

<!-- Bi-directional text support
     This feature-test entity is used to declare elements
     and attributes used for internationalization support.
     Set it to INCLUDE or IGNORE as appropriate for your markup language.
-->
<!ENTITY % XHTML.bidi            "IGNORE" >

<!-- ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -->
<!-- Pre-Framework Redeclaration placeholder  .................... -->
<!-- This serves as a location to insert markup declarations
     into the DTD prior to the framework declarations.
-->
<!ENTITY % xhtml-prefw-redecl.module "IGNORE" >
<![%xhtml-prefw-redecl.module;[
%xhtml-prefw-redecl.mod;
<!-- end of xhtml-prefw-redecl.module -->]]>

<!-- The events module should be included here if you need it. In this
     skeleton it is IGNOREd.
-->
<!ENTITY % xhtml-events.module "IGNORE" >

<!-- Modular Framework Module  ................................... -->
<!ENTITY % xhtml-framework.module "INCLUDE" >
<![%xhtml-framework.module;[
<!ENTITY % xhtml-framework.mod
     PUBLIC "-//W3C//ENTITIES XHTML 1.1 Modular Framework 1.0//EN"
            "xhtml-framework-1.mod" >
%xhtml-framework.mod;]]>

<!-- Post-Framework Redeclaration placeholder  ................... -->
<!-- This serves as a location to insert markup declarations
     into the DTD following the framework declarations.
-->
<!ENTITY % xhtml-postfw-redecl.module "IGNORE" >
<![%xhtml-postfw-redecl.module;[
%xhtml-postfw-redecl.mod;
<!-- end of xhtml-postfw-redecl.module -->]]>

<!-- Text Module (required)  ............................... -->
<!ENTITY % xhtml-text.module "INCLUDE" >
<![%xhtml-text.module;[
<!ENTITY % xhtml-text.mod
     PUBLIC "-//W3C//ELEMENTS XHTML 1.1 Text 1.0//EN"
            "xhtml-text-1.mod" >
%xhtml-text.mod;]]>

<!-- Hypertext Module (required) ................................. -->
<!ENTITY % xhtml-hypertext.module "INCLUDE" >
<![%xhtml-hypertext.module;[
<!ENTITY % xhtml-hypertext.mod
     PUBLIC "-//W3C//ELEMENTS XHTML 1.1 Hypertext 1.0//EN"
            "xhtml-hypertext-1.mod" >
%xhtml-hypertext.mod;]]>

<!-- Lists Module (required)  .................................... -->
<!ENTITY % xhtml-list.module "INCLUDE" >
<![%xhtml-list.module;[
<!ENTITY % xhtml-list.mod
     PUBLIC "-//W3C//ELEMENTS XHTML 1.1 Lists 1.0//EN"
            "xhtml-list-1.mod" >
%xhtml-list.mod;]]>

<!-- Your modules can be included here.  Use the basic form defined above,
     and be sure to include the public FPI definition in your catalog file
     for each module that you define. You may also include W3C-defined
     modules at this point.
-->
<!-- CafeML Module (custom module)  ....................... -->
<!ENTITY % cafeml.module "INCLUDE" >
<![%cafeml.module;[
<!ENTITY % cafeml.mod
     PUBLIC "-//Cafe con Leche//XHTML Extensions today 1.0//EN"
            "CafeML-1.mod" >
%cafeml.mod;]]>

<!-- Tables Module (optional)  ....................... -->
<!ENTITY % xhtml-table.module "INCLUDE" >
<![%xhtml-table.module;[
<!ENTITY % xhtml-table.mod
     PUBLIC "-//W3C//ELEMENTS XHTML Tables 1.0//EN"
            "xhtml-table-1.mod" >
%xhtml-table.mod;]]>

<!-- Meta Module (optional)  ....................... -->
<!ENTITY % xhtml-meta.module "INCLUDE" >
<![%xhtml-meta.module;[
<!ENTITY % xhtml-meta.mod
     PUBLIC "-//W3C//ELEMENTS XHTML Meta 1.0//EN"
            "xhtml-meta-1.mod" >
%xhtml-meta.mod;]]>

<!-- Image Module (optional)  ....................... -->
<!ENTITY % xhtml-image.module "INCLUDE" >
<![%xhtml-image.module;[
<!ENTITY % xhtml-image.mod
     PUBLIC "-//W3C//ELEMENTS XHTML Images 1.0//EN"
            "xhtml-image-1.mod" >
%xhtml-image.mod;]]>

<!-- Block Presentation Module (optional)  ....................... -->
<!ENTITY % xhtml-blkpres.module "INCLUDE" >
<![%xhtml-blkpres.module;[
<!ENTITY % xhtml-blkpres.mod
     PUBLIC "-//W3C//ELEMENTS XHTML Block Presentation 1.0//EN"
            "xhtml-blkpres-1.mod" >
%xhtml-blkpres.mod;]]>

<!-- Document Structure Module (required)  ....................... -->
<!ENTITY % xhtml-struct.module "INCLUDE" >
<![%xhtml-struct.module;[
<!ENTITY % xhtml-struct.mod
     PUBLIC "-//W3C//ELEMENTS XHTML 1.1 Document Structure 1.0//EN"
            "xhtml-struct-1.mod" >
%xhtml-struct.mod;]]>

<!-- end of CAFEML DTD  .............................................. -->
<!-- ................................................................. -->
Previous | Home | Next
7.2. Direct Display of XML in Browsers | Book Index | 7.4. Prospects for Improved Web-Search Methods
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Prospects for Improved Web-Search Methods (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

7.4. Prospects for Improved Web-Search Methods

Part of the hype of XML has been that web search engines will finally understand what a document means by looking at its markup. For instance, you can search for the movie Sneakers and just get back hits about the movie without having to sort through "Internet Wide Area `Tiger Teamers' mailing list," "Children's Side Zip Sneakers Recalled by Reebok," "Infant's `Little Air Jordan' Sneakers Recalled by NIKE," "Sneakers.com - Athletic shoes from Nike, Reebok, Adidas, Fila, New," and the 32,395 other results that Google pulled up on this search that had nothing to do with the movie.[6]

[6]In fairness to Google, four of the first ten hits it returned were about the movie.

In practice, this is still vapor, mostly because few web pages are available on the frontend in XML, even though more and more backends are XML. The search-engine robots only see the frontend HTML. As this slowly changes, and as the search engines get smarter, we should see more and more useful results. Meanwhile, it's possible to add some XML hints to your HTML pages that knowledgeable search engines can take advantage of using the Resource Description Framework (RDF), the Dublin Core, and the robots processing instruction.

7.4.1. RDF

The Resource Description Framework (RDF, http://www.w3.org/RDF/) can be understood as an XML encoding for a particularly simple data model. An RDF document describes resources. Each resource has zero or more properties. Each property has a name and a value. The value may itself be another resource.

The root element of an RDF document is an RDF element. Each resource the RDF element describes is represented as a Description element whose about attribute contains a URI or other identifier pointing to the resource described. Each child element of the Description element represents a property of the resource. The contents of that child element are the value of that property. All RDF elements like RDF and Description are placed in the http://www.w3.org/1999/02/22-rdf-syntax-ns# namespace. Property values generally come from other namespaces.

For example, suppose we want to say that the book XML in a Nutshell has the authors W. Scott Means and Elliotte Rusty Harold. In other words, we want to say that the resource identified by the URI urn:isbn:0596002920 has one author property with the value "W. Scott Means" and another author property with the value "Elliotte Rusty Harold." Example 7-10 does this.

Example 7-10. A simple RDF document saying that W. Scott Means and Elliotte Rusty Harold are the authors of XML in a Nutshell

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">

  <rdf:Description about="urn:isbn:0596002920">
    <author>Elliotte Rusty Harold</author>
    <author>W. Scott Means</author>
  </rdf:Description>

</rdf:RDF>

In this simple example the values of the author properties are merely text. However, they could be XML as well. Indeed, they could be other RDF elements.

There's more to RDF, including containers, schemas, and nested properties. However, this will be sufficient description for web metadata.

7.4.2. Dublin Core

The Dublin Core, http://purl.org/dc/, is a standard set of ten information items with specified semantics that reflect the sort of data you'd be likely to find in a card catalog or annotated bibliography. These are:

Title
Fairly self-explanatory; this is the name by which the resource is known. For instance, the title of this book is "XML in a Nutshell."

Creator
The person or organization who created the resource, e.g., a painter, author, illustrator, composer, and so on. For instance, the creators of this book are W. Scott Means and Elliotte Rusty Harold.

Subject
A list of keywords, very likely from some other vocabulary such as the Dewey Decimal System or Yahoo categories, identifying the topics of the resource. For instance, using the Library of Congress Subject Headings vocabulary, the subject of this book is "XML (Document markup language)."

Description
Typically, a brief amount of text describing the content of the resource in prose, but it may also include a picture, a table of contents, or any other description of the resource. For instance, a description of this book might be "A brief tutorial on and quick reference to XML and related technologies and specifications."

Publisher
The name of the person, company, or organization who makes the resource available. For instance, the publisher of this book is "O'Reilly & Associates."

Contributor
A person or organization who made some contribution to the resource but is not the primary creator of the resource. For example, the editors of this book, Laurie Petrycki, Simon St.Laurent, and Jeni Tennison, might be identified as contributors, as would Susan Hart, the artist who drew the picture on the cover.

Date
The date when the book was created or published, normally given in the form YYYY-MM-DD. For instance, this book's date might be 2002-05-23.

Type
The abstract kind of resource such as image, text, sound, or software. For instance, a description of this book would have the type text.

Format
For hard objects like books, the physical dimensions of the resource. For instance, the paper version of XML in a Nutshell has the dimensions 6" x 9". For digital objects like web pages, this is possibly the MIME media type. For instance, an online version of this book would have the Format text/html.

Identifier
A formal identifier for the resource, such as an ISBN number, a URI, or a Social Security number. This book's identifier is "0596002920."

Source
The resource from which the present resource was derived. For instance, the French translation of this book might reference the original English edition as its source.

Language
The language in which this resource is written, typically an ISO-639 language code, optionally suffixed with a hyphen and an ISO-3166 country code. For instance, the language for this book is en-US. The language for the French translation of this book might be fr-FR.

Relation
A reference to a resource that is in some way related to the current one, generally using a formal identifier, such as a URI or an ISBN number. For instance, this might refer to the web page for this book.

Coverage
The location, time, or jurisdiction the resource covers. For instance, the coverage of this book might be the U.S., Canada, Australia, the U.K., and Ireland. The coverage of the French translation of this book might be France, Canada, Haiti, Belgium, and Switzerland. Generally these will be listed in some formal syntax such as country codes.

Rights
Information about copyright, patent, trademark and other restrictions on the content of the resource. For instance, a rights statement about this book may say "Copyright 2002 O'Reilly & Associates."

Dublin Core can be encoded in a variety of forms including HTML META tags and RDF. Here we concentrate on its encoding in RDF. Typically, each resource is described with an rdf:Description element. This element contains child elements for as many of the Dublin Core information items as are known about the resource. The name of each of these elements matches the name of one of the 14 Dublin Core properties. These are placed in the http://purl.org/dc/elements/1.1/ namespace. Example 7-11 shows an RDF-encoded Dublin Core description of this book.

Example 7-11. An RDF-encoded Dublin Core description for XML in a Nutshell

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:dc="http://purl.org/dc/elements/1.1/">

  <rdf:Description about="urn:isbn:0596002920">
    <dc:Title>XML in a Nutshell</dc:Title>
    <dc:Creator>W. Scott Means</dc:Creator>
    <dc:Creator>Elliotte Rusty Harold</dc:Creator>
    <dc:Subject>XML (Document markup language)</dc:Subject>.
    <dc:Description>
      A brief tutorial on and quick reference to XML and
      related technologies and specifications
    </dc:Description>
    <dc:Publisher>O'Reilly &amp; Associates</dc:Publisher>
    <dc:Contributor>Laurie Petrycki</dc:Contributor>
    <dc:Contributor>Simon St. Laurent</dc:Contributor>
    <dc:Contributor>Jeni Tennison</dc:Contributor>
    <dc:Contributor>Susan Hart</dc:Contributor>
    <dc:Date>2002-04-23</dc:Date>
    <dc:Type>text</dc:Type>
    <dc:Format>6" x 9"</dc:Format>
    <dc:Identifier>0596002920</dc:Identifier>
    <dc:Language>en-US</dc:Language>
    <dc:Relation>http://www.oreilly.com/catalog/xmlnut/</dc:Relation>
    <dc:Coverage>US UK ZA CA AU NZ</dc:Coverage>
    <dc:Rights>Copyright 2002 O'Reilly &amp; Associates</dc:Rights>
  </rdf:Description>

</rdf:RDF>

There is as yet no standard for how an RDF document should be associated with the XML document it describes. One possibility is for the rdf:RDF element to be embedded in the document it describes, for instance, as a child of the BookInfo element of the DocBook source for this book. Another possibility is that servers provide this meta information through an extra-document channel. For instance, a standard protocol could be defined that would allow search engines to request this information for any page on the site. A convention could be adopted so that for any URL xyz on a given web site, the URL xyz/meta.rdf would contain the RDF-encoded Dublin Core metadata for that URL.

7.4.3. Robots

In HTML the robots META tag tells search engines and other robots whether they're allowed to index a page. Walter Underwood has proposed the following processing instruction as an equivalent for XML documents:

<?robots index="yes" follow="no"?>

Robots will look for this in the prolog of any XML document they encounter. The syntax of this particular processing instruction is two pseudoattributes, one named index and one named follow, whose values are either yes or no. If the index attribute has the value yes, then this page will be indexed by a search-engine robot. If index has the value no, then it won't be. Similarly, if follow has the value yes, then links from this document will be followed. If follow has the value no, then they won't be.

Previous | Home | Next
7.3. Authoring Compound Documents with Modular XHTML | Book Index | 8. XSL Transformations
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSL Transformations (XSLT) (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 8. XSL Transformations (XSLT)

Contents:

An Example Input Document
xsl:stylesheet and xsl:transform
Stylesheet Processors
Templates and Template Rules
Calculating the Value of an Element with xsl:value-of
Applying Templates with xsl:apply-templates
The Built-in Template Rules
Modes
Attribute Value Templates
XSLT and Namespaces
Other XSLT Elements

The Extensible Stylesheet Language (XSL) is divided into two parts: XSL Transformations (XSLT) and XSL Formatting Objects (XSL-FO). This chapter describes XSLT. Chapter 13 covers XSL-FO.

XSLT is an XML application for specifying rules by which one XML document is transformed into another XML document. An XSLT document--that is, an XSLT stylesheet--contains template rules. Each template rule has a pattern and a template. An XSLT processor compares the elements and other nodes in an input XML document to the template-rule patterns in a stylesheet. When one matches, it writes the template from that rule into the output tree. When it's done, it may further serialize the output tree into an XML document or some other format like plain text or HTML.

This chapter describes the template rules and a few other elements that appear in an XSLT stylesheet. XSLT uses the XPath syntax to identify matching nodes. We'll introduce a few pieces of XPath here, but most of it will be covered in Chapter 9.

8.1. An Example Input Document

To demonstrate XSL transformations, we first need a document to transform. Example 8-1 shows the document used in this chapter. The root element is people, which contains two person elements. The person elements have roughly the same structure (a name followed by professions and hobbies) with some differences. For instance, Alan Turing has three professions, but Richard Feynman only has one. Feynman has a middle_initial and a hobby, but Turing doesn't. Still these are clearly variations on the same basic structure. A DTD that permitted both of these would be easy to write.

Example 8-1. An XML document describing two people

<?xml version="1.0"?>
<people>
  <person born="1912" died="1954">
    <name>
      <first_name>Alan</first_name>
      <last_name>Turing</last_name>
    </name>
    <profession>computer scientist</profession>
    <profession>mathematician</profession>
    <profession>cryptographer</profession>
  </person>
  <person born="1918" died="1988">
    <name>
      <first_name>Richard</first_name>
      <middle_initial>P</middle_initial>
      <last_name>Feynman</last_name>
    </name>
    <profession>physicist</profession>
    <hobby>Playing the bongoes</hobby>
  </person>
</people>

Example 8-1 is an XML document. For purposes of this example, it will be stored in a file called people.xml. It doesn't have a DTD; however, this is tangential. XSLT works equally well with valid and invalid (but well-formed) documents. This document doesn't use namespaces either, though it could. XSLT works just fine with namespaces. Unlike DTDs, XSLT does pay attention to the namespace URIs instead of the prefixes. Thus, it's possible to use one prefix for an element in the input document and different prefixes for the same namespace in the stylesheet and output documents.

Previous | Home | Next
7.4. Prospects for Improved Web-Search Methods | Book Index | 8.2. xsl:stylesheet and xsl:transform
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


xsl:stylesheet and xsl:transform (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.2. xsl:stylesheet and xsl:transform

An XSLT stylesheet is an XML document. It can and generally should have an XML declaration. It can have a document type declaration, though most stylesheets do not. The root element of this document is either stylesheet or transform. These are synonyms for each other. You can use either stylesheet or transform as you prefer. There is absolutely no difference between them, aside from the name. They both have the same possible children and attributes. They both mean the same thing to an XSLT processor.

The stylesheet and transform elements, like all other XSLT elements, are in the http://www.w3.org/1999/XSL/Transform namespace. This namespace is customarily mapped to the xsl prefix so that you write xsl:transform or xsl:stylesheet rather than simply transform or stylesheet.

As well as the xmlns:xsl attribute declaring this prefix mapping, the root element must have a version attribute with the value 1.0. Thus, a minimal XSLT stylesheet, with only the root element and nothing else, is as shown in Example 8-2.

WARNING: This namespace URI must be exactly correct. If even so much as a single character is wrong, the stylesheet processor will output the stylesheet itself instead of either the input document or the transformed input document. There's a reason for this (see Section 2.3 of the XSLT 1.0 specification, Literal Result Element as Stylesheet, if you really want to know), but the bottom line is that this weird behavior looks very much like a bug in the XSLT processor if you're not expecting it. If you ever do see your stylesheet processor spitting your stylesheet back out at you, the problem is almost certainly an incorrect namespace URI.

TIP: Internet Explorer 5.0 and 5.5 partially support a very old and out-of-date working draft of XSLT, as well as various Microsoft extensions to this old working draft. They do not support XSLT 1.0, and indeed no XSLT stylesheets in this book work in IE5. Stylesheets that are meant for Microsoft XSLT can be identified by their use of the http://www.w3.org/TR/WD-xsl namespace. IE6 supports both http://www.w3.org/1999/XSL/Transform and http://www.w3.org/TR/WD-xsl. Good XSLT developers don't use http://www.w3.org/TR/WD-xsl and don't associate with developers who do.

Example 8-2. A minimal XSLT stylesheet

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

</xsl:stylesheet>

Perhaps a little surprisingly, this is a complete XSLT stylesheet; an XSLT processor can apply it to an XML document to produce an output document. Example 8-3 shows the effect of applying this stylesheet to Example 8-1.

Example 8-3. people.xml transformed by the minimal XSLT stylesheet

<?xml version="1.0" encoding="utf-8"?>

      Alan
      Turing

    computer scientist
    mathematician
    cryptographer

      Richard
      P
      Feynman

    physicist
    Playing the bongoes

You can see that the output consists of a text declaration plus the text of the input document. In this case, the output is a well-formed external parsed entity, but it is not itself a complete XML document.

Markup from the input document has been stripped. The net effect of applying an empty stylesheet, like Example 8-2, to any input XML document is to reproduce the content but not the markup of the input document. To change that, we'll need to add template rules to the stylesheet telling the XSLT processor how to handle the specific elements in the input document. In the absence of explicit template rules, an XSLT processor falls back on built-in rules that have the effect shown here.

Previous | Home | Next
8. XSL Transformations | Book Index | 8.3. Stylesheet Processors
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Stylesheet Processors (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.3. Stylesheet Processors

An XSLT processor is a piece of software that reads an XSLT stylesheet, reads an input XML document, and builds an output document by applying the instructions in the stylesheet to the information in the input document. An XSLT processor can be built into a web browser, just as MSXML is in Internet Explorer 6. It can be built into a web or application server, as in the Apache XML Project's Cocoon (http://xml.apache.org/cocoon). Or it can be a standalone program run from the command like Michael Kay's SAXON (http://saxon.sourceforge.net) or the Apache XML Project's Xalan (http://xml.apache.org/xalan-j/ ).

8.3.1. Command-Line Processors

The exact details of how to install, configure, and run the XSLT processor naturally vary from processor to processor. Generally, you have to install the processor in your path, or add its jar file to your class path if it's written in Java. Then you pass in the names of the input file, stylesheet file, and output file on the command line. For example, using Xalan, Example 8-3 is created in this fashion:

% java org.apache.xalan.xslt.Process -IN people.xml -XSL minimal.xsl
  -OUT 8-3.txt
========= Parsing file:D:/books/xian/examples/08/minimal.xsl ==========
Parse of file:D:/books/xian/examples/08/minimal.xsl took 771 milliseconds
========= Parsing people.xml ==========
Parse of people.xml took 90 milliseconds
=============================
Transforming...
transform took 20 milliseconds
XSLProcessor: done

For exact details, you'll need to consult the documentation that comes with your XSLT processor.

8.3.2. The xml-stylesheet Processing Instruction

XML documents that will be served directly to web browsers can have an xml-stylesheet processing instruction in their prolog telling the browser where to find the associated stylesheet for the document, as discussed in the last chapter. If this stylesheet is an XSLT stylesheet, then the type pseudoattribute should have the value application/xml. For example, this xml-stylesheet processing instruction says that browsers should apply the stylesheet found at the absolute URL http://www.oreilly.com/styles/people.xsl. Relative URLs can also be used.

<?xml version="1.0"?>
<?xml-stylesheet type="application/xml"
                 href="http://www.oreilly.com/styles/people.xsl"?>
<people>
  ...
TIP: Microsoft Internet Explorer uses type="text/xsl" for XSLT stylesheets. However, the text/xsl MIME media type has not been and will not be registered with the IANA. It is a figment of Microsoft's imagination. In the future, application/xslt+xml will probably be registered to identify XSLT stylesheets specifically.

Previous | Home | Next
8.2. xsl:stylesheet and xsl:transform | Book Index | 8.4. Templates and Template Rules
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Templates and Template Rules (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.4. Templates and Template Rules

To control what output is created from what input, you add template rules to the XSLT stylesheet. Each template rule is represented by an xsl:template element. This element has a match attribute that contains an XPath pattern identifying the input it matches; it also contains a template that is instantiated and output when the pattern is matched. The terminology is a little tricky here: the xsl:template element is a template rule that contains a template. An xsl:template element is not itself the template.

The simplest match pattern is an element name. Thus, this template rule says that every time a person element is seen, the stylesheet processor should emit the text "A Person":

<xsl:template match="person">A Person</xsl:template>

Example 8-4 is a complete stylesheet that uses this template rule.

Example 8-4. A very simple XSLT stylesheet

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="person">A Person</xsl:template>

</xsl:stylesheet>

Applying this stylesheet to the document in Example 8-1 produces this output:

<?xml version="1.0" encoding="utf-8"?>

 A Person

 A Person

There were two person elements in the input document. Each time the processor saw one, it emitted the text "A Person." The whitespace outside the person elements was preserved, but everything inside the person elements was replaced by the contents of the template rule, which is called the template.

The text "A Person" is called literal data characters, which is a fancy way of saying plain text that is copied from the stylesheet into the output document. A template may also contain literal result elements, i.e., markup that is copied from the stylesheet to the output document. For instance, Example 8-5 wraps the text "A Person" in between <p> and </p> tags:

Example 8-5. A simple XSLT stylesheet with literal result elements

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="person">
    <p>A Person</p>
  </xsl:template>

</xsl:stylesheet>

The output from this stylesheet is:

<?xml version="1.0" encoding="utf-8"?>

  <p>A Person</p>

  <p>A Person</p>

The <p> and </p> tags were copied from the input to the output. The only major restriction on the markup you may output is that it must be well-formed XML because the stylesheet must be well-formed XML. For instance, you cannot write a template rule like this:

<xsl:template match="person">
  A Person<p>
</xsl:template>

Here the <p> start-tag has no matching end-tag, and, therefore, the stylesheet is malformed. Any other markup you include in your XSLT stylesheet must be similarly well-formed. Empty-element tags must end with />; attribute values must be quoted; less-than signs must be escaped as &lt;; all entity references must be declared in a DTD except for the five predefined ones, and so forth. XSLT has no exceptions to the rules of well-formedness.

Previous | Home | Next
8.3. Stylesheet Processors | Book Index | 8.5. Calculating the Value of an Element with xsl:value-of
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Calculating the Value of an Element with xsl:value-of (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.5. Calculating the Value of an Element with xsl:value-of

Most of the time, the text that is output is more closely related to the text that is input than it was in the last couple of examples. Other XSLT elements can select particular content from the input document and insert it into the output document.

One of the most generally useful elements of this kind is xsl:value-of. This element calculates the string value of an XPath expression and inserts it into the output. The value of an element is the text content of the element after all the tags have been removed and entity and character references have been resolved. The element whose value is taken is identified by a select attribute containing an XPath expression.

For example, suppose you just want to extract the names of all the people in the input document. Then you might use a stylesheet like Example 8-6. Here the person template outputs only the value of the name child element of the matched person in between <p> and </p> tags.

Example 8-6. A simple XSLT stylesheet that uses xsl:value-of

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="person">
    <p>
      <xsl:value-of select="name"/>
    </p>
  </xsl:template>

</xsl:stylesheet>

When an XSLT processor applies this stylesheet to Example 8-1, it outputs this text:

<?xml version="1.0" encoding="utf-8"?>

  <p>
      Alan
      Turing
    </p>

  <p>
      Richard
      P
      Feynman
    </p>
Previous | Home | Next
8.4. Templates and Template Rules | Book Index | 8.6. Applying Templates with xsl:apply-templates
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Applying Templates with xsl:apply-templates (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.6. Applying Templates with xsl:apply-templates

By default, an XSLT processor reads the input XML document from top to bottom, starting at the root of the document and working its way down using preorder traversal. Template rules are activated in the order in which they match elements encountered during this traversal. This means a template rule for a parent will be activated before template rules matching the parent's children.

However, one of the things a template can do is change the order of traversal. That is, it can specify which element(s) should be processed next. It can specify that an element(s) should be processed in the middle of processing another element. It can even prevent particular elements from being processed. In fact, Examples 8-4 through 8-6 all implicitly prevent the child elements of each person element from being processed. Instead, they provided their own instructions about what the XSLT processor was and was not to do with those children.

The xsl:apply-templates element lets you make explicit your choice of processing order. Its select attribute contains an XPath expression telling the XSLT processor which nodes to process at that point in the output tree.

For example, suppose you wanted to list the names of the people in the input document; however, you want to put the last names first, regardless of the order in which they occur in the input document, and you don't want to output the professions or hobbies. First you need a name template that looks like this:

<xsl:template match="name">
  <xsl:value-of select="last_name"/>,
  <xsl:value-of select="first_name"/>
</xsl:template>

However, this alone isn't enough; if this were all there was in the stylesheet, not only would the output include the names, it would also include the professions and hobbies. You also need a person template rule that says to apply templates to name children only, but not to any other child elements like profession or hobby. This template rule does that:

<xsl:template match="person">
  <xsl:apply-templates select="name"/>
</xsl:template>

Example 8-7 shows the complete stylesheet.

Example 8-7. A simple XSLT stylesheet that uses xsl:apply-templates

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="name">
    <xsl:value-of select="last_name"/>,
    <xsl:value-of select="first_name"/>
  </xsl:template>

  <xsl:template match="person">
    <xsl:apply-templates select="name"/>
  </xsl:template>

</xsl:stylesheet>

When an XSLT processor applies this stylesheet to Example 8-1, this is output:

<?xml version="1.0" encoding="utf-8"?>

  Turing,
    Alan

  Feynman,
    Richard

The order of the template rules in the stylesheet doesn't matter. It's only the order of the elements in the input document that matters.

Applying templates is also important when the child elements have templates of their own, even if you don't need to reorder the elements. For example, let's suppose you want a template rule for the root people element that wraps the entire document in an HTML header and body. Its template will need to use xsl:apply-templates to indicate where it wants the children of the root element to be placed. That template rule might look like this:

<xsl:template match="people">
  <html>
    <head><title>Famous Scientists</title></head>
    <body>
      <xsl:apply-templates select="person"/>
    </body>
  </html>
</xsl:template>

This template tells the XSLT processor to replace every people element in the input document (of which there is only one in Example 8-1) with an html element. This html element contains some literal character data and several literal result elements of which one is a body element. The body element contains an xsl:apply-templates element telling the XSLT processor to process all the person children of the current people element and insert the output of any matched templates into the body element of the output document.

If you'd rather apply templates to all types of children of the people element, rather than just person children, you can omit the select attribute as demonstrated in Example 8-8. You can also use the more complicated XPath expressions discussed in the next chapter to be more precise about which elements you want to apply templates to.

Example 8-8. An XSLT stylesheet that generates a complete HTML document

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="people">
    <html>
      <head><title>Famous Scientists</title></head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="name">
    <p><xsl:value-of select="last_name"/>,
    <xsl:value-of select="first_name"/></p>
  </xsl:template>

  <xsl:template match="person">
    <xsl:apply-templates select="name"/>
  </xsl:template>

</xsl:stylesheet>

When an XSLT processor applies this stylesheet to Example 8-1, it outputs the well-formed HTML document shown in Example 8-9. Look closely at this example, and you may spot an important change that was not explicitly caused by the instructions in the stylesheet.

Example 8-9. The HTML document produced by applying Example 8-8 to Example 8-1

<html>
<head>
<title>Famous Scientists</title>
</head>
<body>
  <p>Turing,
    Alan</p>
  <p>Feynman,
    Richard</p>
</body>
</html>

The difference between Example 8-9 and all the previous output examples is that the text declaration has disappeared! Although there is an XSLT element you can use to specify whether you want a text declaration preceding your output (xsl:output), we haven't used that here. Instead, the XSLT processor noted that the root output element was html, and it adjusted itself accordingly. Since HTML output is such a common case, XSLT has special rules just to handle it. As well as omitting the text declaration, the processor will use HTML empty-element syntax like <br> instead of XML empty-element syntax like <br/> in the output document. (The input document and stylesheet must still be well-formed XML.) There are about half a dozen other changes the XSLT processor will make when it knows it's outputting HTML, all designed to make the output more acceptable to existing web browsers than is well-formed XML.

Previous | Home | Next
8.5. Calculating the Value of an Element with xsl:value-of | Book Index | 8.7. The Built-in Template Rules
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Built-in Template Rules (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.7. The Built-in Template Rules

There are seven kinds of nodes in an XML document: the root node, element nodes, attribute nodes, text nodes, comment nodes, processing instruction nodes, and namespace nodes. XSLT provides a default built-in template rule for each of these seven kinds of nodes that says what to do with that node if the stylesheet author has not provided more specific instructions. These rules use special wildcard XPath expressions to match all nodes of a given type. Together these template rules have major effects on which nodes are activated when.

8.7.1. The Default Template Rule for Text and Attribute Nodes

The most basic built-in template rule copies the value of text and attribute nodes into the output document. It looks like this:

<xsl:template match="text( )|@*">
  <xsl:value-of select="."/>
</xsl:template>

The text( ) node test is an XPath pattern matching all text nodes, just as first_name is an XPath pattern matching all first_name element nodes. @* is an XPath pattern matching all attribute nodes. The vertical bar combines these two patterns so that the template rule matches both text and attribute nodes. The rule's template says that whenever a text or attribute node is matched, the processor should output the value of that node. For a text node, this value is simply the text in the node. For an attribute, this value is the attribute value but not the name.

Example 8-10 is an XSLT stylesheet that pulls the birth and death dates out of the born and died attributes in Example 8-1. The default template rule for attributes takes the value of the attributes, but an explicit rule selects those values. The @ sign in @born and @died indicates that these are attributes of the matched element rather than child elements.

Example 8-10. An XSLT stylesheet that reads attribute

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="people">
    <html>
      <head><title>Famous Scientists</title></head>
      <body>
        <dl>
          <xsl:apply-templates/>
        </dl>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="person">
    <dt><xsl:apply-templates select="name"/></dt>
    <dd><ul>
      <li>Born: <xsl:apply-templates select="@born"/></li>
      <li>Died: <xsl:apply-templates select="@died"/></li>
    </ul></dd>
  </xsl:template>

</xsl:stylesheet>

When an XSLT processor applies this stylesheet to Example 8-1, it outputs the HTML document shown in Example 8-11.

Example 8-11. The HTML document produced by applying Example 8-10 to Example 8-1

<html>
   <head>
      <title>Famous Scientists</title>
   </head>
   <body>
      <dl>

         <dt>
            Alan
            Turing
         </dt>

         <dd>
            <ul>
               <li>Born: 1912</li>
               <li>Died: 1954</li>
            </ul>
         </dd>

         <dt>
            Richard
            P
            Feynman
         </dt>
         <dd>
            <ul>
               <li>Born: 1918</li>
               <li>Died: 1988</li>
            </ul>
         </dd>

      </dl>
   </body>
</html>

It's important to note that although this template rule says what should happen when an attribute node is reached, by default the XSLT processor never reaches attribute nodes and, therefore, never outputs the value of an attribute. Attribute values are output according to this template only if a specific rule applies templates to them, and none of the default rules do this because attributes are not considered to be children of their parents. In other words, if element E has an attribute A, then E is the parent of A, but A is not the child of E. (The biological metaphor breaks down here.) Applying templates to the children of an element with <xsl:apply-templates/> does not apply templates to attributes of the element. To do that, the xsl:apply-templates element must contain an XPath expression specifically selecting attributes.

8.7.2. The Default Template Rule for Element and Root Nodes

The most important template rule is the one that guarantees that children are processed. This is that rule:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

The asterisk * is an XPath wild-card pattern that matches all element nodes, regardless of what name they have or what namespace they're in. The forward slash / is an XPath pattern that matches the root node. This is the first node the processor selects for processing, and therefore this is the first template rule the processor executes (unless a nondefault template rule also matches the root node). Again, the vertical bar combines these two expressions so that it matches both the root node and element nodes. In isolation, this rule means that the XSLT processor eventually finds and applies templates to all nodes except attribute and namespace nodes because every nonattribute, non-namespace node is either the root node, a child of the root node, or a child of an element. Only attribute and namespace nodes are not children of their parents. (You can think of them as disinherited nodes.)

Of course, templates may override the default behavior. For example, when you include a template rule matching person elements in your stylesheet, then children of the matched person elements are not necessarily processed, unless your own template says to process them.

8.7.3. The Default Template Rule for Comment and Processing Instruction Nodes

This is the default template rule for comments and processing instructions:

<xsl:template match="processing-instruction()|comment( )"/>

It matches all comments and processing instructions. However, it does not output anything into the result tree. That is, unless you provide specific rules matching comments or processing instructions, no part of these items will be copied from the input document to the output document.

8.7.4. The Default Template Rule for Namespace Nodes

A similar template rule matches namespace nodes and instructs the processor not to copy any part of the namespace node to the output. This is truly a built-in rule that must be implemented in the XSLT processor's source code; it can't even be written down in an XSLT stylesheet because there's no such thing as an XPath pattern matching a namespace node. That is, there's no namespace( ) node test in XPath. The XSLT processor handles the insertion of any necessary namespace declarations in the output document automatically, without any special assistance from namespace templates.

Previous | Home | Next
8.6. Applying Templates with xsl:apply-templates | Book Index | 8.8. Modes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Modes (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.8. Modes

Sometimes the same input content needs to appear multiple times in the output document, formatted according to a different template each time. For instance, the titles of the chapters in a book would be formatted one way in the chapters themselves and a different way in the table of contents. Both xsl:apply-templates and xsl:template elements can have optional mode attributes that connect different template rules to different uses. A mode attribute on xsl:template element identifies in which mode that template rule should be activated. An xsl:apply-templates element with a mode attribute only activates template rules with matching mode attributes. Example 8-12 demonstrates with a stylesheet that begins the output document with a list of people's names. This is accomplished in the toc mode. Then a separate template rule, as well as a separate xsl:apply-templates element in the default mode (really no mode at all), output the complete contents of all person elements.

Example 8-12. A stylesheet that uses modes

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="people">
    <html>
      <head><title>Famous Scientists</title></head>
      <body>
        <ul><xsl:apply-templates select="person" mode="toc"/></ul>
        <xsl:apply-templates select="person"/>
      </body>
    </html>
  </xsl:template>

  <!-- Table of Contents Mode Templates -->
  <xsl:template match="person" mode="toc">
    <xsl:apply-templates select="name" mode="toc"/>
  </xsl:template>

  <xsl:template match="name" mode="toc">
    <li><xsl:value-of select="last_name"/>,
    <xsl:value-of select="first_name"/></li>
  </xsl:template>

  <!-- Normal Mode Templates -->
  <xsl:template match="person">
    <p><xsl:apply-templates/></p>
  </xsl:template>

</xsl:stylesheet>

Example 8-13 shows the output when this stylesheet is applied to people.xml. The people template in Example 8-12 applies templates to its person children twice. The first time it does so in the toc mode. This selects the first person template rule in the stylesheet that outputs each person in the form <li>Turing, Alan</li>. The second time, it doesn't specify any mode. This selects the second person template rule in the stylesheet, which outputs all the character data of the person wrapped in a p element.

Example 8-13. Output from a stylesheet that uses modes to process each person twice with different templates

<html>
<head>
<title>Famous Scientists</title>
</head>
<body>
<ul>
<li>Turing,
    Alan</li>
<li>Feynman,
    Richard</li>
</ul>
<p>

      Alan
      Turing

    computer scientist
    mathematician
    cryptographer
  </p>
<p>

      Richard
      P
      Feynman

    physicist
    Playing the bongoes
  </p>
</body>
</html>

For every mode you use in the stylesheet, the XSLT processor adds one default template rule to its set of built-in rules. This applies to all element and root nodes in the specified mode and applies templates to their children in the same mode (since the usual built-in template rule for element and root nodes doesn't have a mode). For instance, the extra default rule for Example 8-10 looks like this:

<xsl:template match="*|/" mode="toc">
  <xsl:apply-templates mode="toc"/>
</xsl:template>
Previous | Home | Next
8.7. The Built-in Template Rules | Book Index | 8.9. Attribute Value Templates
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Attribute Value Templates (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.9. Attribute Value Templates

It's easy to include known attribute values in the output document as the literal content of a literal result element. For example, this template rule wraps each input person element in an HTML span element that has a class attribute with the value person:

<xsl:template match="person">
  <span class="person"><xsl:apply-templates/></span>
</xsl:template>

However, it's trickier if the value of the attribute is not known when the stylesheet is written, but instead must be read from the input document. The solution is to use an attribute value template. An attribute value template is an XPath expression enclosed in curly braces that's placed in the attribute value in the stylesheet. When the processor outputs that attribute, it replaces the attribute value template with its value. For example, suppose you wanted to write a name template that changed the input name elements to empty elements with first_name, middle_initial, and last_name attributes like this:

<name first="Richard" initial="P" last="Feynman"/>

This template accomplishes that task:

<xsl:template match="name">
  <name first="{first_name}"
        initial="{middle_initial}"
        last="{last_name}" />
</xsl:template>

The value of the first attribute in the stylesheet is replaced by the value of the first_name element from the input document. The value of the initial attribute is replaced by the value of the middle_initial element from the input document; the value of the last attribute is replaced by the value of the last_name element from the input document.

Previous | Home | Next
8.8. Modes | Book Index | 8.10. XSLT and Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT and Namespaces (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.10. XSLT and Namespaces

XPath patterns, as well as expressions that match and select elements, identify these elements based on their local part and namespace URI. They do not consider the namespace prefix. Most commonly, the same namespace prefix is mapped to the same URI in both the input XML document and the stylesheet. However, this is not required. For instance, consider Example 8-14. This is exactly the same as Example 8-1, except that now all the elements have been placed in the namespace http://www.cafeconleche.org/namespaces/people.

Example 8-14. An XML document describing two people that uses a default namespace

<?xml version="1.0"?>
<people xmlns="http://www.cafeconleche.org/namespaces/people">

  <person born="1912" died="1954">
    <name>
      <first_name>Alan</first_name>
      <last_name>Turing</last_name>
    </name>
    <profession>computer scientist</profession>
    <profession>mathematician</profession>
    <profession>cryptographer</profession>
  </person>

  <person born="1918" died="1988">
    <name>
      <first_name>Richard</first_name>
      <middle_initial>M</middle_initial>
      <last_name>Feynman</last_name>
    </name>
    <profession>physicist</profession>
    <hobby>Playing the bongoes</hobby>
  </person>

</people>

Except for the built-in template rules, none of the rules in this chapter so far will work on this document! For instance, consider this template rule from Example 8-8:

<xsl:template match="name">
  <p><xsl:value-of select="last_name"/>,
  <xsl:value-of select="first_name"/></p>
</xsl:template>

It's trying to match a name element in no namespace, but the name elements in Example 8-13 aren't in no namespace. They're in the http://www.cafeconleche.org/namespaces/people namespace. This template rule no longer applies. To make it fit, we map the prefix pe to the namespace URI http://www.cafeconleche.org/namespaces/people. Then instead of matching name, we match pe:name. That the input document doesn't use the prefix pe is irrelevant as long as the namespace URIs match up. Example 8-15 demonstrates by rewriting Example 8-8 to work with Example 8-14 instead.

Example 8-15. An XSLT stylesheet for input documents using the http://www.cafeconleche.org/namespaces/people

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:pe="http://www.cafeconleche.org/namespaces/people">

  <xsl:template match="pe:people">
    <html>
      <head><title>Famous Scientists</title></head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="pe:name">
    <p><xsl:value-of select="pe:last_name"/>,
    <xsl:value-of select="pe:first_name"/></p>
  </xsl:template>

  <xsl:template match="pe:person">
    <xsl:apply-templates select="pe:name"/>
  </xsl:template>

</xsl:stylesheet>

The output is essentially the same output you get by applying Example 8-8 to Example 8-1 except that it will have an extra xmlns:pe attribute on the root element.

Previous | Home | Next
8.9. Attribute Value Templates | Book Index | 8.11. Other XSLT Elements
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Other XSLT Elements (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

8.11. Other XSLT Elements

This is hardly everything there is to say about XSLT. Indeed, XSLT does a lot more than the little we've covered in this introductory chapter. Other features yet to be discussed include:

  • Named templates

  • Numbering and sorting output elements

  • Conditional processing

  • Iteration

  • Extension elements and functions

  • Importing other stylesheets

These and more will all be discussed in Chapter 23. Since XSLT is itself Turing complete and since it can invoke extension functions written in other languages like Java, chances are very good you can use XSLT to make whatever transformations you need to make.

Furthermore, besides these additional elements, you can do a lot more simply by expanding the XPath expressions and patterns used in the select and match attributes of the elements with which you're already familiar. These techniques will be explored in Chapter 9.

However, the techniques outlined in this chapter lay the foundation for all subsequent, more advanced work with XSLT. The key to transforming XML documents with XSLT is to match templates to elements in the input document. Those templates contain both literal result data and XSLT elements that instruct the processor where to go to get more data. Everything you do with XSLT is based on this one simple idea.

Previous | Home | Next
8.10. XSLT and Namespaces | Book Index | 9. XPath
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 9. XPath

Contents:

The Tree Structure of an XML Document
Location Paths
Compound Location Paths
Predicates
Unabbreviated Location Paths
General XPath Expressions
XPath Functions

XPath is a non-XML language for identifying particular parts of XML documents. XPath lets you write expressions that refer to the first person element in a document, the seventh child element of the third person element, the ID attribute of the first person element whose contents are the string "Fred Jones", all xml-stylesheet processing instructions in the document's prolog, and so forth. XPath indicates nodes by position, relative position, type, content, and several other criteria. XSLT uses XPath expressions to match and select particular elements in the input document for copying into the output document or further processing. XPointer uses XPath expressions to identify the particular point in or part of an XML document to which an XLink links. The W3C XML Schema Language uses XPath expressions to define uniqueness and co-occurrence constraints. XForms relies on XPath to bind form controls to instance data, express constraints on user-entered values, and calculate values that depend on other values.

XPath expressions can also represent numbers, strings, or Booleans. This lets XSLT stylesheets carry out simple arithmetic for purposes such as numbering and cross-referencing figures, tables, and equations. String manipulation in XPath lets XSLT perform tasks such as making the title of a chapter uppercase in a headline or extracting the last two digits from a year.

9.1. The Tree Structure of an XML Document

An XML document is a tree made up of nodes. Some nodes contain one or more other nodes. There is exactly one root node, which ultimately contains all other nodes. XPath is a language for picking nodes and sets of nodes out of this tree. From the perspective of XPath, there are seven kinds of nodes:

  • The root node

  • Element nodes

  • Text nodes

  • Attribute nodes

  • Comment nodes

  • Processing-instruction nodes

  • Namespace nodes

One thing to note are the constructs not included in this list: CDATA sections, entity references, and document type declarations. XPath operates on an XML document after all these items have been merged into the document. For instance, XPath cannot identify the first CDATA section in a document or tell whether a particular attribute value was directly included in the source element start-tag or merely defaulted from the declaration of the element in a DTD.

Consider the document in Example 9-1. This exhibits all seven kinds of nodes. Figure 9-1 is a diagram of the tree structure of this document.

Figure 9-1. The tree structure of Example 9-1

Example 9-1. The example XML document used in this chapter

<?xml version="1.0"?>
<?xml-stylesheet type="application/xml" href="people.xsl"?>
<!DOCTYPE people [
 <!ATTLIST homepage xlink:type CDATA #FIXED "simple"
                  xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
 <!ATTLIST person id ID #IMPLIED>
]>
<people>

  <person born="1912" died="1954" id="p342">
    <name>
      <first_name>Alan</first_name>
      <last_name>Turing</last_name>
    </name>
    <!-- Did the word computer scientist exist in Turing's day? -->
    <profession>computer scientist</profession>
    <profession>mathematician</profession>
    <profession>cryptographer</profession>
    <homepage xlink:href="http://www.turing.org.uk/"/>
  </person>

  <person born="1918" died="1988" id="p4567">
    <name>
      <first_name>Richard</first_name>
      <middle_initial>&#x50;</middle_initial>
      <last_name>Feynman</last_name>
    </name>
    <profession>physicist</profession>
    <hobby>Playing the bongoes</hobby>
  </person>

</people>

The XPath data model has several nonobvious features. First of all, the root node of the tree is not the same as the root element. The root node of the tree contains the entire document including the root element, as well as any comments and processing instructions that occur before the root element start-tag or after the root element end-tag. In Example 9-1, this means the root node contains the xml-stylesheet processing instruction, as well as the root element people.

However, the XPath data model does not include everything in the document. In particular, the XML declaration, the DOCTYPE declaration, and the various parts of the DTD are not addressable via XPath, though if the DTD provides default values for any attributes, then those attributes are noted by XPath. The homepage element has an xlink:type attribute that was supplied by the DTD. Similarly, any references to parsed entities are resolved. Entity references, character references, and CDATA sections are not individually identifiable, though any data they contain is addressable. For example, XSLT cannot make all the text in CDATA sections bold because XPath doesn't know which text is and isn't part of a CDATA section.

Finally, xmlns and xmlns:prefix attributes are not considered attribute nodes, even though that's how a non-namespace-aware parser will see them. However, namespace nodes are attached to every element and attribute node for which a declaration has scope. They are not only attached to the single element where the namespace is declared.

Previous | Home | Next
8.11. Other XSLT Elements | Book Index | 9.2. Location Paths
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Location Paths (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

9.2. Location Paths

The most useful XPath expression is a location path. A location path identifies a set of nodes in a document. This set may be empty, may contain a single node, or may contain several nodes. These can be element nodes, attribute nodes, namespace nodes, text nodes, comment nodes, processing instruction nodes, root nodes, or any combination of these. A location path is built out of successive location steps. Each location step is evaluated relative to a particular node in the document called the context node.

9.2.1. The Root Location Path

The simplest location path is the one that selects the root node of the document. This is simply the forward slash (/). (You'll notice that a lot of XPath syntax is deliberately similar to the syntax used by the Unix shell. Here / is the root node of a Unix filesystem, and / is the root node of an XML document.) For example, this XSLT template rule uses the XPath pattern / to match the entire input document tree and wrap it in an html element:

<xsl:template match="/">
  <html><xsl:apply-templates/></html>
</xsl:template>

/ is an absolute location path because no matter what the context node is--that is, no matter where you were in the input document when this template rule was applied--it always means the same thing: the root node of the document. It is relative to which document you're processing, but not to anything within that document.

9.2.2. Child Element Location Steps

The second simplest location path is a single element name. This path selects all child elements of the context node with the specified name. For example, the XPath profession refers to all profession child elements of the context node. Exactly which elements these are depends on what the context node is, so this is a relative XPath. For example, if the context node is the Alan Turing person element in Example 9-1, then the location path profession refers to these three profession child elements of that element:

<profession>computer scientist</profession>
<profession>mathematician</profession>
<profession>cryptographer</profession>

However, if the context node is the Richard Feynman person element in Example 9-1, then the XPath profession refers to its single profession child element:

<profession>physicist</profession>

If the context node is the name child element of Richard Feynman or Alan Turing's person element, then this XPath doesn't refer to anything at all because neither of those has any profession child elements.

In XSLT, the context node for an XPath expression used in the select attribute of xsl:apply-templates and similar elements is the node that is currently matched. For example, consider the simple stylesheet in Example 9-2. In particular, look at the template rule for the person element. The XSLT processor will activate this rule twice, once for each person node in the document. The first time the context node is set to Alan Turing's person element. The second time the context node is set to Richard Feynman's person element. When the same template is instantiated with a different context node, the XPath expression in <xsl:value-of select="name"/> refers to a different element, and the output produced is therefore different.

Example 9-2. A very simple stylesheet for Example 9-1

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="people">
    <xsl:apply-templates select="person"/>
  </xsl:template>

  <xsl:template match="person">
    <xsl:value-of select="name"/>
  </xsl:template>

</xsl:stylesheet>

When XPath is used in other systems, such as XPointer or XForms, other means are provided for determining what the context node is.

9.2.3. Attribute Location Steps

Attributes are also part of XPath. To select a particular attribute of an element, use an @ sign followed by the name of the attribute you want. For example, the XPath expression @born selects the born attribute of the context node. Example 9-3 is a simple XSLT stylesheet that generates an HTML table of names and birth and death dates from documents like Example 9-1.

Example 9-3. An XSLT stylesheet that uses root, child element, and attribute location steps

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <html>
      <xsl:apply-templates select="people"/>
    </html>
  </xsl:template>

  <xsl:template match="people">
    <table>
      <xsl:apply-templates select="person"/>
    </table>
  </xsl:template>

  <xsl:template match="person">
    <tr>
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="@born"/></td>
      <td><xsl:value-of select="@died"/></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

The stylesheet in Example 9-3 has three template rules. The first template rule has a match pattern that matches the root node, /. The XSLT processor activates this template rule and sets the context node to the root node. Then it outputs the start-tag <html>. This is followed by an xsl:apply-templates element that selects nodes matching the XPath expression people. If the input document is Example 9-1, then there is exactly one such node, the root element. This is selected and its template rule, the one with the match pattern of people, is applied. The XSLT processor sets the context node to the root people element and then begins processing the people template. It outputs a <table> start-tag and then encounters an xsl:apply-templates element that selects nodes matching the XPath expression person. Two child elements of this context node match the XPath expression person so they're each processed in turn using the person template rule. When it begins processing each person element, the XSLT processor sets the context node to that element. It outputs that element's name child element value and born and died attribute values wrapped in a table row and three table cells. The net result is:

<html>
   <table>
      <tr>
         <td>
            Alan
            Turing

         </td>
         <td>1912</td>
         <td>1954</td>
      </tr>
      <tr>
         <td>
            Richard
            P
            Feynman

         </td>
         <td>1918</td>
         <td>1988</td>
      </tr>
   </table>
</html>

9.2.4. The comment(), text(), and processing-instruction( ) Location Steps

Although element, attribute, and root nodes account for 90% or more of what you need to do with XML documents, this still leaves four kinds of nodes that need to be addressed: namespace nodes, text nodes, processing-instruction nodes, and comment nodes. Namespace nodes are rarely handled explicitly. The other three node types have special node tests to match them. These are as follows:

  • comment( )

  • text( )

  • processing-instruction( )

Since comments and text nodes don't have names, the comment( ) and text( ) node tests match any comment or text node in the context node. Each comment is a separate comment node. Each text node contains the maximum possible contiguous run of text not interrupted by any tag. Entity references and CDATA sections are resolved into text and markup and do not interrupt text nodes.

By default, XSLT stylesheets do process text nodes but do not process comment nodes. You can add a comment template rule to an XSLT stylesheet so it will process comments too. For example, this template rule replaces each comment with the text "Comment Deleted" in italic:

<xsl:template match="comment( )">
  <i>Comment Deleted</i>
</xsl:template>

With no arguments, the processing-instruction( ) node test selects all processing-instruction children of the context node. If it has an argument, then it only selects the processing-instruction children with the specified target. For example, the XPath expression processing-instruction('xml-stylesheet') selects all processing-instruction children of the context node whose target is xml-stylesheet.

9.2.5. Wildcards

Wildcards match different element and node types at the same time. There are three of these: *, node( ), and @*.

The asterisk (*) matches any element node regardless of name. For example, this XSLT template rule says that all elements should have their child elements processed but should not result in any output in and of themselves:

<xsl:template match="*"><xsl:apply-templates select="*"/></xsl:template>

The * does not match attributes, text nodes, comments, or processing-instruction nodes. Thus, in the previous example output will only come from child elements that have their own template rules that override this one.

You can put a namespace prefix in front of the asterisk. In this case, only elements in the same namespace are matched. For example, svg:* matches all elements with the same namespace URI as the svg prefix is mapped to. As usual, it's the URI that matters, not the prefix. The prefix can be different in the stylesheet and the source document as long as the namespace URI is the same.

The node( ) wildcard matches not only all element types but also text nodes, processing-instruction nodes, namespace nodes, attribute nodes, and comment nodes.

The @* wildcard matches all attribute nodes. For example, this XSLT template rule copies the values of all attributes of a person element in the document into the content of an attributes element in the output:

<xsl:template match="person">
  <attributes><xsl:apply-templates select="@*"/></attributes>
</xsl:template>

As with elements, you can attach a namespace prefix to the wildcard only to match attributes in a specific namespace. For instance, @xlink:* matches all XLink attributes provided that the prefix xlink is mapped to the http://www.w3.org/1999/xlink URI. Again, it's the URI that matters, not the actual prefix.

9.2.6. Multiple Matches with |

You often want to match more than one type of element or attribute but not all types. For example, you may want an XSLT template that applies to the profession and hobby elements but not to the name, person, or people elements. You can combine location paths and steps with the vertical bar (|) to indicate that you want to match any of the named elements. For instance, profession|hobby matches profession and hobby elements. first_name | middle_initial | last_name matches first_name, middle_initial, and last_name elements. @id|@xlink:type matches id and xlink:type attributes. *|@* matches elements and attributes but does not match text nodes, comment nodes, or processing instruction nodes. For example, this XSLT template rule applies to all the nonempty leaf elements (elements that don't contain any other elements) of Example 9-1:

<xsl:template match="first_name|last_name|profession|hobby">
  <xsl:value-of select="text( )"/>
</xsl:template>
Previous | Home | Next
9. XPath | Book Index | 9.3. Compound Location Paths
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Compound Location Paths (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

9.3. Compound Location Paths

The XPath expressions you've seen so far--element names, @ plus an attribute name, /, comment( ), text( ), and processing-instruction( )--are all single location steps. You can combine these with the forward slash to move around the hierarchy from the matched node to other nodes. Furthermore, you can use a period to refer to the context node, a double period to refer to the parent node, and a double forward slash to refer to descendants of the context node. With the exception of //, these are all similar to Unix shell syntax for navigating a hierarchical filesystem.

9.3.1. Building Compound Location Paths from Location Steps with /

Location steps can be combined with a forward slash (/) to make a compound location path. Each step in the path is relative to the one that preceded it. If the path begins with /, then the first step in the path is relative to the root node. Otherwise, it's relative to the context node. For example, consider the XPath expression /people/person/name/first_name. This begins at the root node, then selects all people element children of the root node, then all person element children of those nodes, then all name children of those nodes, and finally all first_name children of those nodes. Applied to Example 9-1, it indicates these two elements:

<first_name>Alan</first_name>
<first_name>Richard</first_name>

To indicate only the textual content of those two nodes, we have to go one step further. The XPath expression /people/person/name/first_name/text( ) selects the strings "Alan" and "Richard" from Example 9-1.

These two XPath expressions both began with /, so they're absolute location paths that start at the root. Relative location paths can also count down from the context node. For example, the XPath expression person/@id selects the id attribute of the person child elements of the context node.

9.3.2. Selecting from Descendants with //

A double forward slash (//) selects from all descendants of the context node, as well as the context node itself. At the beginning of an XPath expression, it selects from all descendants of the root node. For example, the XPath expression //name selects all name elements in the document. The expression //@id selects all the id attributes of any element in the document. The expression person//@id selects all the id attributes of any element contained in the person child elements of the context node, as well as the id attributes of the person elements themselves.

9.3.3. Selecting the Parent Element with ..

A double period (..) indicates the parent of the current node. For example, the XPath expression //@id identifies all id attributes in the document. Therefore, //@id/.. identifies all elements in the document that have id attributes. The XPath expression //middle_initial/../first_name identifies all first_name elements that are siblings of middle_initial elements in the document. Applied to Example 9-1, this selects <first_name>Richard</first_name> but not <first_name>Alan</first_name>.

9.3.4. Selecting the Context Node with .

Finally, the single period (.) indicates the context node. In XSLT this is most commonly used when you need to take the value of the currently matched node. For example, this template rule copies the content of each comment in the input document to a span element in the output document:

<xsl:template match="comment( )">
  <span class="comment"><xsl:value-of select="."></span>
</xsl:template>

The . given as the value of the select attribute of xsl:value-of stands for the matched node. This works equally well for element nodes, attribute nodes, and all the other kinds of nodes. For example, this template rule matches name elements from the input document and copies their value into strongly emphasized text in the output document:

<xsl:template match="name">
  <strong><xsl:value-of select="."></strong>
</xsl:template>
Previous | Home | Next
9.2. Location Paths | Book Index | 9.4. Predicates
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Predicates (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

9.4. Predicates

In general, an XPath expression may refer to more than one node. Sometimes this is what you want, but sometimes you want to further winnow the node-set. You want to select only some of the nodes the expression returns. Each step in a location path may (but does not have to) have a predicate that selects from the node list current at that step in the expression. The predicate contains a Boolean expression, which is tested for each node in the context node list. If the expression is false, then that node is deleted from the list. Otherwise, it's retained.

For example, suppose you want to find all profession elements whose value is "physicist." The XPath expression //profession[. = "physicist"] does this. Here the period stands for the string value of the current node, the same as would be returned by xsl:value-of. You can use single quotes around the string instead of double quotes, which is often useful when the XPath expression appears inside a double-quoted attribute value, for example, <xsl:apply-templates select="//profession[.= 'physicist']" />.

If you want to ask for all person elements that have a profession child element with the value "physicist," you'd use the XPath expression //person[profession="physicist"]. If you want to find the person element with id p4567, put an @ in front of the name of the attribute as in //person[@id="p4567"].

As well as the equals sign, XPath supports a full complement of relational operators including <, >, >=, <=, and !=. For instance, the expression //person[@born<=1976] locates all person elements in the document with a born attribute whose numeric value is less than or equal to 1976. Note that if this expression is used inside an XML document, you still have to escape the less-than sign as &lt;, for example, <xsl:apply-templates select="//person[@born &lt;= 1976]"/>. XPath doesn't get any special exemptions from the normal well-formedness rules of XML. On the other hand, if the XPath expression appears outside of an XML document, as it may in some uses of XPointer, then you may not need to escape the less-than sign.

XPath also provides Boolean and and or operators to combine expressions logically. For example, the XPath expression //person[@born<=1920 and @born>=1910] selects all person elements with born attribute values between 1910 and 1920 inclusive. //name[first_name="Richard" or first_name="Dick"] selects all name elements that have a first_name child with the value of either Richard or Dick.

In some cases the predicate may not be a Boolean, but it can be converted to one in a straightforward fashion. Predicates that evaluate to numbers are true if they're equal to the position of the context node, otherwise false. Predicates that indicate node-sets are true if the node-set is nonempty and false if it's empty. String values are true if the string isn't the empty string, false if it is. For example, suppose you want to select only those name elements in the document that have a middle_initial child element. The XPath expression //name selects all name elements. The XPath expression //name[middle_initial] selects all name elements and then checks each one to see if it has a middle_initial child element. Only those that do are retained. When applied to Example 9-1, this expression indicates Richard P. Feynman's name element but not Alan Turing's.

Any or all of the location steps in a location path can have predicates. For example, the XPath expression /people/person[@born < 1950]/name[first_name = "Alan"] first selects all people child elements of the root element (of which there's exactly one in Example 9-1). Then from those it chooses all person elements whose born attribute has a value numerically less than 1950. Finally, from that group of elements, it selects all name child elements that have a first_name child element with the value "Alan."

Previous | Home | Next
9.3. Compound Location Paths | Book Index | 9.5. Unabbreviated Location Paths
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Unabbreviated Location Paths (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

9.5. Unabbreviated Location Paths

Up until this point, we've been using what are called abbreviated location paths. These are much easier to type, much less verbose, and much more familiar to most people. They're also the kind of XPath expression that works best for XSLT match patterns. However, XPath also offers an unabbreviated syntax for location paths, which is more verbose but perhaps less cryptic and definitely more flexible.

Every location step in a location path has two required parts, an axis and a node test, and one optional part, the predicates. The axis tells you which direction to travel from the context node to look for the next nodes. The node test tells you which nodes to include along that axis, and the predicates further reduce the nodes according to some expression.

In an abbreviated location path, the axis and the node test are combined, while in an unabbreviated location path, they're separated by a double colon (::). For example, the abbreviated location path people/person/@id is composed of three location steps. The first step selects people element nodes along the child axis. The second step selects person element nodes along the child axis. The third step selects id attribute nodes along the attribute axis. When rewritten using the unabbreviated syntax, the same location path is child::people/child::person/attribute::id.

These full, unabbreviated location paths may be absolute if they start from the root node, just as abbreviated paths can be. For example, the full form /child::people/child::person is equivalent to the abbreviated form /people/person.

Unabbreviated location paths may have and be used in predicates as well. For example, the abbreviated path /people/person[@born<1950]/name[first_name="Alan"] becomes /child::people/child::person[ attribute::born < 1950 ] /child::name[ child::first_name = "Alan" ] in the full form.

Overall, the unabbreviated form is quite verbose and not much used in practice. It isn't even allowed in XSLT match patterns. However, it does offer one crucial ability that makes it essential to know: it is the only way to access most of the axes from which XPath expressions can choose nodes. The abbreviated syntax lets you walk along the child, parent, self, attribute, and descendant-or-self axes. The unabbreviated syntax adds eight more:

The ancestor axis
All element nodes that contain the context node, that is, the parent node, the parent's parent, the parent's parent's parent, and so on up through the root node in reverse document order.

The following-sibling axis
All nodes that follow the context node and are children of the same parent node in document order. Attribute and namespace nodes do not have any siblings.

The preceding-sibling axis
All nodes that precede the context node and are children of the same parent node in reverse document order. Attribute and namespace nodes do not have any siblings.

The following axis
All nodes that follow the end of the context node in document order except for attribute and namespace nodes.

The preceding axis
All nodes that precede the start of the context node in reverse document order except for attribute and namespace nodes.

The namespace axis
All namespaces in scope on the context node, whether declared on the context node or one of its ancestors.

The descendant axis
All descendants of the context node but not the context node itself.

The ancestor-or-self axis
All ancestors of the context node and the context node itself.

Example 9-4 demonstrates several of these axes using the full unabbreviated syntax. The goal is to produce a list of person elements that look more or less like this (after accounting for whitespace):

<dt>Richard P Feynman</dt>
<dd>
  <ul>
    <li>physicist</li>
    <li>Playing the bongoes</li>
  </ul>
</dd>

Example 9-4. An XSLT stylesheet that uses unabbreviated XPath syntax

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <dl>
      <xsl:apply-templates select="descendant::person"/>
    </dl>
  </xsl:template>

  <xsl:template match="person">
    <dt><xsl:value-of select="child::name"/></dt>
    <dd>
      <ul>
        <xsl:apply-templates select="child::name/following-sibling::*"/>
      </ul>
    </dd>
  </xsl:template>

  <xsl:template match="*">
    <li><xsl:value-of select="self::*"/></li>
  </xsl:template>

  <xsl:template match="homepage"
               xmlns:xlink="http://www.w3.org/1999/xlink">
    <li><xsl:value-of select="attribute::xlink:href"/></li>
  </xsl:template>

</xsl:stylesheet>

The first template rule matches the root node. It applies templates to all descendants of the root node that happen to be person elements. That is, it moves from the root node along the descendant axis with a node test of person. This XPath expression could have been rewritten in the abbreviated syntax as //person.

The second template rule matches person elements. It places the value of the name child of each person element in a dt element. The location path used here, child::name, could have been rewritten in the abbreviated syntax as the single word name. Then it applies templates to all elements that follow the name element at the same level of the hierarchy. It begins at the context node person element, then moves along the child axis to find the name element. From there it moves along the following-sibling axis looking for elements of any type (*) after the name element that are also children of the same person element. There is no abbreviated equivalent for the following-sibling axis, so this really is the simplest way to make this statement.

The third template rule matches any element not matched by another template rule. It simply wraps that element in an li element. The XPath self::* selects the value of the currently matched element, that is, the context node. This expression could have been abbreviated as a single period.

The fourth and final template rule matches homepage elements. In this case we need to select the value of xlink:href attribute, so we move from the context homepage node along the attribute axis. The node test is looking for the xlink:href attributes. (More properly, it's looking for an attribute with the local name href whose prefix is mapped to the http://www.w3.org/1999/xlink namespace URI.)

Previous | Home | Next
9.4. Predicates | Book Index | 9.6. General XPath Expressions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


General XPath Expressions (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

9.6. General XPath Expressions

So far we've focused on the very useful subset of XPath expressions called location paths. Location paths identify a set of nodes in an XML document and are used in XSLT match patterns and select expressions. However, location paths are not the only possible type of XPath expression. XPath expressions can also return numbers, Booleans, and strings. For instance, these are all legal XPath expressions:

  • 3.141529

  • 2+2

  • 'Rosalind Franklin'

  • true( )

  • 32.5 < 76.2

  • position()=last( )

XPath expressions that aren't node-sets can't be used in the match attribute of an xsl:template element. However, they can be used as values for the select attribute of xsl:value-of elements, as the well as in location path predicates.

9.6.1. Numbers

There are no pure integers in XPath. All numbers are 8-byte, IEEE 754 floating-point doubles, even if they don't have an explicit decimal point. This format is identical to Java's double primitive type. As well as representing floating-point numbers ranging from 4.94065645841246544e-324 to 1.79769313486231570e+308 (positive or negative) and zero, this type includes special representations of positive and negative infinity and a special not a number value (NaN) used as the result of operations like dividing zero by zero.

XPath provides the five basic arithmetic operators that will be familiar to any programmer:

+
Addition

-
Subtraction

*
Multiplication

div
Division

mod
Taking the remainder

The more common forward slash couldn't be used for division because it's already used to separate location steps in a location path. Consequently, a new operator had to be chosen. The word mod was chosen instead of the more common % operator. Aside from these minor differences in syntax, all five operators behave exactly as they do in Java. For instance, 2+2 is 4, 6.5 div 1.5 is 4.33333333, 6.5 mod 1.5 is 0.5, and so on. Placing the element <xsl:value-of select="6*7"/> in an XSLT template inserts the string 42 into the output tree when the template is instantiated. More often, a stylesheet performs some simple arithmetic on numbers read from the input document. For instance, this template rule calculates the century in which a person was born:

<xsl:template match="person">
  <century>
    <xsl:value-of select="(@born - (@born mod 100)) div 100"/>th
  </century>
</xsl:template>

9.6.2. Strings

XPath strings are ordered sequences of Unicode characters such as "Fred", "Ethel", " Figure Figure Figure Figure ", or " Figure Figure Figure Figure Figure ". String literals may be enclosed in either single or double quotes as convenient. The quotes are not themselves part of the string. The only restriction XPath places on a string literal is that it must not contain the kind of quote that delimits it. That is, if the string contains single quotes, it has to be enclosed in double quotes and vice versa. String literals may contain whitespace including tabs, carriage returns, and line feeds, as well as back slashes and other characters that would be illegal in many programming languages. However, if the XPath expression is part of an XML document, some of these possibilities may be ruled out by XML's well-formedness rules, depending on context.

You can use the = and != comparison operators to check whether two strings are the same. You can also use the relational <, >, <=, and >= operators to compare strings, but unless both strings clearly represent numbers (e.g., "-7.5" or '54.2'), the results are unlikely to make sense. In general, you can't define any real notion of string order in Unicode without detailed knowledge of the language in which the string is written.

Other operations on strings are provided by XPath functions and will be discussed shortly.

9.6.3. Booleans

A Boolean is a value that has exactly two states, true or false. Every Boolean must have one of these binary values. XPath does not provide any Boolean literals. If you use <xsl:value-of select="true"/> in an XSLT stylesheet, then the XSLT processor looks for a child element of the context node named true. However, the XPath functions true( ) and false( ) can substitute for the missing literals quite easily.

Most of the time, however, Booleans are created by comparisons between other objects, most commonly numbers. XPath provides all the usual relational operators including =, !=, <, >, >=, and <=. In addition, the and and or operators can combine Boolean expressions according to the usual rules of logic.

Booleans are most commonly used in predicates of location paths. For example, in the location step person[profession="physicist"], profession="physicist" is a Boolean. It is either true or false; there is no other possibility. Booleans are also commonly used in the test attribute of xsl:if and xsl:when elements. For example, this XSLT template rule includes the profession element in the output only if its contents are "physicist" or "computer scientist":

 <xsl:template match="profession">
  <xsl:if test=".='computer scientist' or .='physicist'">
    <xsl:value-of select="."/>
  </xsl:if>
</xsl:template>

This XSLT template rule italicizes the profession element if and only if its content is the string "computer scientist":

<xsl:template match="profession">
  <xsl:choose>
    <xsl:when test=".='computer scientist'">
      <i><xsl:value-of select="."/></i>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="."/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Finally, there's a not( ) function that reverses the sense of its Boolean argument. For example, if .='computer scientist' is true, then not(.='computer scientist') is false and vice versa.

Previous | Home | Next
9.5. Unabbreviated Location Paths | Book Index | 9.7. XPath Functions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Functions (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

9.7. XPath Functions

XPath provides a number of functions that you may find useful in predicates or raw expressions. All of these are discussed in Chapter 22. For example, the position( ) function returns the position of the current node in the context node list as a number. This XSLT template rule uses the position( ) function to calculate the number of the person being processed, relative to other nodes in the context node list:

<xsl:template match="person">
  Person <xsl:value-of select="position( )"/>,
  <xsl:value-of select="name"/>
</xsl:template>

Each XPath function returns one of these four types:

  • Boolean

  • Number

  • Node-set

  • String

There are no void functions in XPath. Therefore, XPath is not nearly as strongly typed as languages like Java or even C. You can often use any of these types as a function argument regardless of which type the function expects, and the processor will convert it as best it can. For example, if you insert a Boolean where a string is expected, then the processor will substitute one of the two strings "true" and "false" for the Boolean. The one exception is functions that expect to receive node-sets as arguments. XPath cannot convert strings, Booleans, or numbers to node-sets.

Functions are identified by the parentheses at the end of the function names. Sometimes these functions take arguments between the parentheses. For instance, the round( ) function takes a single number as an argument. It returns the number rounded to the nearest integer. For example, <xsl:value-of select="round(3.14)"/> inserts 3 into the output tree.

Other functions take more than one argument. For instance, the starts-with( ) function takes two arguments, both strings. It returns true if the first string starts with the second string. For example, this XSLT apply-templates element selects all name elements whose last name begins with T:

<xsl:apply-templates select="name[starts-with(last_name, 'T')]"/>

In this example the first argument to the starts-with( ) function is actually a node-set, not a string. The XPath processor converts that node-set to its string value (the text content of the first element in that node-set) before checking to see whether it starts with T.

Some XSLT functions have variable-length argument lists. For instance, the concat( ) function takes as arguments any number of strings and returns one string formed by concatenating all those strings together in order. For example, concat("a", "b", "c", "d") returns "abcd".

In addition to the functions defined in XPath and discussed in this chapter, most uses of XPath, such as XSLT and XPointer, define many more functions that are useful in their particular context. You use these extra functions just like the built-in functions when you're using those applications. XSLT even lets you write extension functions in Java and other languages that can do almost anything, for example, making SQL queries against a remote database server and returning the result of the query as a node-set.

9.7.1. Node-Set Functions

The node-set functions either operate on or return information about node-sets, that is, collections of XPath nodes. You've already encountered the position( ) function. Two related functions are last( ) and count( ). The last( ) function returns the number of nodes in the context node list, which also happens to be the same as the position of the last node in the list. The count( ) function is similar except that it returns the number of nodes in its node-set argument rather than in the context node list. For example, count(//name) returns the number of name elements in the document. Example 9-5 uses the position( ) and count( ) functions to list the people in the document in the form "Person 1 of 10, Person 2 of 10, Person 3 of 10 . . . " In the second template the position( ) function determines which person element is currently being processed, and the count( ) function determines how many total person elements there are in the document.

Example 9-5. An XSLT stylesheet that uses the position( ) and count( ) functions

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="people">
    <xsl:apply-templates select="person"/>
  </xsl:template>

  <xsl:template match="person">
    Person <xsl:value-of select="position( )"/>
    of <xsl:value-of select="count(//person)"/>:
    <xsl:value-of select="name"/>
  </xsl:template>

</xsl:stylesheet>

The id( ) function takes as an argument a string containing one or more IDs separated by whitespace and returns a node-set containing all the nodes in the document that have those IDs. These are attributes declared to have type ID in the DTD not necessarily attributes named ID or id. (A DTD must be both present and processed by the parser for the id() function to work.) Thus, in Example 9-1, id('p342') indicates Alan Turing's person element; id('p342 p4567') indicates both Alan Turing and Richard Feynman's person elements.

The id( ) function is most commonly used in the abbreviated XPath syntax. It allows you to form absolute location paths that don't start from the root. For example, id('p342')/name refers to Alan Turing's name element, regardless of where Alan Turing's person element is in the document, as long as it hasn't changed ID. This function is especially useful for XPointers where it takes the place of HTML's named anchors.

Finally, there are three node-set functions related to namespaces. The local-name( ) function takes as an argument a node-set and returns the local part of the first node in that set. The namespace-uri( ) function takes a node-set as an argument and returns the namespace URI of the first node in the set. Finally, the name( ) function takes a node-set as an argument and returns the prefixed name of the first node in that set. In all three functions the argument may be omitted, in which case the context node's namespace is evaluated. For instance, when applied to Example 9-1 the XPath expression, local-name(//homepage/@xlink:href) is href; namespace-uri(//homepage/@xlink:href) is http://www.w3.org/1999/xlink; and name(//homepage/@xlink:href) is xlink:href.

9.7.2. String Functions

XPath includes functions for basic string operations such as finding the length of a string or changing letters from upper- to lowercase. It doesn't have the full power of the string libraries in Python or Perl--for instance, there's no regular expression support--but it's sufficient for many simple manipulations you need for XSLT or XPointer.

The string( ) function converts an argument of any type to a string in a reasonable fashion. Booleans are converted to the string "true" or the string "false." Node-sets are converted to the string value of the first node in the set. This is the same value calculated by the xsl:value-of element. That is, the string value of the element is the complete text of the element after all entity references are resolved and tags, comments, and processing instructions have been stripped out. Numbers are converted to strings in the format used by most programming languages, such as "1987," "299792500," or "2.71828."

TIP: In XSLT the xsl:decimal-format element and format-number( ) function provide more precise control over formatting so you can insert separators between groups, change the decimal separator, use non-European digits, and make similar adjustments.

The normal use of most of the rest of the string functions is to manipulate or address the text content of XML elements or attributes. For instance, if date attributes were given in the format MM/DD/YYYY, then the string functions would allow you to target the month, day, and year separately.

The starts-with( ) function takes two string arguments. It returns true if the first argument starts with the second argument. For example, starts-with('Richard', 'Ric') is true but starts-with('Richard', 'Rick') is false. There is no corresponding ends-with( ) function.

The contains( ) function also takes two string arguments. However, it returns true if the first argument contains the second argument--that is, if the second argument is a substring of the first argument--regardless of position. For example, contains('Richard', 'ar') is true but contains('Richard', 'art') is false.

The substring-before( ) function takes two string arguments and returns the substring of the first argument that precedes the initial appearance of the second argument. If the second string doesn't appear in the first string, then substring-before( ) returns the empty string. For example, substring-before('MM/DD/YYYY', '/') is MM. The substring-after( ) function also takes two string arguments but returns the substring of the first argument that follows the initial appearance of the second argument. If the second string doesn't appear in the first string, then substring-after( ) returns the empty string. For example, substring-after ('MM/DD/YYYY', '/') is 'DD/YYYY'. substring-before(substring-after('MM/DD/YYYY', '/')', '/') is DD. substring-after(substring-after('MM/DD/YYYY', '/')', '/') is YYYY.

If you know the position of the substring you want, then you can use the substring( ) method instead. This takes three arguments: the string from which the substring will be copied, the position in the string from which to start extracting, and the number of characters to copy to the substring. The third argument may be omitted, in which case the substring contains all characters from the specified start position to the end of the string. For example, substring('MM/DD/YYYY', 1, 2) is MM; substring('MM/DD/YYYY', 4, 2) is DD; and substring('MM/DD/YYYY', 7) is YYYY.

The string-length( ) function returns a number giving the length of its argument's string value or the context node if no argument is included. In Example 9-1, string-length(//name[position( )=1]) is 29. If that seems long to you, remember that all whitespace characters are included in the count. If it seems short to you, remember that markup characters are not included in the count.

Theoretically, you could use these functions to trim and normalize whitespace in element content. However, since this would be relatively complex and is such a common need, XPath provides the normalize-space( ) function to do this. For instance, in Example 9-1 the value of string(//name[position( )=1]) is:

Alan
Turing

This contains a lot of extra whitespace that was inserted purely to make the XML document neater. However, normalize-space(string(//name[position( )=1])) is the much more reasonable:

Alan Turing

Although a more powerful string-manipulation library would be useful, XSLT is really designed for transforming the element structure of an XML document. It's not meant to have the more general power of a language like Perl, which can handle arbitrarily complicated and varying string formats.

9.7.3. Boolean Functions

The Boolean functions are few in number and quite straightforward. They all return a Boolean that has the value true or false. The true( ) function always returns true. The false( ) function always returns false. These substitute for Boolean literals in XPath.

The not( ) function reverses the sense of its Boolean argument. For example, not(@id>400) is almost always equivalent to (@id<=400). (NaN is a special case.)

The boolean( ) function converts its single argument to a Boolean and returns the result. If the argument is omitted, then it converts the context node. Numbers are converted to false if they're zero or NaN. All other numbers are true. Node-sets are false if they're empty, true if they have at least one element. Strings are false if they have zero length, otherwise they're true. Note that according to this rule, the string "false" is in fact true.

9.7.4. Number Functions

XPath includes a few simple numeric functions for summing groups of numbers and finding the nearest integer to a number. It doesn't have the full power of the math libraries in Java or Fortran--for instance, there's no square root or exponentiation function--but it's got enough to do most of the basic math you need for XSLT or the even simpler requirements of XPointer.

The number( ) function can take any type as an argument and convert it to a number. If the argument is omitted, then it converts the context node. Booleans are converted to 1 if true and if false. Strings are converted in a plausible fashion. For instance the string "7.5" will be converted to the number 7.5. The string "Fred" will be converted to NaN. Node-sets are converted to numbers by first converting them to their string values and then converting the resulting string to a number. The detailed rules are a little more complex, but as long as the object you're converting can reasonably be interpreted as a single number, chances are the number( ) function will do what you expect. If the object you're converting can't be reasonably interpreted as a single number, then the number( ) function will return NaN.

The round( ), floor( ), and ceiling( ) functions all take a single number as an argument. The floor( ) function returns the greatest integer less than or equal to its argument. The ceiling( ) function returns the smallest integer greater than or equal to its argument. The round( ) function returns its argument rounded to the nearest integer. When rounding numbers like 1.5 and -3.5 that are equally close to two integers, round() returns the greater of the two possibilities. (This means that -1.5 rounds to -1, but 1.5 rounds to 2.)

The sum( ) function takes a node-set as an argument. It converts each node in the set to its string value, then converts each of those strings to a number. It then adds up the numbers and returns the result.

Previous | Home | Next
9.6. General XPath Expressions | Book Index | 10. XLinks
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XLinks (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 10. XLinks

Contents:

Simple Links
Link Behavior
Link Semantics
Extended Links
Linkbases
DTDs for XLinks

XLinks are an attribute-based syntax for attaching links to XML documents. XLinks can be simple Point A-to-Point B links, like the links you're accustomed to from HTML's A element. XLinks can also be bidirectional, linking two documents in both directions so you can go from A to B or B to A. XLinks can even be multidirectional, presenting many different paths between any number of XML documents. The documents don't have to be XML documents--XLinks can be placed in an XML document that lists connections between other documents that may or may not be XML documents themselves. Web graffiti artists take note: these third-party links let you attach links to pages you don't even control, like the home page of the New York Times or the C.I.A. At its core XLink is nothing more and nothing less than an XML syntax for describing directed graphs, in which the vertices are documents at particular URIs and the edges are the links between the documents. What you put in that graph is up to you.

Current web browsers at most support simple XLinks that do little more than duplicate the functionality of HTML's A element. Many browsers don't support XLinks at all. However, custom applications may do a lot more. Since XLinks are so powerful, it shouldn't come as a surprise that they can do more than blue underlined links on web pages. XLinks can describe tables of contents or indexes. They can connect textual emendations to the text they describe. They can indicate possible paths through online courses or virtual worlds. Different applications will interpret different sets of XLinks differently. Just as no one browser really understands the semantics of all the various XML applications, so too no one program can process all collections of XLinks.

10.1. Simple Links

A simple link defines a one-way connection between two resources. The source or starting resource of the connection is the link element itself. The target or ending resource of the connection is identified by a Uniform Resource Identifier (URI). The link goes from the starting resource to the ending resource. The starting resource is always an XML element. The ending resource may be an XML document, a particular element in an XML document, a group of elements in an XML document, a span of text in an XML document, or something that isn't a part of an XML document, such as an MPEG movie or a PDF file. The URI may be something other than a URL, for instance a book ISBN number like urn:isbn:1565922247.

A simple XLink is encoded in an XML document as an element of arbitrary type that has an xlink:type attribute with the value simple and an xlink:href attribute whose value is the URI of the link target. The xlink prefix must be mapped to the http://www.w3.org/1999/xlink namespace URI. As usual, the prefix can change as long as the URI stays the same. For example, suppose this novel element appears in a list of children's literature and we want to link it to the actual text of the novel available from the URL ftp://archive.org/pub/etext/etext93/wizoz10.txt.

<novel>
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>

We give the novel element an xlink:type attribute with the value simple, an xlink:href attribute that contains the URL to which we're linking, and an xmlns:xlink attribute that associates the prefix xlink with the namespace URI http://www.w3.org/1999/xlink. The result is this:

<novel xmlns:xlink= "http://www.w3.org/1999/xlink"
       xlink:type = "simple"
       xlink:href = "ftp://archive.org/pub/etext/etext93/wizoz10.txt">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>

This establishes a simple link from this novel element to the plain text file found at ftp://archive.org/pub/etext/etext93/wizoz10.txt. Browsers are free to interpret this link as they like. However, the most natural interpretation, and the one implemented by the few browsers that do support simple XLinks, is to make this a blue underlined phrase the user can click on to replace the current page with the file being linked to. Other schemes are possible however.

XLinks are fully namespace aware. The xlink prefix is customary, though it can be changed. However, it must be mapped to the URI http://www.w3.org/1999/xlink. This can be done on the XLink element itself, as in this novel example, or it can be done on any ancestor of that element up to and including the root element of the document. Future examples in this and the next chapter use the xlink prefix exclusively and assume that this prefix has been properly declared on some ancestor element.

Every XLink element must have an xlink:type attribute telling you what kind of link (or part of a link) it is. This attribute has six possible values:

  • Simple

  • Extended

  • Locator

  • Arc

  • Title

  • Resource

Simple XLinks are the only ones that are really similar to HTML links. The remaining five kinds of XLink elements will be discussed in later sections.

The xlink:href attribute identifies the resource being linked to. It always contains a URI. Both relative and absolute URLs can be used, as they are in HTML links. However, the URI need not be a URL. For example, this link identifies but does not locate the print edition of The Wonderful Wizard of Oz with the ISBN number 0688069444:

<novel xmlns:xlink= "http://www.w3.org/1999/xlink"
       xlink:type = "simple"
       xlink:href = "urn:isbn:0688069444">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>
Previous | Home | Next
9.7. XPath Functions | Book Index | 10.2. Link Behavior
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Link Behavior (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

10.2. Link Behavior

So far, we've been careful to talk in the abstract. We've said that an XLink describes a connection between two resources, but we haven't said much about how that connection is presented to the end user or what it makes software reading the document do. That's because there isn't one answer to these questions. For instance, when the browser encounters a novel element that uses an http URL, clicking the link should probably load the text of the novel from the URL into the current window, thereby replacing the document that contained the link. Then again, maybe it should open a new window and show the user the new document in that window. The proper behavior for a browser encountering the novel element that uses an isbn URN is even less clear. Perhaps it should reserve the book with the specified ISBN at the local library for the user to walk in and pick up. Or perhaps it should order the book from an online bookstore. In other cases something else entirely may be called for. For instance, the content of some links are embedded directly in the linking document, as in this image element:

<image width="152" height="345" xlink:type="simple"
       xlink:href="http://www.turing.org.uk/turing/pi1/bus.jpg" />

Here, the author most likely intends the browser to download and display the image as soon as it finds the link. And rather than opening a new window for the image or replacing the current document with the image, the image should be embedded into the current document.

Just as XML is more flexible than HTML in the documents it describes, so too is XLink more flexible in the links it describes. An XLink indicates that there's a connection between two documents, but it's up to the application reading the XLink to decide what that connection means. It's not necessarily a blue, underlined phrase on which the user clicks in a browser to jump from the first source document to the target. It may indeed be that, just as an XML document may be a web page, but it may be something else too.

Page authors can offer suggestions to browsers about how links should be handled by using the xlink:show and xlink:actuate attributes. The xlink:show attribute tells a browser or other application what to do when the link is activated, for example, whether to show the linked content in the same window or to open a new window to display it. The xlink:actuate attribute tells the browser when to show the content, for example, whether it should follow the link as soon as it sees it or whether it should wait for an explicit user request.

10.2.1. xlink:show

The optional xlink:show attribute has five possible values that suggest in what context the application loading an XLinked resource should display that resource:

new
Open a new window and show the content of the link's URI (the ending resource) in that window.

replace
Show the ending resource in the current window, replacing the current document.

embed
Embed the ending resource in the current document at the location of the link element.

other
Exhibit some behavior other than opening a new window, replacing the document in the existing window or embedding the resource in the existing document. Exactly what that behavior is may be specified by other, nonstandard markup that a particular application understands.

none
Specify no behavior.

All five of these are only suggestions that browsers or other applications following XLinks are free to ignore. For example, a web spider would not open a window for any link and might ignore embedded links and treat the other four types identically. Mozilla might open a new tab in an existing window for xlink:show="new" rather than creating a completely new window. This is all allowed as long as it makes sense in the context of the application.

10.2.2. xlink:actuate

The optional xlink:actuate attribute has four possible values, which suggest when an application that encounters an XLink should follow it:

onLoad
The link should be followed as soon as the application sees it.

onRequest
The link should be followed when the user asks to follow it.

other
When to follow the link is determined by other markup in the document not specified by XLink.

none
No details are available about when or whether to follow this link. Indeed, following the link may not have any plausible meaning as in the previous example where the link pointed to a book's ISBN number rather than a URL where the book could be found.

All four of these are only suggestions that browsers or other applications following XLinks are free to ignore. For example, a web spider would use its own algorithms to decide when to follow and not follow a link. Differing behavior when faced with the same attributes is allowed as long as it makes sense for the application reading the document.

For example, a traditional link such as is provided by HTML's A element and indicated by the first novel example would be encoded like this:

<novel xlink:type="simple"
       xlink:href="ftp://archive.org/pub/etext/etext93/wizoz10.txt"
       xlink:actuate="onRequest" xlink:show="replace">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>

This says to wait for an explicit user request to follow the link (e.g., clicking on the content of the link) and then to replace the existing document with the document found at ftp://archive.org/pub/etext/etext93/wizoz10.txt. On the other hand, if you were using XLinks to embed images in documents, you'd want them traversed immediately and then embedded in the originating document. The following syntax would be appropriate:

<image xlink:type="simple"
       xlink:actuate="onLoad" xlink:show="embed"
       xlink:href="http://www.turing.org.uk/turing/pi1/bus.jpg"
       width="152" height="345" />

Both xlink:show and xlink:actuate are optional. An application should be prepared to do something reasonable if they're missing.

Previous | Home | Next
10. XLinks | Book Index | 10.3. Link Semantics
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Link Semantics (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

10.3. Link Semantics

A link describes a connection between two resources. These resources may or may not be XML documents, but even if they are XML documents, the relationships they have with each other can be quite varied. For example, links can indicate parent-child relationships, previous-next relationships, employer-employee relationships, customer-supplier relationships, and many more. XLink elements can have xlink:title and xlink:role attributes to specify the meaning of the connection between the resources. The xlink:title attribute contains a small amount of plain text describing the remote resource such as might be shown in a tooltip when the user moves the cursor over the link. The xlink:role attribute contains a URI. This points to something that somehow describes or annotates the remote resource. For instance, it might indicate the MIME media type of the remote resource by pointing to a registration page for the type at the Internet Assigned Numbers Authority (IANA), such as http://www.isi.edu/in-notes/iana/assignments/media-types/application/xml.

For example, the xlink:title attribute of this novel element specifies that the text comes from Project Gutenberg, while the xlink:role attribute points to the Project Gutenberg home page.

<novel xlink:type="simple"
 xlink:href="ftp://archive.org/pub/etext/etext93/wizoz10.txt"
 xlink:title= "The complete text of the novel from Project Gutenberg"
 xlink:role="http://promo.net/pg/" >
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>

As with almost everything else related to XLink, exactly what browsers or other applications will do with this information or how they'll present it to readers remains to be determined.

Previous | Home | Next
10.2. Link Behavior | Book Index | 10.4. Extended Links
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Extended Links (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

10.4. Extended Links

Whereas a simple link describes a single unidirectional connection between one XML element and one remote resource, an extended link describes a collection of resources and a collection of paths between those resources. Each path connects exactly two resources. Any individual resource may be connected to one of the other resources, two of the other resources, zero of the other resources, all of the other resources, or any subset of the other resources in the collection. It may even be connected back to itself. In computer science terms, an extended link is a directed, labeled graph in which the paths are arcs, the documents are vertices, and the URIs are labels.

Simple links are very easy to understand by analogy with HTML links. However, there's no obvious analogy for extended links. What they look like, how applications treat them, what user interfaces present them to people, is all up in the air. No simple visual metaphors like "click on the blue underlined text to jump to a new page" have been invented for extended links, and no browsers support them. How they'll be used and what user interfaces will be designed for them remains to be seen.

In XML, an extended link is represented by an extended link element, that is, an element of arbitrary type that has an xlink:type attribute with the value extended. For example, this is an extended link element that refers to the novel The Wonderful Wizard of Oz:

<novel xlink:type="extended">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>

Although this extended link is quite Spartan, most extended links contain local resources, remote resources, and arcs between those resources. A remote resource is represented by a locator element, that is, an element of any type that has an xlink:type attribute with the value locator. A local resource is represented by a resource element, that is, an element of any type that has an xlink:type attribute with the value resource. And an arc between two resources, whether local or remote, is represented by an arc element, that is, an element of any type that has an xlink:type attribute with the value arc.

10.4.1. Locators

Each locator element has an xlink:type attribute with the value locator and an xlink:href attribute containing a URI for the resource it locates. For example, this novel element for The Wonderful Wizard of Oz contains three locator elements that identify particular editions of the book:

<novel xlink:type = "extended">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
  <edition xlink:type="locator" xlink:href="urn:isbn:0688069444" />
  <edition xlink:type="locator" xlink:href="urn:isbn:0192839306" />
  <edition xlink:type="locator" xlink:href="urn:isbn:0700609857" />
</novel>

Most of the time each locator element also has an xlink:label attribute that serves as a name for the element. The value of this attribute can be any XML name that does not contain a colon (i.e., that does not have a namespace prefix). For instance, in the previous example, we could add labels based on the ISBN number like this:

<novel xlink:type = "extended">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
  <edition xlink:type="locator" xlink:href="urn:isbn:0688069444"
                                xlink:label="ISBN0688069444"/>
  <edition xlink:type="locator" xlink:href="urn:isbn:0192839306"
                                xlink:label="ISBN0192839306"/>
  <edition xlink:type="locator" xlink:href="urn:isbn:0700609857"
                                xlink:label="ISBN0700609857"/>
</novel>

The number alone cannot be used because XML names cannot start with digits. In this and most cases, the labels are unique within the extended link, but they don't absolutely have to be.

Locators may also have the optional semantic attributes xlink:title and xlink:role to provide more information about the remote resource. These attributes have the same meanings they have for simple XLinks. The xlink:title attribute contains a small amount of text describing the remote resource, and the xlink:role attribute contains an absolute URI that somehow describes or annotates the remote resource. For instance, the edition elements could provide the publisher's name and URL using these attributes like this:

<novel xlink:type = "extended">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
  <edition xlink:type="locator" xlink:href="urn:isbn:0688069444"
           xlink:title="William Morrow"
           xlink:role="http://www.williammorrow.com/"
           xlink:label="ISBN0688069444"/>
  <edition xlink:type="locator" xlink:href="urn:isbn:0192839306"
           xlink:title="Oxford University Press"
           xlink:role="http://www.oup-usa.org/"
           xlink:label="ISBN0192839306"/>
  <edition xlink:type="locator" xlink:href="urn:isbn:0700609857"
           xlink:title="University Press of Kansas"
           xlink:role="http://www.kansaspress.ku.edu/"
           xlink:label="ISBN0700609857"/>
</novel>

10.4.2. Arcs

Paths between resources are called arcs, and they are represented by arc elements, that is, elements of arbitrary type that have an xlink:type attribute with the value arc. Each arc element should have an xlink:from attribute and an xlink:to attribute. The xlink:from attribute identifies the source of the link . The xlink:to attribute identifies the target of the link. These attributes do not contain URIs as you might expect. Rather they contain a name matching the value of the xlink:label attribute of one of the locator elements in the extended link.

Example 10-1 shows an extended link that contains the first three novels in the Wizard of Oz series: The Wonderful Wizard of Oz, The Marvelous Land of Oz, and Ozma of Oz. Arcs connect the first book in the series to the second and the second to the third, and then back again. In this example, the root series element is the extended link element; each novel element is a locator element; and the next and previous elements are arc elements.

Example 10-1. An extended link with three locators and four arcs

<series xlink:type="extended" xmlns:xlink="http://www.w3.org/1999/xlink">

  <author>L. Frank Baum</author>
  <!-- locator elements -->
  <novel xlink:type="locator" xlink:label="oz1"
         xlink:href="ftp://archive.org/pub/etext/etext93/wizoz10.txt">
    <title>The Wonderful Wizard of Oz</title>
    <year>1900</year>
  </novel>
  <novel xlink:type="locator" xlink:label="oz2"
         xlink:href="ftp://archive.org/pub/etext/etext93/ozland10.txt">
    <title>The Marvelous Land of Oz</title>
    <year>1904</year>
  </novel>
  <novel xlink:type="locator" xlink:label="oz3"
         xlink:href="ftp://archive.org/pub/etext/etext93/wizoz10.txt">
    <title>Ozma of Oz</title>
    <year>1907</year>
  </novel>

  <!-- arcs -->
  <next     xlink:type="arc" xlink:from="oz1" xlink:to="oz2" />
  <next     xlink:type="arc" xlink:from="oz2" xlink:to="oz3" />
  <previous xlink:type="arc" xlink:from="oz2" xlink:to="oz1" />
  <previous xlink:type="arc" xlink:from="oz3" xlink:to="oz2" />
</series>

Figure 10-1 diagrams this extended link. Resources are represented by books. Arcs are represented by arrows. However, although we can understand this link in this sort of abstract sense, this doesn't really tell us anything about how a browser might present it to a user and how users might choose which links to follow. For instance, this extended link might be interpreted as nothing more than a list of the order in which to print these documents. All details of interpretation are left up to the application.

Figure 10-1. An extended link with three resources and four arcs between them

10.4.2.1. Multiple arcs from one arc element

On occasion a single arc element defines multiple arcs. If multiple elements share the same label, then an arc element that uses that label in either its xlink:to or xlink:from attribute defines arcs between all resources that share that label. Example 10-2 shows an extended link containing locator elements for three different online bookstores and one edition of The Wonderful Wizard of Oz. Each bookstore element has the label buy, and a single purchase arc element connects all of these. Figure 10-2 shows the graph structure of this extended link.

Example 10-2. An extended link with one arc element but three arcs

<book xlink:type="extended" xmlns:xlink="http://www.w3.org/1999/xlink">

  <author>L. Frank Baum</author>
  <title>The Wonderful Wizard of Oz</title>

  <!-- locator elements -->
  <edition xlink:type="locator" xlink:href="urn:isbn:0192839306"
           xlink:title="Oxford University Press"
           xlink:role="http://www.oup-usa.org/"
           xlink:label="ISBN0192839306"/>

  <store xlink:type="locator"
         xlink:href="http://www.amazon.com/exec/obidos/ASIN/0192839306"
         xlink:label="buy">Amazon</store>

  <store xlink:type="locator" xlink:href=
   "http://www1.fatbrain.com/asp/bookinfo/bookinfo.asp?theisbn=0192839306"
    xlink:label="buy">Fatbrain</store>

  <store xlink:type="locator" xlink:href=
"http://shop.bn.com/booksearch/isbninquiry.asp?isbn=0192839306"
    xlink:label="buy">Barnes &amp; Noble</store>

  <!-- arcs -->
  <purchase xlink:type="arc" xlink:from="ISBN0192839306" xlink:to="buy" />

</book>

Figure 10-2. One arc element can generate several arcs

If an arc element does not have an xlink:to attribute, then it uses all the resources in the extended link as sources. If an arc element does not have an xlink:from attribute, then it uses all the resources in the extended link as targets.

However, it is an error for more than one arc element to define an arc between the same two resources, whether implicitly or explicitly. For example, if an extended link contains N resources and an arc element such as <edition xlink:type="arc"/> with neither an xlink:to or xlink:from attribute, then it cannot contain any other arc elements because this one arc element defines all N2 possible arcs between the resources in the extended link.

10.4.2.2. Arc titles

Each arc element may optionally have an xlink:title attribute, just like all other XLink elements. This contains a small amount of text describing the arc intended for humans to read. For instance, in Example 10-1, we might give these titles to the arcs:

<next     xlink:type="arc" xlink:from="oz1" xlink:to="oz2"
          xlink:title="Next" />
<next     xlink:type="arc" xlink:from="oz2" xlink:to="oz3"
          xlink:title="Next" />
<previous xlink:type="arc" xlink:from="oz2" xlink:to="oz1"
          xlink:title="Previous" />
<previous xlink:type="arc" xlink:from="oz3" xlink:to="oz2"
          xlink:title="Previous" />

When processing an extended link, a browser might show the title to the user as the contents of a link so they could choose which arc they wanted to follow from their current position, or they might appear in a pop-up menu when the user was on one of the referenced pages. XLink does not require or suggest any specific user interface for arcs or arc titles.

10.4.2.3. Arc roles

Arc elements cannot have xlink:role attributes. However, an arc element can have an xlink:arcrole attribute that contains an absolute URI pointing to a description of the arc. More specifically, this URI should point to a resource that indicates which relationship the arc describes (e.g., parent-child, employer-employee). However, there's really no way to validate this at all beyond checking to see that xlink:arcrole does contain a legal URI (and even that is not strictly required). For instance, in Example 10-2 we might add an xlink:arcrole attribute to the purchase arc that pointed to the URL http://www.example.com/purchase_details.txt.

<purchase xlink:type="arc" xlink:from="ISBN0192839306" xlink:to="buy"
          xlink:arcrole="http://www.example.com/purchase_details.txt" />

The file purchase_details.html might then contain the text "will be bought from." This would indicate that the source of the link is bought from the target of the link; that is, "The Wonderful Wizard of Oz will be bought from Amazon," or "The Wonderful Wizard of Oz will be bought from Fatbrain," or "The Wonderful Wizard of Oz will be bought from Barnes & Noble." However, although this usage is suggested, XLink processors will not enforce it, and indeed there's really no way they could be asked to do this since that would require that they actually understand what they read. The xlink:arcrole attribute is optional. You don't have to include it on your arcs, and XLink processors don't have to do anything with it even if you do.

10.4.3. Local Resources

Locators represent remote resources, that is, resources that are not part of the document that contains the extended link. Extended links can also contain local resources in which the data is contained inside the extended link element. Each such resource is represented by a resource element. This is an element of arbitrary type that has an xlink:type attribute with the value resource. For instance, in Example 10-1 the series extended link element contains an author child element. This can easily be made a local resource simply by giving it an xlink:type="resource" attribute:

<author xlink:type="resource">L. Frank Baum</author>

A resource element can and generally does have the same attributes as a locator element, that is, xlink:label , xlink:role, and xlink:title. These all have the same semantics as they do for locator elements. For instance, the label is a name arcs use to connect resources. An arc can connect a resource to a resource, a resource to a locator, a locator to a resource, or a locator to a locator. Arcs really don't care whether resources are local or remote. To link to or from this resource, it needs an xlink:label attribute like this:

<author xlink:type="resource" xlink:label="baum">L. Frank Baum</author>

To establish links from this local resource to all the books, we'd simply add these three arc elements:

<book xlink:type="arc" xlink:from="baum" xlink:to="oz1" />
<book xlink:type="arc" xlink:from="baum" xlink:to="oz2" />
<book xlink:type="arc" xlink:from="baum" xlink:to="oz3" />

To move in the other direction, you'd simply reverse the values of the xlink:from and xlink:to attributes.

10.4.4. Title Elements

As you've seen, extended link elements, locator elements, arc elements, and resource elements can all have xlink:title attributes that provide a short blurb of text identifying the link. However, this isn't always enough. For instance, in a document that was a rather large extended link, you might want to mark up the titles using XHTML or some other vocabulary. To this end, a title can instead (or in addition) be provided as a title type child element, that is, an element whose xlink:type attribute has the value title.

For example, suppose you wanted to provide a more complete description of each edition of The Wonderful Wizard of Oz than simply who published it. Then you would give the edition element a title type element containing any convenient markup like this:

<edition xlink:type="locator" xlink:href="urn:isbn:0700609857"
         xlink:title="University Press of Kansas"
         xlink:role="http://www.kansaspress.ku.edu/"
         xlink:label="ISBN0700609857">
  <publisher_info xlink:type="title">
    <ul>
      <li>The Kansas Centennial Edition</li>
      <li>Illustrated by Michael McCurdy</li>
      <li>Foreword by Ray Bradbury</li>
      <li>1999</li>
      <li>216 pages</li>
      <li>SRP: $24.95</li>
    </ul>
  </publisher_info>
</edition>

What markup you use inside the title element is up to you as long as it's well-formed XML. XLink doesn't constrain it in any way. How the application interprets that markup is its own business. Here we've used basic HTML that a browser might perhaps be able to render. Once again, however, this is far enough past the bleeding edge that exact browser behavior, even when browsers do support extended XLinks, is hard to predict.

Previous | Home | Next
10.3. Link Semantics | Book Index | 10.5. Linkbases
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Linkbases (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

10.5. Linkbases

One of the most revolutionary features of XLinks is the ability to define links between documents you don't control. For instance, Example 10-1 is an extended link that describes and links three documents neither of the authors of this book has anything to do with. Links between purely remote resources are called third-party links . A third-party link is created when an arc's xlink:from and xlink:to attributes both contain labels for locator elements. Links from a remote resource to a local resource are called inbound links . An inbound link is created when an arc's xlink:from attribute contains the label of a locator element and its xlink:to attribute contains the label of a resource element. Links from a local resource to a remote resource are called outbound links. An outbound link is established when an arc's xlink:from attribute contains the label of a resource element and its xlink:to attribute contains the label of a locator element. Simple links are also outbound links.

An XML document that contains any inbound or third-party links is called a linkbase. A linkbase establishes links from documents other than the linkbase itself, perhaps documents that the author of the linkbase does not own and cannot control. Exactly how a browser or other application will load a linkbase and discover the links there is still an open question. It will probably involve visiting a web site that provides the linkbase. When the browser sees the extended link that attempts to establish links from a third web site, it should ask the user whether he wishes to accept the suggested links. It might even use the xlink:role and xlink:title attributes to help the user make this decision, though if past experience with cookies, Java applets, and ActiveX controls is any guide, the initial user interfaces are likely to be quite poor and the choices offered quite limited.

Once a browser has loaded a linkbase and arrived at a page that's referenced as the starting resource of one or more of the links in the linkbase, it should make this fact known to the user somehow and give them a means to traverse the link. Once again the user interface for this activity remains to be designed. Perhaps it will be a pop-up window showing the third-party links associated with a page. Or perhaps it will simply embed the links in the page, but use a different color underlining. The user could still activate them in exactly the same way they activate a normal HTML link.

If this is the scheme that's adopted, then it would be useful if the starting resource of the link didn't have to be an entire document, but could rather be just one part of it, such as a specific paragraph, personal name, or book title. Indeed, you can attach an XPointer to the URI identifying the starting resource of the link that chooses a particular part of or point in the starting document. This will be the subject of Chapter 11.

Previous | Home | Next
10.4. Extended Links | Book Index | 10.6. DTDs for XLinks
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DTDs for XLinks (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

10.6. DTDs for XLinks

For a document that contains XLinks to be valid, all the XLink attributes that the document uses have to be declared in a DTD just like any other attributes. In most cases some of the attributes can be declared #FIXED, and then they do not need to be explicitly included in the document itself. For example, this DTD fragment describes the novel element seen earlier:

<!ELEMENT novel  (title, author, year)>
<!ATTLIST novel  xmlns:xlink CDATA   #FIXED 'http://www.w3.org/1999/xlink'
                 xlink:type (simple) #FIXED 'simple'
                 xlink:href  CDATA   #REQUIRED>
<!ELEMENT title  (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT year   (#PCDATA)>

Given this DTD to fill in the fixed attributes xmlns:xlink and xlink:type, a novel element only needs an xlink:href attribute to be a complete simple XLink:

<novel xlink:href = "urn:isbn:0688069444">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>

Documents that contain many XLink elements often use parameter entity references to define the common attributes. For example, suppose novel, anthology, and nonfiction are all simple XLink elements. Their XLink attributes could be declared in a DTD like this:

<!ENTITY % simplelink
  "xlink:type (simple) #FIXED 'simple'
   xlink:href  CDATA   #REQUIRED
   xmlns:xlink CDATA   #FIXED 'http://www.w3.org/1999/xlink'
   xlink:role  CDATA   #IMPLIED
   xlink:title CDATA   #IMPLIED
   xlink:actuate (onRequest | onLoad | other | none) 'onRequest'
   xlink:show (new | replace | embed | other | none) 'new'"
>
<!ATTLIST anthology   %simplelink;>
<!ATTLIST novel       %simplelink;>
<!ATTLIST nonfiction  %simplelink;>

Similar techniques can be applied to declarations of attributes for extended XLinks.

Previous | Home | Next
10.5. Linkbases | Book Index | 11. XPointers
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPointers (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 11. XPointers

Contents:

XPointers on URLs
XPointers in Links
Bare Names
Child Sequences
Namespaces
Points
Ranges

XPointers are a non-XML syntax for identifying locations inside XML documents. An XPointer is attached to the end of the URI as its fragment identifier to indicate a particular part of an XML document rather than the entire document. XPointer syntax builds on the XPath syntax used by XSLT and covered in Chapter 9. To the four fundamental XPath data types--Boolean, node-set, number, and string--XPointer adds points and ranges, as well as the functions needed to work with these types. It also adds some shorthand syntax for particularly useful and common forms of XPath expressions.

WARNING: This chapter is based on the September 11, 2001 W3C Candidate Recommendation of XPointer. However, there are known issues with this draft, and some of the details described here are likely to change. The most current version of the XPointer recommendation can be found at http://www.w3.org/TR/xptr.

11.1. XPointers on URLs

A URL that identifies a document typically looks something like http://java.sun.com:80/products/jndi/index.html. The scheme, http in this example, tells you what protocol the application should use to retrieve the document. The authority, java.sun.com:80 in this example, tells you from which host the application should retrieve the document. The authority may also contain the port to connect to that host and the username and password to use. The path, /products/jndi/index.html in this example, tells you which file in which directory to ask the server for. This may not always be a real file in a real filesystem, but it should be a complete document the server knows how to generate and return. All of this you're already familiar with, and XPointer doesn't change any of it.

You probably also know that some URLs contain fragment identifiers that point to a particular named anchor inside the document the URL locates. This is separated from the path by the sharp sign #. For example, if we were to add the fragment download to the previous URL, then it would become http://java.sun.com:80/products/jndi/index.html#download. When a web browser follows a link to this URL, it looks for a named anchor in the document at http://java.sun.com:80/products/jndi/index.html with the name download such as this one:

<a name="download"></a>

It would then scroll the browser window to the position in the document where the anchor with that name is found. This is a simple and straightforward system, and it works well for HTML's simple needs. However, it has one major drawback: to link to a particular point of a particular document, you must be able to modify the document to which you're linking in order to insert a named anchor at the point to which you want to link. XPointer endeavors to eliminate this restriction by allowing you to specify where you want to link to using full XPath expressions as fragment identifiers. Furthermore, XPointer expands on XPath by providing operations to select particular points in or ranges of an XML document that do not necessarily coincide with any one node or set of nodes. For instance, an XPointer can describe the range of text currently selected by the mouse.

The most basic form of XPointer is simply an XPath expression -- often, though not necessarily, a location path--enclosed in the parentheses of xpointer( ). For example, these are all acceptable XPointers:

xpointer(/)
xpointer(//first_name)
xpointer(id('sec-intro'))
xpointer(/people/person/name/first_name/text( ))
xpointer(//middle_initial[position( )=1]/../first_name)
xpointer(//profession[.="physicist"])
xpointer(/child::people/child::person[@index<4000])
xpointer(/child::people/child::person/attribute::id)

Not all of these XPointers necessarily refer to a single element. Depending on which document the XPointer is evaluated relative to, an XPointer may identify zero, one, or more than one node. Most commonly the nodes identified are elements, but they can also be attribute nodes or text nodes, as well as points or ranges.

If you're uncertain whether a given XPointer will locate something, you can back it up with an alternative XPointer. For example, this XPointer looks first for first_name elements. However, if it doesn't find any, it looks for last_name elements instead:

xpointer(//first_name)xpointer(//last_name)

The last_name elements will be found only if there are no first_name elements. You can string as many of these XPointer parts together as you like. For example, this XPointer looks first for first_name elements. If it doesn't find any, it then seeks out last_name elements. If it doesn't find any of those, it looks for middle_initial elements. If it doesn't find any of those, it returns an empty node set:

xpointer(//first_name)xpointer(//last_name)xpointer(//middle_initial)

No special separator character or whitespace is required between the individual xpointer( ) parts, though whitespace is allowed. This XPointer means the same thing:

xpointer(//first_name) xpointer(//last_name) xpointer(//middle_initial)
Previous | Home | Next
10.6. DTDs for XLinks | Book Index | 11.2. XPointers in Links
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPointers in Links (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

11.2. XPointers in Links

Obviously, what an XPointer points to depends on to which document it's applied. This document is specified by the URL to which the XPointer is attached. For example, if you wanted a URL that pointed to the first name element in the document at http://www.cafeconleche.org/people.xml, you would type:

http://www.cafeconleche.org/people.xml#xpointer(//name[position( )=1])

If the XPointer uses any characters that are not allowed in URIs--for instance, the less than sign <, the double quotation mark ", or non-ASCII letters like é--then these must be hexadecimally escaped as specified by the URI specification before the XPointer is attached to the URI. That is, each such character is replaced by a percent sign followed by the hexadecimal value of each byte in the character in the UTF-8 encoding of Unicode. Thus, < would be written as %3C, " would be written as %22, and é would be written as %C3%A9.

In HTML, the URLs used in a elements can contain an XPointer fragment identifier. For example:

<a href = "http://www.cafeconleche.org/people.xml#xpointer(//name[1])">
  The name of a person
</a>

If a browser followed this link, it would likely load the entire document at http://www.cafeconleche.org/people.xml and then scroll the window to the beginning of the first name element in the document. However, no browsers yet support XPointer, so the exact behavior is open for debate. In some situations it might make sense for the browser to show only the specific element node(s) the XPointer referred to rather than the entire document.

Since XPath can only locate nodes in a well-formed XML document, XPointers can only point into XML documents. You can't use them to link into nonwell-formed HTML, plain-text files, or other non-XML documents. However, linking from HTML documents is perfectly fine, as is printing XPointers in books, painting them on the sides of buildings, or communicating them by any means by which text can be communicated.

XPointers are more frequently used in XLinks. For example, this simple link points to the first book child of the bookcoll child of the testament root element in the document at the relative URL ot.xml:

<In_the_beginning xlink:type="simple"
   xlink:href="ot.xml#xpointer(/testament/bookcoll/book[position( )=1])">
  Genesis
</In_the_beginning>

In extended links, an XPointer can help identify both the starting and ending resources of an arc. For example, this extended XLink establishes an arc between the last v element in the document at the relative URL ot.xml and the first v element of the document at the relative URL nt.xml. Then it establishes a link from the first v element of nt.xml to the last v element of ot.xml:

<Bible xlink:type="extended" xmlns:xlink="http://www.w3.org/1999/xlink">

  <testament xlink:type="locator" xlink:label="ot"
             xlink:href="ot.xml#xpointer(//v[position()=last( )])"/>
  <testament xlink:type="locator" xlink:label="nt"
             xlink:href="nt.xml#xpointer(//v[position( )=1])" />

  <next     xlink:from="ot" xlink:to="nt"/>
  <previous xlink:from="nt" xlink:to="ot"/>

</Bible>

Links can even be purely internal; that is, they can link from one place in the document to another place in the same document. The slide element shown in this example contains simple XLinks that point to the first and last slide elements in the document:

<slide xmlns:xlink="http://www.w3.org/1999/xlink">

  <point>Acme Wonder Goo is a delicious dessert topping!</point>
  <point>Acme Wonder Goo is a powerful floor cleaner!</point>
  <point>It's two products in one!</point>

  <first xlink:type="simple"
         xlink:href="#xpointer(//slide[position( )=1])">
    Start
  </first>
  <last  xlink:type="simple"
         xlink:href="#xpointer(//slide[position()=last( )]))">
    End
  </last>
</slide>

When the XPath expressions used in an XPointer are themselves relative, the context node is the root node of the entity that contains the XPointer.

Previous | Home | Next
11. XPointers | Book Index | 11.3. Bare Names
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Bare Names (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

11.3. Bare Names

XPointers provide a number of convenient extensions to XPath. One of the simplest is the bare name. A bare name XPointer is similar to an HTML named anchor; that is, a bare name XPointer identifies the element at which it's pointing by its name. However, this name is supplied by an ID type attribute of the element being pointed at rather than by a special a element with a name attribute. To link to an element with a bare name, append the usual fragment separator # to the URL followed by the ID of the element to which you're linking. For example, the URL http://www.w3.org/TR/1999/REC-xpath-19991116.xml#NT-AbsoluteLocationPath links to the element in the XPath 1.0 specification that has an ID type attribute with the value NT-AbsoluteLocationPath.

The ID attribute is an attribute declared to have ID type in the document's DTD. It does not have to be named ID or id. Bare names cannot be used to link to elements in documents that don't have DTDs because such a document cannot have any ID type attributes.

TIP: The inability to use IDs in documents without DTDs or schemas is a major shortcoming of XML. Work is ongoing to attempt to remedy this, perhaps by defining a generic ID attribute such as xml:id or by defining a namespace that identifies ID type attributes.

For example, suppose you wanted to link to the Motivation and Summary section of the Namespaces in XML recommendation at http://www.w3.org/TR/1999/REC-xml-names-19990114/xml-names.xml. A quick peek at the source code of this document reveals that it has an id attribute with the value sec-intro and that indeed this attribute is declared to have ID type in the associated DTD. Its start-tag looks like this:

<div1 id='sec-intro'>

Therefore, http://www.w3.org/TR/1999/REC-xml-names-19990114/xml-names.xml#sec-intro is a URL that points to this section. The name does not need to be (and indeed should not be) enclosed in xpointer( ) to make this work. Just the ID value is sufficient. This is basically just a convenient shorthand for an XPointer using an XPath expression using the id( ) function. The same URL could just as easily have been written as http://www.w3.org/TR/1999/REC-xml-names-19990114/xml-names.xml#xpointer(id(sec-intro)).

Previous | Home | Next
11.2. XPointers in Links | Book Index | 11.4. Child Sequences
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Child Sequences (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

11.4. Child Sequences

Another very common form of XPointer is one that descends exclusively along the child axis, selecting elements by their position relative to their siblings. For example, xpointer(/child::*[position() = 1]/child::*[ position( ) = 2]/child::*[position( ) = 3]) selects the third child element of the second child element of the root element of the document. Since this is so common, XPointer allows you to abbreviate this syntax by providing only the numbers of the child elements separated by forward slashes. This is called a child sequence. For example, the previous XPointer could be rewritten as a child sequence in the much more compact form /1/2/3. A child sequence should not be enclosed in xpointer( ) as a normal XPath expression would.

For example, the Motivation and Summary section of the Namespaces in XML recommendation at http://www.w3.org/TR/1999/REC-xml-names-19990114/xml-names.xml is given as a div element. It so happens that this div element is the first child element of the second child element of the root element. Therefore, the URL http://www.w3.org/TR/1999/REC-xml-names-19990114/xml-names.xml#/1/2/1 points to this section.

Previous | Home | Next
11.3. Bare Names | Book Index | 11.5. Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Namespaces (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

11.5. Namespaces

Since XPointers may appear in places that are not XML documents (HTML documents, database fields, magazine pages, etc.), they require their own mechanism for binding namespace prefixes to namespace URIs. This is done by placing one or more xmlns parts before the xpointer part. The syntax is xmlns(prefix=URI). For example, this XPointer maps the svg prefix to the http://www.w3.org/2000/svg namespace and then searches out all rect elements in that namespace:

xmlns(svg=http://www.w3.org/2000/svg) xpointer(//svg:rect)

As with most other uses of namespaces, only the URI matters in an XPointer, not the prefix. The previous XPointer finds all rect elements in the http://www.w3.org/2000/svg namespace regardless of what prefix they use or whether they're in the default namespace.

There is no way to define a default, unprefixed namespace for an XPointer. However, prefixed names in an XPointer can refer to unprefixed but namespace-qualified elements in the targeted document. For example, this XPointer finds the third div element in an XHTML document:

xmlns(html=http://www.w3.org/1999/xhtml) xpointer(//html:div[3])

It uses the prefix html to identify the XHTML namespace, even though XHTML documents never use prefixes themselves.

More than one namespace prefix can be used simply by adding extra xmlns parts. For example, this XPointer seeks out svg elements in XHTML documents by declaring one prefix each for the SVG and XHTML namespaces:

xmlns(svg=http://www.w3.org/2000/svg)
xmlns(h=http://www.w3.org/1999/xhtml) xpointer(/h:html//svg:svg)

If an XPointer is included in an XML document, the namespace bindings established by that document do not apply to the XPointer. Only the bindings established by the xmlns parts apply to the XPointer. If the xpointer parts contain XPath expressions that refer to elements or attributes in a namespace, then they must be preceded by xmlns parts declaring the namespaces.

Previous | Home | Next
11.4. Child Sequences | Book Index | 11.6. Points
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Points (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

11.6. Points

XPaths, bare names, and child sequences can only point to entire nodes or sets of nodes. However, sometimes you want to point to something that isn't a node, such as the third word of the second paragraph or the year in a date attribute that looks like date="01/03/1950". XPointer adds points and ranges to the XPath syntax to make this possible. A point is the position preceding or following any tag, comment, processing instruction, or character in the #PCDATA. Points can also be positions inside comments, processing instructions, or attribute values. Points cannot be located inside an entity reference, though they can be located inside the entity's replacement text. A range is the span of parsed character data between two points. Nodes, points, and ranges are collectively called locations; a set that may contain nodes, points, and ranges is called a location set. In other words, a location is a generalization of the XPath node that includes points and ranges, as well as elements, attributes, namespaces, text nodes, comments, processing instructions, and the root node.

A point is identified by its container node and a non-negative index into that node. If the node contains child nodes--that is, if it's a document or element node--then there are points before and after each of its children (except at the ends, where the point after one child node will also be the point before the next child node). If the node does not contain child nodes--that is, if it's a comment, processing instruction, attribute, namespace, or text node--then there's a point before and after each character in the string value of the node, and again the point after one character will be the same as the point before the next character.

Consider the document in Example 11-1. It contains a novel element that has seven child nodes, three of which are element nodes and four of which are text nodes containing only whitespace.

Example 11-1. A novel document

<?xml version="1.0"?>
<?xml-stylesheet type="text/css" value="novel.css"?>
<!-- You may recognize this from the last chapter -->
<novel copyright="public domain">
  <title>The Wonderful Wizard of Oz</title>
  <author>L. Frank Baum</author>
  <year>1900</year>
</novel>

There are eight points directly inside the novel element numbered from 0 to 7, one immediately after and one immediately before each tag. Figure 11-1 identifies these points.

Figure 11-1. The points inside the novel element

Inside the text node child of the year element, there are five points:

  • Point 0 between <year> and 1

  • Point 1 between 1 and 9

  • Point 2 between 9 and 0

  • Point 3 between 0 and 0

  • Point 4 between 0 and </year>

Notice that the points occur between the characters of the text rather than on the characters themselves. Points are zero-dimensional. They identify a location, but they have no extension, not even a single character. To indicate one or more characters, you need to specify a range between two points.

XPointer adds two functions to XPath that make it very easy to select the first and last points inside a node, start-point( ) and end-point( ). For example, this XPointer identifies the first point inside the title element, that is, the point between the title node and its text node child:

xpointer(start-point(//title))

This XPointer indicates the point immediately before the </author> tag:

xpointer(end-point(//author))

If there were multiple title and author elements in the document, then these functions would select multiple points.

This XPointer points to the point immediately before the letter T in "The Wonderful Wizard of Oz":

xpointer(start-point(//title/text( )))

This point falls immediately after the point indicated by xpointer(start-point(//title)). These are two different points, even though they fall between the same two characters (> and T) in the text.

To select points other than the start-point or end-point of a node, you first need to form a range that begins or ends with the point of interest using string-range( ) and then use the start-point or end-point function on that range. We take this up in the next section.

Previous | Home | Next
11.5. Namespaces | Book Index | 11.7. Ranges
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Ranges (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

11.7. Ranges

A range is the span of parsed character data between two points. It may or may not represent a well-formed chunk of XML. For example, a range can include an element's start-tag but not its end-tag. This makes ranges suitable for uses such as representing the text a user selected with the mouse. Ranges are created with four functions XPointer adds to XPath:

  • range( )

  • range-to( )

  • range-inside( )

  • string-range( )

11.7.1. The range( ) function

The range( ) function takes as an argument an XPath expression that returns a location set. For each location in this set, the range( ) function returns a range exactly covering that location; that is, the start-point of the range is the point immediately before the location, and the end-point of the range is the point immediately after the location. If the location is an element node, then the range begins right before the element's start-tag and finishes right after the element's end-tag. For example, consider this XPointer:

xpointer(range(//title))

When applied to Example 11-1, it selects a range exactly covering the single title element. If there were more than one title element in the document then it would return one range for each such title element. If there were no title elements in the document, then it wouldn't return any ranges.

Now consider this XPointer:

xpointer(range(/novel/*))

If applied to Example 11-1, it returns three ranges, one covering each of the three child elements of the novel root element.

11.7.2. The range-inside( ) function

The range-inside( ) function takes as an argument an XPath expression that returns a location set. For each location in this set, it returns a range exactly covering the contents of that location. For anything except an element node this will be the same as the range returned by range( ). For an element node, this range includes everything inside the element, but not the element's start-tag or end-tag. For example, when applied to Example 11-1, xpointer(range-inside(//title)) returns a range covering The Wonderful Wizard of Oz but not <title>The Wonderful Wizard of Oz</title>. For a comment, processing instruction, attribute, text, or namespace node, this range covers the string value of that node. For a range, this range is the range itself. For a point, this range begins and ends with that point.

11.7.3. The range-to( ) function

The range-to( ) function is evaluated with respect to a context node. It takes a location set as an argument that should return exactly one location. The start-points of the context nodes are the start-points of the ranges it returns. The end-point of the argument is the end-point of the ranges. If the context node set contains multiple nodes, then the range-to( ) function returns multiple ranges.

TIP: This function is underspecified in the XPointer candidate recommendation. In particular, what should happen if the argument contains more or less than one location is not clear.

For instance, suppose you want to produce a single range that covers everything between <title> and </year> in Example 11-1. This XPointer does that by starting with the start-point of the title element and continuing to the end-point of the year element:

xpointer(//title/range-to(year))

Ranges do not necessarily have to cover well-formed fragments of XML. For instance, the start-tag of an element can be included but the end-tag left out. This XPointer selects <title>The Wonderful Wizard of Oz:

xpointer(//title/range-to(text( )))

It starts at the start-point of the title element, but it finishes at the end-point of the title element's text node child, thereby omitting the end-tag.

11.7.4. The string-range( ) function

The string-range( ) function is unusual. Rather than operating on a location set including various tags, comments, processing instructions, and so forth, it operates on the text of a document after all markup has been stripped from it. Tags are more or less ignored.

The string-range( ) function takes as arguments an XPath expression identifying locations and a substring to try to match against the XPath string value of each of those locations. It returns one range for each match, exactly covering the matched string. Matches are case sensitive. For example, this XPointer produces ranges for all occurrences of the word "Wizard" in title elements in the document:

xpointer(string-range(//title, "Wizard"))

If there are multiple matches, then multiple ranges are returned. For example, this XPointer returns two ranges when applied to Example 11-1, one covering the W in "Wonderful" and one covering the W in "Wizard":

xpointer(string-range(//title, "W"))
TIP: This function is also underspecified in the XPointer candidate recommendation. In particular, it is not clear what happens when there are overlapping matches.

You can also specify an offset and a length to the function so that strings start a certain number of characters from the beginning of the match and continue for a specified number of characters. The point before the first character in the string to search is 1. For example, this XPointer selects the first four characters after the word "Wizard" in title elements:

xpointer(string-range(//title, "Wizard", 7, 4))

Nonpositive indices work backwards in the document before the beginning of the match. For example, this XPointer selects the first four characters before the word "Wizard" in title elements:

xpointer(string-range(//title, "Wizard", -3, 4))

If the offset or length causes the range to fall outside the document, then no range is returned.

Since string ranges can begin and end at pretty much any character in the text content of a document, they're the way to indicate points that don't fall on node boundaries. Simply create a string range that either begins or ends at the position you want to point to, and then use start-point( ) or end-point( ) on that range. For example, this XPointer returns the point immediately before the word "Wizard" in the title element in Listing 11-1:

xpointerstart-point(start-pointxpointer(string-range(//title, "Wizard")))

11.7.5. Relative XPointers

Normally, an XPointer is a fragment identifier attached to a URL. The root node of the document the URL points to is the context location for the XPointer. However, XPointers can also be used by themselves without explicit URLs in XML documents. By default, the context node for such an XPointer is the root node of the document where the XPointer appears. However, either the here( ) or the origin( ) function can change the context node for the XPointer's XPath expression.

11.7.6. here( )

The here( ) function is only used inside XML documents. It refers to the node that contains the XPointer or, if the node that contains the XPointer is a text node, the element node that contains that text node. here( ) is useful in relative links. For example, these navigation elements link to the page elements preceding and following the pages in which they're contained.

<page>
  content of the page...
  <navigation xlink:type="simple"
    xlink:href="#xpointer(here( )/../../preceding-sibling::page[1])">
    Previous
  </navigation>
  <navigation xlink:type="simple"
    xlink:href="#xpointer(here( )/../../following-sibling::page[1])">
    Next
  </navigation>
</page>

In these elements, the here( ) function refers to the xlink:href attribute nodes that contain the XPointer. The first .. selects the navigation parent element. The second .. selects its parent page element, and the final location step selects the previous or next page element.

11.7.7. origin( )

The origin( ) function is useful when the document has been loaded from an out-of-line link. It refers to the node from which the user is initiating traversal, even if that is not the node that defines the link. For example, consider an extended link like this one. It has many novel elements, each of which is a locator that shares the same label:

<series xlink:type="extended" xmlns:xlink="http://www.w3.org/1999/xlink">

  <!-- locator elements -->
  <novel xlink:type="locator" xlink:label="oz"
         xlink:href="ftp://archive.org/pub/etext/etext93/wizoz10.txt">
    <title>The Wonderful Wizard of Oz</title>
    <year>1900</year>
  </novel>
  <novel xlink:type="locator" xlink:label="oz"
         xlink:href="ftp://archive.org/pub/etext/etext93/ozland10.txt">
    <title>The Marvelous Land of Oz</title>
    <year>1904</year>
  </novel>
  <novel xlink:type="locator" xlink:label="oz"
         xlink:href="ftp://archive.org/pub/etext/etext93/wizoz10.txt">
    <title>Ozma of Oz</title>
    <year>1907</year>
  </novel>
  <!-- many more novel elements... -->

  <sequel xlink:type="locator" xlink:label="next"
        xlink:href="#xpointer(origin( )/following-sibling::novel[1])" />
  <next xlink:type="arc" xlink:from="oz" xlink:to="next" />

</series>

The sequel element uses an XPointer and the origin( ) function to define a locator that points to the following novel in the series. If the user is reading The Wonderful Wizard of Oz, then the sequel element locates The Marvelous Land of Oz. If the user is reading The Marvelous Land of Oz, then that same sequel element locates Ozma of Oz, and so on. The next element defines links from each novel (since they all share the label oz) to its sequel. The ending resource changes from one novel to the next.

Previous | Home | Next
11.6. Points | Book Index | 12. Cascading Style Sheets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Cascading Style Sheets (CSS) (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 12. Cascading Style Sheets (CSS)

Contents:

The Three Levels of CSS
CSS Syntax
Associating Stylesheets with XML Documents
Selectors
The Display Property
Pixels, Points, Picas, and Other Units of Length
Font Properties
Text Properties
Colors

The names of most elements describe the semantic meaning of the content they contain. However, ultimately this content needs to be formatted and displayed to users. For this to occur, there must be a step where formatting information is applied to the XML document and the semantic markup is transformed into presentational markup. There are a variety of choices for the syntax of this presentation layer. However, two are particularly noteworthy:

  • Cascading Style Sheets (CSS)

  • XSL Formatting Objects (XSL-FO)

CSS is a non-XML syntax for describing the appearance of particular elements in a document. CSS is a very straight-forward language. No transformation is performed. The parsed character data of the document is presented more or less exactly as it appears in the XML document, though of course you can always transform the document with XSLT and then apply a CSS stylesheet to it if you need to rearrange the content of a document before showing it to the user. A CSS stylesheet does not change the markup of an XML document at all; it merely applies styles to the content that already exists.

By way of contrast, XSL-FO is a complete XML application for describing the layout of text on a page. It has elements that represent pages, blocks of text on the pages, graphics, horizontal rules, and more. You do not normally work with this application directly. Instead, you write an XSLT stylesheet that transforms your document's native markup into XSL-FO. The application rendering the document reads the XSL-FO and displays it to the user.

In this and the next chapter, we'll demonstrate the features of the two major stylesheet languages by applying them to the simple well-formed XML document shown in Example 12-1. This document does not have a document type declaration and is not valid, though a DTD or schema could be added easily enough. In general, DTDs and schemas don't have any impact on stylesheets, except insofar as they change the document content through entity declarations, default attribute values, and the like.

Example 12-1. Marjorie Anderson's recipe for Southern Corn Bread

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml-stylesheet type="text/css" href="recipe.css"?>
<recipe source="Marjorie Anderson">
  <dish>Southern Corn Bread</dish>
  <ingredients>
    <ingredient>
      <quantity>1 cup</quantity>
      <component>flour</component>
    </ingredient>
    <ingredient>
      <quantity>4 tablespoons</quantity>
      <component>Royal Baking Powder</component>
    </ingredient>
    <ingredient>
      <quantity>1/2 teaspoon</quantity>
      <component>salt</component>
    </ingredient>
    <ingredient>
      <quantity>1 cup</quantity>
      <component>corn meal</component>
    </ingredient>
    <ingredient>
      <quantity>11/2 cups</quantity>
      <component>whole milk</component>
    </ingredient>
    <ingredient>
      <quantity>4 tablespoons</quantity>
      <component>melted butter</component>
    </ingredient>
  </ingredients>

  <directions>
    <step>Sift flour, baking powder, sugar &amp; salt together.</step>
    <step>Add 1 cup corn meal.</step>
    <step>
      Beat egg in cup and add beaten egg and 11/2 cups whole
       milk to make a batter. Stir well.
    </step>
    <step>
      Add melted shortening and beat until light and thoroughly mixed.
    </step>
    <step>
      Pour into greased shallow pan or greased muffin rings.
    </step>
    <step>
      Bake in hot oven at <temperature>425º F</temperature> for
      <duration>25 minutes</duration>.
    </step>
    <step optional="yes">
      Cut into squares if cooked in shallow pan.
    </step>
  </directions>

  <story>
    After my mother-in-law <person>Marjorie Anderson</person> died,
    Beth and I found this recipe written on the "extra recipes"
    page in a local cookbook in her cupboard.
    This was published by The Episcopal Churchwomen,
    Church of Ascension, <city>Mt. Sterling</city>,
    <state>Kentucky</state>.
  </story>

</recipe>

12.1. The Three Levels of CSS

At the time of this writing, there are three versions of CSS. CSS Level 1 was an early W3C Recommendation from 1996 for HTML only, though the extension to XML was obvious. The CSS Level 1 specification was incomplete and led to inconsistent browser implementations.

CSS Level 2 is the current recommendation and the version of CSS on which this chapter focuses. CSS Level 2 places XML on an equal footing with HTML. Indeed, CSS Level 2 often works better with XML than with HTML because CSS styles don't have to interact with any predefined rendering semantics. For the most part, CSS Level 2 is a superset of CSS Level 1. That is, all CSS Level 1 stylesheets are also CSS Level 2 stylesheets that mean pretty much the same thing.

The W3C is now working on CSS Level 3. When complete, it will modularize the CSS specification so software can implement particular subsets of CSS functionality without having to implement everything. For instance, an audio browser could implement audio stylesheets but ignore the visual formatting model. Furthermore, CSS Level 3 adds a number of features to CSS, including multi-column layouts, better support for non-Western languages, such as Arabic and Chinese, XML namespace support, more powerful selectors, paged media, and more. However, CSS Level 3 is not yet implemented by any browsers.

Previous | Home | Next
11.7. Ranges | Book Index | 12.2. CSS Syntax
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


CSS Syntax (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.2. CSS Syntax

CSS syntax isn't XML syntax, but the syntax is so trivial this hardly matters. A CSS stylesheet is simply a list of the elements you want to apply the styles to, normally one to a line. If the element is in a namespace, then the qualified name like recipe:dish must be used. The prefix must be the same in the stylesheet as in the XML document. Each element name is followed by the list of styles you want to apply to that element. Comments can be inserted using the /*...*/ format of comments familiar to C programmers. Whitespace isn't particularly significant, so it can be used to format the stylesheet. Example 12-2 is a simple CSS stylesheet for the recipe document in Example 12-1. Figure 12-1 shows the recipe document as rendered and displayed by the Opera 4.01 browser with this stylesheet.

Example 12-2. A CSS stylesheet for recipes

/* Defaults for the entire document */
recipe  {font-family: "New York", "Times New Roman", serif;
         font-size: 12pt }

/* Make the dish look like a headline */
dish    {
  display: block;
  font-family: Helvetica, Arial, sans-serif;
  font-size: 20pt;
  font-weight: bold;
  text-align: center
}

/* A bulleted list */
ingredient  {display: list-item; list-style-position: inside }

/* Format these two items as paragraphs */
directions, story {
  display: block;
  margin-top: 12pt;
  margin-left: 4pt
}

Figure 12-1. A semantically tagged XML document after application of a CSS stylesheet

This stylesheet has four style rules. Each rule names the element(s) it formats and follows that with a pair of curly braces containing the style properties to apply to those elements. Each property has a name such as font-family and a value such as "New York", "Times New Roman", serif. Properties are separated from each other by semicolons. Neither the names nor the values are case sensitive. That is, font-family is the same as FONT-FAMILY or Font-Family. CSS Level 2 defines over 100 different style properties. However, you don't need to know all of these. Reasonable default values are provided for all the properties you don't set.

For example, the first rule applies to the recipe element and says that it should be formatted using the New York font at a 12 point size. If New York isn't available, then Times New Roman will be chosen instead; if that isn't available, then any convenient serif font will suffice. These styles also apply to all descendants of the recipe element; that is, the styles cascade down the tree. Since recipe is the root element, this sets the default font for the entire document.

The second rule makes the dish element look like a heading, as you can see in Figure 12-1. It's set to a much larger sans serif font and made bold and centered besides. Furthermore, its display style is set to block. This means there'll be a line break between the dish and its next and previous sibling elements. The third rule formats the ingredients as a bulleted list, while the fourth rule formats both the directions and story elements as more-or-less straight-forward paragraphs with a little extra whitespace around their top and left-hand sides.

Not all the elements in the document have style rules and not all need them. For example, the step element is not specifically styled. Rather, it simply inherits a variety of styles from its ancestor elements directions and recipe, as well as using some defaults. A different stylesheet could add a rule for the step element that overrides the styles it inherits. For example, this rule would set its font to 10 point Palatino:

step  {font-family: Palatino, serif; font-size: 10pt }
Previous | Home | Next
12. Cascading Style Sheets | Book Index | 12.3. Associating Stylesheets with XML Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Associating Stylesheets with XML Documents (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.3. Associating Stylesheets with XML Documents

CSS stylesheets are primarily intended for use in web pages. Web browsers find the stylesheet for a document by looking for xml-stylesheet processing instructions in the prolog of the XML document. This processing instruction should have a type pseudoattribute with the value text/css and an href pseudoattribute whose value is an absolute or relative URL locating the stylesheet document. For example, this is the processing instruction that attaches the stylesheet in Listing 12-2 (recipe.css) to the file in Example 12-1 (cornbread.xml) if both are found in the same directory.

<?xml-stylesheet type="text/css" href="recipe.css"?>

Including the required type and href pseudoattributes, the xml-stylesheet processing instruction can have up to six pseudoattributes:

type
This is the MIME media type of the stylesheet; text/css for CSS and application/xml (not text/xsl!) for XSLT.

href
This is the absolute or relative URL where the stylesheet can be found.

charset
This names the character set in which the stylesheet is written, such as UTF-8 or ISO-8859-7. There's no particular reason this has to be the same as the character set in which the document is written. The names used are the same ones used for the encoding pseudoattribute of the XML declaration.

title
This pseudoattribute names the stylesheet. If more than one stylesheet is available for a document, the browser may (but is not required to) present readers with a list of the titles of the available stylesheets and ask them to choose one.

media
Printed pages, television screens, and computer displays are all fundamentally different media that require different styles. For example, comfortable reading on screen requires much larger fonts than on a printed page. This pseudoattribute specifies the media types this stylesheet should apply to. There are nine predefined values:

  • screen

  • tty

  • tv

  • projection

  • handheld

  • print

  • braille

  • aural

  • all

By including several xml-stylesheet processing instructions, each pointing to a different stylesheet and each using a different media type, you can make a single document attractive in many different environments.

alternate
This pseudoattribute must be assigned one of the two values yes or no. yes means this is an alternate stylesheet, not normally used. no means this is the stylesheet that will be chosen unless the user indicates that they want a different one. The default is no.

For example, this group of xml-stylesheet processing instructions could be placed in the prolog of the recipe document to make it more accessible on a broader range of devices:

<?xml-stylesheet type="text/css" href="recipe.css" media="screen"
             alternate="no"  title="For Web Browsers" charset="US-ASCII"?>
<?xml-stylesheet type="text/css" href="printable_recipe.css" media="print"
             alternate="no" title="For Printing" charset="ISO-8859-1"?>
<?xml-stylesheet type="text/css" href="big_recipe.css" media="projection"
             alternate="no" title="For presentations" charset="UTF-8"?>
<?xml-stylesheet type="text/css" href="tty_recipe.css" media="tty"
             alternate="no" title="For Lynx" charset="US-ASCII"?>
<?xml-stylesheet type="text/css" href="small_recipe.css" media="handheld"
             alternate="no" title="For Palm Pilots" charset="US-ASCII"?>
Previous | Home | Next
12.2. CSS Syntax | Book Index | 12.4. Selectors
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Selectors (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.4. Selectors

CSS provides limited abilities to select the elements to which a given rule applies. Many stylesheets only use element names and lists of element names separated by commas, as shown in Example 12-2. However, CSS provides some other basic selectors you can use, though they're by no means as powerful as the XPath syntax of XSLT.

12.4.1. The Universal Selector

The asterisk matches any element at all; that is, it applies the rule to everything in the document that does not have a more specific, conflicting rule. For example, this rule says that all elements in the document should use a large font:

* {font-size: large}

12.4.2. Matching Descendants, Children, and Siblings

An element name A followed by another element name B matches all B elements that are descendants of A elements. For example, this rule matches quantity elements that are descendants of ingredients elements, but not other ones that appear elsewhere in the document:

ingredients quantity {font-size: medium}

If the two element names are separated by a greater than sign (>), then the second element must be an immediate child of the first for the rule to apply. For example, this rule gives quantity children of ingredient elements the same font-size as the ingredient element:

ingredient > quantity {font-size: inherit}

If the two element names are separated by a plus sign (+), then the second element must be the next sibling element immediately after the first element. For example, this style rule sets the border-top-style property for only the first story element following a directions element:

directions + story {border-top-style: solid}

12.4.3. Attribute Selectors

Square brackets allow you to select elements with particular attributes or attribute values. For example, this rule hides all step elements that have an optional attribute:

step[optional] {display: none}

This rule hides all elements that have an optional attribute regardless of their name:

*[optional] {display: none}

An equals sign selects an element by a given attribute's value. For example, this rule hides all step elements that have an optional attribute with the value yes:

step[optional="yes"] {display: none}

The ~= operator selects elements that contain a given word as part of the value of a specified attribute. The word must be complete and separated from other words in the attribute value by whitespace, as in a NMTOKENS or ENTITIES attribute. That is, this is not a substring match. For example, this rule makes bold all recipe elements whose source attribute contains the word "Anderson":

recipe[source~="Anderson"] {font-weight: bold}

Finally, the |= operator matches against the first word in a hyphen-separated attribute value, such as Anderson-Harold or fr-CA.

CSS also provides a special syntax for selecting elements with a given ID value, even when you don't know exactly what the name of the ID type attribute is. Simply separate the ID from the element name with a sharp sign (#). For example, this rule applies to the single step element whose ID type attribute has the value P833:

step#P833 { font-weight: 800 }

12.4.4. Pseudoclass Selectors

Pseudoclass selectors match elements according to a condition not involving their name. There are seven of these. They are all separated from the element name by a colon. For example, the first-child pseudoclass matches the first child element of the named element. When applied to Example 12-1, this rule italicizes the first, and only the first, step element:

step:first-child {font-style: italic}

The link pseudoclass matches the named element if and only if that element is the source of an as yet unvisited link. For example, this rule makes all links in the document blue and underlined:

*:link {color: blue; text-decoration: underline}

The visited pseudoclass applies to all visited links of the specified type. For example, this rule marks all visited links as purple and underlined:

*:visited {color: purple; text-decoration: underline}

The active pseudoclass applies to all elements that the user is currently activating (for example, by clicking the mouse on). Exactly what it means to activate an element depends on the context, and indeed not all applications can activate elements. For example, this rule marks all active elements as red:

*:active {color: red}

The linking pseudoclasses are not yet well-supported for XML documents because most browsers don't recognize XLinks. So far, Mozilla and Netscape 6, the only browsers that recognize XLinks, are the only browsers that will apply these pseudoclasses to XML.

The hover pseudoclass applies to elements on which the cursor is currently positioned but which the user has not yet activated. For example, this rule marks all these elements as green and underlined:

*:hover {color: green; text-decoration: underline}

The focus pseudoclass applies to the element that currently has the focus. For example, this rule draws a one-pixel red border around the element with the focus, assuming there is such an element:

*:focus {border: 1px solid red }

Finally, the lang pseudoclass matches all elements in the specified language as determined by the xml:lang attribute. For example, this rule uses the David New Hebrew font for all elements written in Hebrew (more properly, all elements whose xml:lang attribute has the value he or any subtype thereof).

*:lang(he) {font-family: "David New Hebrew"}

12.4.5. Pseudoelement Selectors

Pseudoelement selectors match things that aren't actually elements. Like pseudoclass selectors they're attached to an element selector by a colon. There are four of these:

  • first-letter

  • first-line

  • before

  • after

The first-letter pseudoelement selects the first letter of an element. For example, this rule makes the first letter of the story element a drop cap:

story:first-letter {
  font-size: 200%;
  font-weight: bold;
  float: left;
  padding-right: 3pt
}

The first-line pseudoelement applies formatting to all characters in the first line of a block-level element. If the browser window is resized so that characters move into or out of the first line, then the formatting changes to match. For example, this rule formats the first line of the story element in small capitals instead of lowercase letters:

story:first-line {font-variant: small-caps}

The before and after pseudoelements select the points immediately before and after the specified element. You can't really apply font or text styles to a zero-width point, but you can insert text at that point using the content property. For example, this rule inserts the string "Ingredients!" before the ingredients element:

ingredients:before {content: "Ingredients! "}

This rule places the number of the step in front of each step element in the form 1., 2., 3., and so on:

step:before {
  content: counter(step) ". ";
  counter-increment: step;
}
Previous | Home | Next
12.3. Associating Stylesheets with XML Documents | Book Index | 12.5. The Display Property
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Display Property (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.5. The Display Property

Display is one of the most important CSS properties. This property determines how the element will be positioned on the page. There are 18 legal values for this property. However, the two primary values are inline and block. The display property can also be used to create lists and tables, as well as to hide elements completely.

12.5.1. Inline Elements

Setting the display to inline, the default value, places the element in the next available position from left to right, much as each word in this paragraph is positioned. (The exact direction can change for right-to-left languages like Hebrew or top-to-bottom languages like traditional Chinese.) The text may be wrapped from one line to the next if necessary, but there won't be any hard line breaks between each inline element. In Example 12-1 and 12-2, the quantity, step, person, city, and state elements were all formatted as inline. This didn't need to be specified explicitly because it's the default.

12.5.2. Block Elements

In contrast to inline elements, an element set to display: block is separated from its siblings, generally by a line break. For example, in HTML, paragraphs and headings are block elements. In Example 12-1 and 12-2, the dish, directions, and story elements were all formatted with display: block.

12.5.3. List Elements

An element whose display property is set to list-item is also formatted as a block-level element. However, a bullet is inserted at the beginning of the block. The list-style-type, list-style-image, and list-style-position properties control which character or image is used for a bullet and exactly how the list is indented. For example, this rule would format the steps as a numbered list rather than rendering them as a single paragraph:

step {
  display: list-item;
  list-style-type: decimal;
  list-style-position: inside
}

12.5.4. Hidden Elements

An element whose display property is set to none is not included in the rendered document the reader sees. It is invisible and does not occupy any space or affect the placement of other elements. For example, this style rule hides the story element completely:

story {display: none}

12.5.5. Table Elements

There are ten display values that identify elements as parts of a table. These are:

  • table

  • inline-table

  • table-row-group

  • table-header-group

  • table-footer-group

  • table-row

  • table-column-group

  • table-column

  • table-cell

  • table-caption

These display values have the obvious meanings by analogy with HTML 4.0 table tags. Their use should be consistent with each other and with other elements in the document. For instance, an element formatted as a table-row element should have a parent element formatted as a table and child elements formatted as table-cells. For example, these three rules format the ingredients as a simple table:

ingredients         { display: table      }
ingredient          { display: table-row  }
quantity, component { display: table-cell }
Previous | Home | Next
12.4. Selectors | Book Index | 12.6. Pixels, Points, Picas, and Other Units of Length
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Pixels, Points, Picas, and Other Units of Length (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.6. Pixels, Points, Picas, and Other Units of Length

Many CSS properties represent lengths. Some of the most important (though far from all) of these include:

  • border-width

  • font-size

  • line-height

  • margin-left, margin-top, margin-right, and margin-bottom

  • left

  • top

  • height

  • width

CSS provides many different units to specify length. They fall into two groups:

  • Absolute units of length such as inches, centimeters, millimeters, points, and picas.

  • Relative units such as ems, exes, pixels, and percentages.

Absolute units of length are appropriate for printed media (that is, paper), but should be avoided in other media. Relative units should be used for all other media, except for pixels, which probably shouldn't be used at all. For example, this style rule sets the dish element to be exactly one-half centimeter high:

dish { height: 0.5cm }

However, documents intended for display on screen media like television sets and computer monitors should not be set to fixed sizes. For one thing, the size of an inch or other absolute unit can vary depending on the resolution of the monitor. For another, not all users like the same defaults, and what looks good on one monitor may be illegible on another. Instead, you should use units that are relative to something, such as an em, which is relative to the width of the uppercase letter M, in the current font, or ex, which is relative to the height of the lowercase letter x in the current font. For example, this rule sets the line-height property of the story element to one and half times the height of the letter x:

story { line-height: 1.5ex}

Pixel is also a relative unit, though what it's relative to is the size of a pixel on the current display. This is generally somewhere in the vicinity of a point, but it can vary from system to system. In general, we don't recommend using pixels unless you need to line something up with a bitmapped graphic displayed at exactly a 1:1 ratio. Web pages formatted with pixel lengths invariably look too small or too large on a large fraction of users' monitors.

One very useful technique is to specify lengths as percentages of some other length. Exactly what this is a percentage of varies from property to property. For instance, if the line-height is given as a percentage, then it's calculated with respect to the font-height of the same element. These two rules set the font-height of the dish element to 0.5 centimeters and the line-height of the dish element to 0.75 centimeters:

dish  { font-height: 0.5cm }
dish  { line-height: 150% }
Previous | Home | Next
12.5. The Display Property | Book Index | 12.7. Font Properties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Font Properties (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.7. Font Properties

Fonts are one of the most basic things designers want to set with CSS. Is the text italic? Is it bold? What typeface and size are used? CSS provides properties to set all these basic characteristics of text. In particular, you can set these properties:

font-family
This is a list of font names, separated by commas, in order of preference. The last name in the list should always be one of the generic names serif, sans-serif, monospace, cursive, or fantasy. Multiword names like "Times New Roman" should be enclosed in quotes.

font-style
The value italic indicates that an italic version of the font should be used if one is available. The value oblique suggests that the text should be algorithmically slanted, as opposed to using a specially designed italic font. The default is normal (no italicizing or slanting). An element can also be set to inherit the font-style of the parent element.

font-size
This is the size of the font. This should be specified as one of the values xx-small, x-small, small, medium, large, x-large, xx-large. Alternately, it can be given as a percentage of the font-size of the parent element. It can also be specified as a length like 0.2cm or 12pt, but this should only be done for print media.

font-variant
If this property is set to small-caps, then lowercase text is rendered in smaller capitals like this instead of normal lowercase letters.

font-weight
This property determines how bold or light the text is. It's generally specified as one of the keywords normal (the default), bold, bolder, or lighter. It can also be set to any multiple of 100 from 100 (lightest) to 900 (darkest). However, not all browsers necessarily provide nine different levels of boldness.

font-stretch
This property adjusts the space between letters to make the text more or less compact. Legal values include normal (the default), wider, narrower, ultra-condensed, extra-condensed, condensed, semi-condensed, semi-expanded, expanded, extra-expanded, and ultra-expanded.

For example, this style rule uses all of the previous properties to make the dish element a suitably impressive headline:

dish {
  font-family: Helvetica, Arial, sans-serif;
  font-size: x-large;
  font-style: italic;
  font-variant: small-caps;
  font-weight: 900;
  font-stretch: semi-expanded
}
Previous | Home | Next
12.6. Pixels, Points, Picas, and Other Units of Length | Book Index | 12.8. Text Properties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Text Properties (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.8. Text Properties

Text properties cover those aspects of text formatting other than what can be adjusted merely by changing the font. These include how far the text is indented, how the paragraph is aligned, and so forth. The most common of these properties include:

text-indent
The text-indent property specifies how far in to indent the first line of the block. (Indents of all lines are generally applied via margin properties.) Hanging indents can be specified by making text-indent negative. This property only applies to block-level elements. For example, this style rule indents the first line of the story element by half an inch from the left side:

story { text-indent: 0.5in }
text-align
The text-align property can be set to left, right, center, or justify to align the text with the left edge of the block or the right edge of the block, to center the text in the block, or to spread the text out across the block. This property only applies to block-level elements.

text-decoration
The text-decoration property can be set to underline, overline, line-through or blink to produce the obvious effects. Note, however, that the CSS2 specification specifically allows browsers to ignore the request to make elements blink. This is a good thing.

text-transform
The text-transform property has three main values: capitalize, uppercase, and lowercase. Uppercase changes all the text to capital letters LIKE THIS. Lowercase changes all the text to lowercase letters like these. Capitalize simply uppercases the first letter of each word Like This, but leaves the other letters alone. The default value of this property is none, which performs no transformation. It can also be set to inherit to indicate that the same transform as used on the parent element should be used.

TIP: Changing the case in English is fairly straightforward, but this isn't true of all languages. In particular, software written by native English speakers tends to do a very poor job of algorithmically changing the case in ligature-heavy European languages like Maltese or context-sensitive languages like Arabic. Outside of English text, it's best to make the transformations directly in the source document rather than relying on the stylesheet engine to make the correct decisions about which letters to capitalize.

white-space
The white-space property determines whether text is wrapped. It has only four legal values: normal, pre, nowrap, and inherit. Normal is of course the default and simply means to wrap the text wherever convenient, much as is done in this paragraph. Pre means to preserve all line breaks and whitespace in the document, as does the pre element in HTML. Nowrap means that runs of whitespace can be condensed, but that line breaks will not be inserted. Finally, inherit simply takes on the same behavior as the parent element.

Previous | Home | Next
12.7. Font Properties | Book Index | 12.9. Colors
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Colors (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

12.9. Colors

CSS has several properties for changing the color of various items:

color
The color of the text itself (black on this page)

background-color
The color of the background behind the text (white on this page)

border-color
The color of a visible box surrounding the text

CSS uses a 24-bit color space to specify colors, much as HTML does. Always keep in mind, however, that just because you can specify a color doesn't mean any given device can render it. A black-and-white printer isn't going to print red no matter how you identify it; it might give you some nice shades of gray though. Like many other properties, color depends on the medium in which the document is presented.

The simplest way to choose a color is through one of these 16 named constants: aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, and yellow. There are also a number of colors that are defined to be the same as some part of the user interface. For instance, WindowText is the same color as text in windows on the local system.

Beyond this small list, you can specify the color of an item by specifying the three components--red, green, and blue--of each color, much as you do for background colors on HTML pages. Each component is given as a number between 0 and 255 with 255 being the maximum amount of the color. Numbers can be given in decimal or hexadecimal. For example, these rules use hexadecimal syntax to color the dish element pure red, the story element pure green, and the directions element pure blue:

dish       { color: #FF0000 }
story      { color: #00FF00 }
directions { color: #0000FF }

If you prefer, you can specify the color as decimals separated by commas inside an rgb( ) function. For example, white is rgb(255,255,255); black is rgb(0,0,0). Colors in which each component is equal form various shades of gray. These rules use decimal syntax to color the ingredient element a light shade of gray but its quantity child element a darker shade of gray:

ingredient { color: rgb(43,43,43) }
quantity   { color: rgb(21,21,21) }

You can also specify the color as percentages of each primary color from 0 to 100%. For example, the previous rules can be rewritten like this:

ingredient { color: rgb(16.9%,16.9%,16.9%) }
quantity   { color: rgb(8.2%,8.2%,8.2%) }
Previous | Home | Next
12.8. Text Properties | Book Index | 13. XSL Formatting Objects
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSL Formatting Objects (XSL-FO) (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 13. XSL Formatting Objects (XSL-FO)

Contents:

XSL Formatting Objects
The Structure of an XSL-FO Document
Laying Out the Master Pages
XSL-FO Properties
Choosing Between CSS and XSL-FO

The last chapter covered CSS; this chapter discusses XSL-FO. In distinct contrast to CSS, XSL-FO is a complete XML application for describing the precise layout of text on a page. It has elements that represent pages, blocks of text on the pages, graphics, horizontal rules, and more. Most of the time, however, you don't write XSL-FO directly. Instead, you write an XSLT stylesheet that transforms your document's native markup into XSL-FO. The application rendering the document reads the XSL-FO and displays it to the user. Since no major browsers currently support direct rendering of XSL-FO documents, there's normally a third step in which another processor transforms the XSL-FO into a third format, such as PDF or TEX.

Once again we demonstrate the features of XSL-FO by applying it to the simple well-formed XML document shown in Example 12-1 in the last chapter and repeated here in Example 13-1 for convenience.

Example 13-1. Marjorie Anderson's recipe for Southern Corn Bread

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recipe source="Marjorie Anderson">
  <dish>Southern Corn Bread</dish>
  <ingredients>
    <ingredient>
      <quantity>1 cup</quantity>
      <component>flour</component>
    </ingredient>
    <ingredient>
      <quantity>4 tablespoons</quantity>
      <component>Royal Baking Powder</component>
    </ingredient>
    <ingredient>
      <quantity>1/2 teaspoon</quantity>
      <component>salt</component>
    </ingredient>
    <ingredient>
      <quantity>1 cup</quantity>
      <component>corn meal</component>
    </ingredient>
    <ingredient>
      <quantity>11/2 cups</quantity>
      <component>whole milk</component>
    </ingredient>
    <ingredient>
      <quantity>4 tablespoons</quantity>
      <component>melted butter</component>
    </ingredient>
  </ingredients>

  <directions>
    <step>Sift flour, baking powder, sugar &amp; salt together.</step>
    <step>Add 1 cup corn meal.</step>
    <step>
      Beat egg in cup and add beaten egg and 11/2 cups whole
       milk to make a batter. Stir well.
    </step>
    <step>
      Add melted shortening and beat until light and thoroughly mixed.
    </step>
    <step>
      Pour into greased shallow pan or greased muffin rings.
    </step>
    <step>
      Bake in hot oven at <temperature>425º F</temperature> for
      <duration>25 minutes</duration>.
    </step>
    <step optional="yes">
      Cut into squares if cooked in shallow pan.
    </step>
  </directions>

  <story>
    After my mother-in-law <person>Marjorie Anderson</person> died,
    Beth and I found this recipe written on the "extra recipes"
    page in a local cookbook in her cupboard.
    This was published by the The Episcopal Churchwomen,
    Church of Ascension, <city>Mt. Sterling</city>,
    <state>Kentucky</state>.
  </story>
</recipe>

13.1. XSL Formatting Objects

An XSL-FO document describes the layout of a series of nested boxes or areas that are placed on one or more pages. These boxes contain text or occasionally other items, such as an external image or a horizontal rule. There are four kinds of boxes:

  • Block areas

  • Inline areas

  • Line areas

  • Glyph areas

Block and inline areas are created by particular elements in the formatting objects document. Line and glyph areas are created by the formatter as necessary. For the most part, the rendering engine decides exactly where to place the boxes and how big to make them, based on their contents. However, you can specify properties for these boxes that adjust both their relative and absolute position, spacing, and size on a page. Most of the time the individual boxes don't overlap. However, they can be forced to do so by setting the properties absolute-position, left, top, right, and top on the boxes.

Considered by itself, each box has a content area in which its content, generally text but possibly an image or a rule, is placed. This content area is surrounded by a padding area of blank space. An optional border can surround the padding. The size of the area is the combined size of the border, padding, and content. The box may also have a margin that adds blank space outside the box's area, as diagramed in Figure 13-1.

Figure 13-1. Content, padding, border, and margin of an XSL-FO area

Text properties such as font family, font size, alignment, and font weight can be applied by attaching the appropriate properties to one of the boxes that contains the text. Text takes on the properties specified on the nearest enclosing box. Properties are set by attaching attributes to the elements that generate the boxes. For the most part these properties have the same semantics as the CSS properties of the same name. Only the syntax for applying the properties to particular ranges of text is different.

The elements in the XSL-FO document do not map in a one-to-one fashion to the boxes on the page. Instead, the XSL-FO document contains a slightly more abstract representation of the document. The formatting software uses the XSL-FO elements to decide which boxes to create and where to place them. In the process it will split the large blocks, which the XSL-FO document describes with fo:block elements, into smaller line and glyph areas. It may also split single block areas that the XSL-FO document describes into multiple block areas if a page break is required in the middle of a large block, though XSL-FO does let you prevent these breaks if necessary. The formatter also generates the correct number of pages for the content that's found. In short, the XSL-FO document contains hints and instructions that the formatter uses to decide what items to place where on which pages, but you do not need to specify the exact position of each and every box.

Previous | Home | Next
12.9. Colors | Book Index | 13.2. The Structure of an XSL-FO Document
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Structure of an XSL-FO Document (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

13.2. The Structure of an XSL-FO Document

The root element of all XSL-FO documents is fo:root. This element normally declares the fo prefix, which must be mapped to the http://www.w3.org/1999/XSL/Format namespace URI. As always, the prefix can change as long as the URI stays the same. In this chapter, we assume that the prefix fo has been associated with http://www.w3.org/1999/XSL/Format. Thus a typical FO document looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <!-- Formatting object elements -->
</fo:root>

Of course this normally isn't written directly as here. Instead, it's formed by an XSLT template like this one:

<xsl:template match="/">
  <fo:root>
    <xsl:apply-templates/>
  </fo:root>
</xsl:template>

The fo:root element must contain two things, a fo:layout-master-set and a fo:page-sequence . The fo:layout-master-set contains elements describing the overall layout of the pages themselves, that is, how large the pages are, whether they're in landscape or portrait mode, how wide the margins are, and so forth. The fo:page-sequence contains the actual text that will be placed on the pages, along with the instructions for formatting that text as italic, 20 points high, justified, and so forth. It has a master-reference attribute identifying the particular master page that will be used to layout this content. Adding these elements, a formatting objects document looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <fo:layout-master-set>
    <!-- page masters -->
  </fo:layout-master-set>
  <fo:page-sequence master-reference="first">
    <!-- data to place on the page -->
  </fo:page-sequence>
</fo:root>

The formatting engine uses the layout master set to create a page. Then it adds content to the page from the fo:page-sequence until the page is full. Then it creates the next page in the sequence and places the next batch of content on that page. This process continues until all the content has been positioned.

Previous | Home | Next
13. XSL Formatting Objects | Book Index | 13.3. Laying Out the Master Pages
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Laying Out the Master Pages (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

13.3. Laying Out the Master Pages

XSL-FO 1.0 only defines one kind of master page, the fo:simple-page-master . This represents a standard rectangular page with margins on all four sides. This master page also has a unique name given by a master-name attribute. For example, this element describes a master page named first that represents an 8.5 by 11 inch page with one inch margins on all four sides:

<fo:simple-page-master margin-right="1in"  margin-left="1in"
                       margin-bottom="1in" margin-top="1in"
                       page-width="8.5in"  page-height="11in"
                       master-name="first">
  <!-- Separate parts of the page go here -->
</fo:simple-page-master>

The part of the page inside the margins is divided into five regions: the start region, the end region, the before region, the after region, and the body region. Where these fall on a page depends on the writing direction. In left-to-right, top-to-bottom languages like English, start is on the lefthand side, end is on the righthand side, before is on top, and after is on bottom as diagramed in Figure 13-2. However, if the text were Hebrew, then the start region would be on the righthand side of the page, and the end region would be on the lefthand side of the page. If the text were traditional Chinese, then the start would be on top, the end on bottom, the before on the lefthand side, and the after on the righthand side. Other combinations are possible.

Figure 13-2. The five regions in a left-to-right, top-to-bottom writing system

These regions are represented by fo:region-start , fo:region-end , fo:region-before , fo:region-after , and fo:region-body child elements of the fo:simple-page-master element. You can place different content into each of the five regions. For instance, the after region often contains a page number, and the before region may contain the title of the book or chapter.

The body region and the corresponding fo:region-body element are required. The other four are optional. By default, the body region takes up the entire page, and the other four regions have zero area. To specify this simplest page, you add an empty fo:region-body child element to the fo:simple-page-master element like this:

<fo:simple-page-master margin-right="1in"  margin-left="1in"
                       margin-bottom="1in" margin-top="1in"
                       page-width="8.5in"  page-height="11in"
                       master-name="first">
  <fo:region-body/>
</fo:simple-page-master>

However, you can add extent attributes to the four nonbody regions to specify the height of the before and after regions and the width of the start and end regions. Then the region body should have margin properties that are at least as large as the extent of each region to push it out of the way of each nonbody region. Otherwise, content placed in the body will be drawn on top of content placed in the other four regions. For example, this fo:simple-page-master element has half-inch margins on each side, representing the unprintable area on many common printers. The start and end regions are half an inch wide. The before and after regions are one inch wide. The body has margins that match the region sizes.

<fo:simple-page-master margin-right="0.5in"  margin-left="0.5in"
                       margin-bottom="0.5in" margin-top="0.5in"
                       page-width="8.5in"    page-height="11in"
                       master-name="first">
  <fo:region-before extent="0.5in"/>
  <fo:region-after  extent="0.5in"/>
  <fo:region-start  extent="0.5in"/>
  <fo:region-end    extent="0.5in"/>
  <fo:region-body   margin-top="1.0in"  margin-bottom="1.0in"
                    margin-left="0.5in" margin-right="0.5in"/>
</fo:simple-page-master>

Most of the time, the details of the layout-master set are fixed in the stylesheet. For example, here's the revised XSLT template that includes a full fo:layout-master-set :

<xsl:template match="/">
  <fo:root>
    <fo:layout-master-set>
      <fo:simple-page-master margin-right="1in"  margin-left="1in"
                             margin-bottom="1in" margin-top="1in"
                             page-width="8.5in"  page-height="11in"
                             master-name="first">
        <fo:region-body/>
      </fo:simple-page-master>
    </fo:layout-master-set>

    <fo:page-sequence master-reference="first">
      <!-- data to place on the page -->

    </fo:page-sequence>
  </fo:root>
</xsl:template>

13.3.1. Flowing Content into the Pages

Next we add a fo:flow child to the fo:page-sequence where the actual text of the transformed document appears. This element has a flow-name attribute specifying into which region of the page its content will flow. Possible values include xsl-region-body, xsl-region-start, xsl-region-end, xsl-region-before, and xsl-region-after.

The formatter instantiates a page based on the master page named by the fo:page-sequence's master-reference attribute, fills one of its regions with content from the fo:flow element until the page is full, then instantiates a second page, fills it with more content from the fo:flow, instantiates a third page, and continues this process until it's used up all the data in the fo:flow.

The fo:flow element must contain block-level formatting object elements. The most basic of these is fo:block . Others include fo:block-container , fo:list-block , fo:table , and fo:table-and-caption . We'll begin with the most basic, fo:block. A fo:block can contain a combination of raw text and formatting objects such as fo:external-graphic, fo:inline, fo:page-number, fo:footnote, and even other fo:block elements. For the moment, we'll restrict ourselves to parsed character data. For example, here's a basic fo:flow for the recipe:

<fo:flow flow-name="xsl-region-body">
  <fo:block>Southern Corn Bread</fo:block>

  <fo:block>1 cup flour</fo:block>
  <fo:block>4 tablespoons Royal Baking Powder</fo:block>
  <fo:block>1/2 teaspoon salt</fo:block>
  <fo:block>1 cup corn meal</fo:block>
  <fo:block>11/2 cups whole milk</fo:block>
  <fo:block>4 tablespoons melted butter</fo:block>

  <fo:block>
    Sift flour, baking powder, sugar &amp; salt together.
    Add 1 cup corn meal.
    Beat egg in cup and add beaten egg and 11/2 cups whole
    milk to make a batter. Stir well.
    Add melted shortening and beat until light and thoroughly mixed.
    Pour into greased shallow pan or greased muffin rings.
    Bake in hot oven at 425º F for 25 minutes.
    Cut into squares if cooked in shallow pan.
  </fo:block>

  <fo:block>
    After my mother-in-law Marjorie Anderson died,
    Beth and I found this recipe written on the "extra recipes"
    page in a local cookbook in her cupboard.
    This was published by the The Episcopal Churchwomen,
    Church of Ascension, Mt. Sterling, Kentucky.
  </fo:block>

</fo:flow>

Here's an XSLT template that produces the content of this fo:flow element (modulo insignificant whitespace) from Example 13-1 through judicious use of the default templates:

<xsl:template match="dish|ingredient|directions|story">
  <fo:block><xsl:apply-templates/></fo:block>
</xsl:template>

13.3.2. Generating the Finished Document

We now have the minimum set of pieces needed to put together a full XSL-FO document. Example 13-2 is an XSLT stylesheet that transforms documents like Example 13-1 into XSL formatting objects documents.

Example 13-2. An XSLT to XSL-FO transform

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <xsl:template match="/">
    <fo:root>
      <fo:layout-master-set>
        <fo:simple-page-master margin-right="1in"  margin-left="1in"
                               margin-bottom="1in" margin-top="1in"
                               page-width="8.5in"  page-height="11in"
                               master-name="first">
          <fo:region-body/>
        </fo:simple-page-master>
      </fo:layout-master-set>

      <fo:page-sequence master-reference="first">

        <fo:flow flow-name="xsl-region-body">
          <xsl:apply-templates/>
        </fo:flow>

      </fo:page-sequence>

    </fo:root>
  </xsl:template>

  <xsl:template match="dish|ingredient|directions|story">
    <fo:block><xsl:apply-templates/></fo:block>
  </xsl:template>

</xsl:stylesheet>

Example 13-3 shows the complete XSL-FO document produced by running the cornbread recipe through an XSLT engine such as Xalan or SAXON with this stylesheet. The whitespace is a little off because of the way XSLT treats whitespace in the transform document. However, this won't be significant when the document is rendered.

Example 13-3. An XSL-FO document describing a recipe for cornbread

<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master margin-right="1in" margin-left="1in"
margin-bottom="1in" margin-top="1in" page-width="8.5in" page-height="11in"
master-name="first"><fo:region-body/></fo:simple-page-master>
</fo:layout-master-set><fo:page-sequence master-reference="first">
<fo:flow flow-name="xsl-region-body">
  <fo:block>Southern Corn Bread</fo:block>

    <fo:block>
      1 cup
      flour
    </fo:block>
    <fo:block>
      4 tablespoons
      Royal Baking Powder
    </fo:block>
    <fo:block>
      1/2 teaspoon
      salt
    </fo:block>
    <fo:block>
      1 cup
      corn meal
    </fo:block>
    <fo:block>
      11/2 cups
      whole milk
    </fo:block>
    <fo:block>
      4 tablespoons
      melted butter
    </fo:block>

  <fo:block>
    Sift flour, baking powder, sugar &amp; salt together.
    Add 1 cup corn meal.

      Beat egg in cup and add beaten egg and 11/2 cups whole
       milk to make a batter. Stir well.

      Add melted shortening and beat until light and thoroughly mixed.

      Pour into greased shallow pan or greased muffin rings.

      Bake in hot oven at 425º F for
      25 minutes.

      Cut into squares if cooked in shallow pan.
  </fo:block>
  <fo:block>
    After my mother-in-law Marjorie Anderson died,
    Beth and I found this recipe written on the "extra recipes"
    page in a local cookbook in her cupboard.
    This was published by the The Episcopal Churchwomen,
    Church of Ascension, Mt. Sterling,
    Kentucky.
  </fo:block>

</fo:flow></fo:page-sequence></fo:root>

The final step in this process is to convert the formatting objects document into some other format that can be viewed onscreen or on paper. This requires running a formatting program such as the Apache XML Project's open source FOP (http://xml.apache.org/fop/). FOP is a Java program that runs on most platforms. At the time of this writing, it has some significant holes in its coverage but is making rapid progress. After you install FOP in the class path, this command line transforms the file cornbread.fo into a PDF document:

% java org.apache.fop.apps.Fop -fo cornbread.fo -pdf cornbread.pdf

FOP can also transform XSL-FO documents into plain text, raw PostScript, a PCL file, or SVG slides, or display it on the screen using the Java 2D API. This command produces the window shown in Figure 13-3.

Figure 13-3. The XSL-FO recipe document in FOP's AWT preview

There are several other programs for working with XSL-FO documents:

  • RenderX's XEP (http://www.renderx.com/FO2PDF.html) is a payware Java XSL-FO-to-PDF converter program much like FOP.

  • Sebastian Rahtz's PassiveTEX (http://users.ox.ac.uk/~rahtz/passivetex/) is an open source collection of TEX macros for converting XSL-FO documents to TEX . A reasonably modern TEX distribution is required.

  • The Antenna House XSL Formatter (http://www.antennahouse.com/xslformatter.html) is a payware Windows program that can print and display XSL-FO documents using the Windows GDI .

  • IBM's XSL Formatting Objects Composer (http://www.alphaworks.ibm.com/tech/xfc) is a free-beer Java program that implements a "substantial portion" of XSL Formatting Objects 1.0. It can display XSL-FO documents on the screen or convert them to PDF.

Previous | Home | Next
13.2. The Structure of an XSL-FO Document | Book Index | 13.4. XSL-FO Properties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSL-FO Properties (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

13.4. XSL-FO Properties

The finished document shown in Figure 13-3 is quite Spartan. It simply breaks the original XML document into a few separate paragraphs. After quite a lot of work, it still hasn't reached the polish that was achieved much more simply with CSS back in the last chapter in Example 12-2 and Figure 12-1. Adding the sparkle of different fonts, bold headlines, bulleted lists, and other desirable features requires setting the relevant properties on the individual formatting objects. These are set through optional attributes of the formatting object elements like fo:block. The good news is that most of the property names and semantics are exactly the same as they are for CSS. For example, to make the text in an fo:block element bold, add a font-weight attribute with the value bold, like this:

<fo:block font-weight="bold">Southern Corn Bread</fo:block>

The similarity with the equivalent CSS rule is obvious:

dish { font-weight: bold }

The property name is the same. The property value is the same. The meaning of the property is the same. Similarly, you can use all the font-weight keywords and values like lighter and 100, 200, 300, 400, etc. that you learned for CSS. Only the syntactic details of how the value bold is assigned to the property font-weight and how that property is then attached to the dish element has changed. When XSL-FO and CSS converge, they do so closely.

Many other properties come across from CSS by straight extrapolation. For instance, in Example 12-2 the dish element was formatted with this rule:

dish    {
  display: block;
  font-family: Helvetica, Arial, sans-serif;
  font-size: 20pt;
  font-weight: bold;
  text-align: center
}

In XSL-FO, it will be formatted with this XSLT template:

<xsl:template match="dish">
  <fo:block font-family="Helvetica, Arial, sans-serif" font-size="20pt"
            font-weight="bold" text-align="center">
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

Similarly, the margin properties set the margins on the various elements:

<xsl:template match="directions|story">
  <fo:block margin-top="12pt" margin-left="4pt">
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

In a few cases CSS properties become XSL-FO elements rather than attributes. For instance, to format the ingredients as a bulleted list, we have to use the fo:list-block, fo:list-item, fo:list-item-label, and fo:list-item-body elements. This XSLT template does that:

<xsl:template match="ingredient">
  <fo:list-item>
             <!-- Unicode Bullet Character -->
    <fo:list-item-label>&#x2022;</fo:list-item-label>
    <fo:list-item-body><xsl:apply-templates/></fo:list-item-body>
  </fo:list-item>
</xsl:template>

We now have the pieces needed to put together a more attractive XSL-FO document. Example 13-4 is an XSLT stylesheet that transforms documents like Example 12-1 into XSL-FO documents.

Example 13-4. An XSLT-to-XSL-FO transform

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <xsl:template match="/">
    <fo:root>
      <fo:layout-master-set>
        <fo:simple-page-master margin-right="1in"  margin-left="1in"
                               margin-bottom="1in" margin-top="1in"
                               page-width="8.5in"  page-height="11in"
                               master-name="first">
          <fo:region-body/>
        </fo:simple-page-master>
      </fo:layout-master-set>

      <fo:page-sequence master-reference="first">

        <fo:flow flow-name="xsl-region-body">
          <xsl:apply-templates/>

        </fo:flow>

      </fo:page-sequence>

    </fo:root>
  </xsl:template>

  <xsl:template match="recipe">
    <fo:block font-family="Times, 'Times New Roman', serif"
              font-size="12pt">
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>
  <xsl:template match="dish">
    <fo:block font-family="Helvetica, Arial, sans-serif" font-size="20pt"
              font-weight="bold" text-align="center">
      <xsl:apply-templates/>
    </fo:block>

  </xsl:template>
  <xsl:template match="directions|story">
    <fo:block margin-top="12pt" margin-left="4pt">
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>

  <xsl:template match="ingredients">
    <fo:list-block><xsl:apply-templates/></fo:list-block>
  </xsl:template>

  <xsl:template match="ingredient">
    <fo:list-item>
               <!-- Unicode Bullet Character -->
      <fo:list-item-label>
        <fo:block>&#x2022;</fo:block>
      </fo:list-item-label>
      <fo:list-item-body>
        <fo:block><xsl:apply-templates/></fo:block>
      </fo:list-item-body>
    </fo:list-item>
  </xsl:template>

</xsl:stylesheet>

Example 13-5 shows the XSL-FO document produced by applying the previous transform to the cornbread recipe in Example 12-1. The whitespace has been cleaned up a little by hand, though that won't affect the final rendered result.

Example 13-5. An XSL-FO document describing a recipe for cornbread

<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <fo:layout-master-set>
    <fo:simple-page-master margin-right="1in" margin-left="1in"
      margin-bottom="1in" margin-top="1in" page-width="8.5in"
      page-height="11in" master-name="first">
        <fo:region-body/>
    </fo:simple-page-master>
  </fo:layout-master-set>
  <fo:page-sequence master-reference="first">
    <fo:flow flow-name="xsl-region-body">
      <fo:block font-family="Times, 'Times New Roman', serif"
                 font-size="12pt">
      <fo:block font-family="Helvetica, Arial, sans-serif"
        font-size="20pt" font-weight="bold"
         text-align="center">Southern Corn Bread</fo:block>
  <fo:list-block>
    <fo:list-item><fo:list-item-label><fo:block>·</fo:block>
    </fo:list-item-label><fo:list-item-body><fo:block>
      1 cup
      flour
    </fo:block></fo:list-item-body></fo:list-item>
    <fo:list-item><fo:list-item-label><fo:block>·</fo:block>
    </fo:list-item-label><fo:list-item-body><fo:block>
      4 tablespoons
      Royal Baking Powder
    </fo:block></fo:list-item-body></fo:list-item>
    <fo:list-item><fo:list-item-label><fo:block>·</fo:block>
    </fo:list-item-label><fo:list-item-body><fo:block>
      1/2 teaspoon
      salt
    </fo:block></fo:list-item-body></fo:list-item>
    <fo:list-item><fo:list-item-label><fo:block>·</fo:block>
    </fo:list-item-label><fo:list-item-body><fo:block>
      1 cup
      corn meal
    </fo:block></fo:list-item-body></fo:list-item>
    <fo:list-item><fo:list-item-label><fo:block>·</fo:block>
    </fo:list-item-label><fo:list-item-body><fo:block>
      11/2 cups
      whole milk
    </fo:block></fo:list-item-body></fo:list-item>
    <fo:list-item><fo:list-item-label><fo:block>·</fo:block>
    </fo:list-item-label><fo:list-item-body><fo:block>
      4 tablespoons
      melted butter
    </fo:block></fo:list-item-body></fo:list-item>
  </fo:list-block>

  <fo:block margin-top="12pt" margin-left="4pt">
    Sift flour, baking powder, sugar &amp; salt together.
    Add 1 cup corn meal.

      Beat egg in cup and add beaten egg and 11/2 cups whole
       milk to make a batter. Stir well.

      Add melted shortening and beat until light and thoroughly mixed.

      Pour into greased shallow pan or greased muffin rings.

      Bake in hot oven at 425º F for
      25 minutes.

      Cut into squares if cooked in shallow pan.

  </fo:block>

  <fo:block margin-top="12pt" margin-left="4pt">
    After my mother-in-law Marjorie Anderson died,
    Beth and I found this recipe written on the "extra recipes"
    page in a local cookbook in her cupboard.
    This was published by the The Episcopal Churchwomen,
    Church of Ascension, Mt. Sterling,
    Kentucky.
  </fo:block>

</fo:block></fo:flow></fo:page-sequence></fo:root>

This document can be run through a formatter to produce a PDF file for viewing. Figure 13-4 shows the final result of this process.

Figure 13-4. The recipe document after conversion from XSL-FO to PDF

XSL-FO does add a number of properties that CSS doesn't provide. To name just a few, XSL-FO has properties to control hyphenation, insert leaders, specify the number of columns on a page, and determine where page breaks occur and which paragraphs must be kept together. CSS has none of these. For the most part, XSL-FO's properties are a superset of CSS's properties.

Previous | Home | Next
13.3. Laying Out the Master Pages | Book Index | 13.5. Choosing Between CSS and XSL-FO
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Choosing Between CSS and XSL-FO (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

13.5. Choosing Between CSS and XSL-FO

CSS is a very straightforward, easy-to-learn, easy-to-use language for formatting web pages. To the extent that CSS has gotten a reputation as buggy and difficult to use, that's mostly because of inconsistent, nonstandard browser implementations. Opera 4.0 and later, Netscape 6.0 and later, and Mozilla provide extensive support for most features of CSS Level 2 with only a few minor bugs. Internet Explorer's support is much weaker, but borders on usable.

It's hard to imagine any text-based web site you can't produce by using XSLT to transform a document into HTML and then applying a CSS stylesheet. Alternately, you can transform the XML document into another XML document and apply the CSS stylesheet to that. If the element content in the original XML document is in fact exactly what you want to display in the output document, in the correct order, you can even omit the XSLT transformation step, as we did in Examples Example 12-1 and Example 12-2 in the last chapter.

Perhaps most importantly, CSS is already well understood by web designers and well supported by current browsers. XSL-FO is not directly supported by any browsers. To view an XSL-FO document, you must first convert it into the inconvenient PDF format. PDF does not adjust as well as HTML to the wide variety of monitors and screen sizes in use today. Viewing it inside a web browser requires a special plug-in. The limited third party tools that support XSL-FO are beta quality at best. Personally, we see little reason to use anything other than CSS on the Web.

On the other hand, XSL-FO does go beyond CSS in some respects that are important for high-quality printing. For example, XSL-FO offers multiple column layouts; CSS doesn't. XSL-FO can condition formatting on what's actually in the document; CSS can't. XSL-FO allows you to place footnotes, running headers, and other information in the margins of a page; CSS doesn't. XSL-FO lets you insert page numbers and automatically cross-reference particular pages by number; CSS doesn't. And for printing, the requirement to render into PDF is much less limiting and annoying since the ultimate delivery mechanism is paper anyway. CSS Level 3 will add some of these features, but it will still focus on ease-of-use and web-based presentation rather than high-quality printing. Once the software is more reliable and complete, XSL-FO should be the clear choice for professionally typeset books, magazines, newspapers, and other printed matter that's rendered from XML documents. It should be very competitive with other solutions like Quark XPress, TEX, troff, and FrameMaker. CSS does not even attempt to compete in this area.

The bottom line is this: CSS is right for web pages; XSL-FO is right for printed matter. XSLT is a crucial step in getting an input document ready for eventual presentation with either CSS or XSL-FO.

Previous | Home | Next
13.4. XSL-FO Properties | Book Index | 14. Resource Directory Description Language
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Resource Directory Description Language (RDDL) (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 14. Resource Directory Description Language (RDDL)

Contents:

What's at the End of a Namespace URL?
RDDL Syntax
Natures
Purposes

RDDL, the Resource Directory Description Language, is an XML application invented by Jonathan Borden, Tim Bray, and various other members of the xml-dev mailing list to describe XML applications identified by namespace URLs. A RDDL document lives at the namespace URL for the application it describes. RDDL is a hybrid of XHTML Basic and one custom element, rddl:resource. A rddl:resource element is a simple XLink that points to a resource related to the application the RDDL document describes. Humans with browsers can read the XHTML parts to learn about the application. Software can read the rddl:resource elements.

14.1. What's at the End of a Namespace URL?

The people who wrote the namespaces specification couldn't agree on what should be put at the end of a namespace URL. Should it be a DTD, a schema, a specification document, a stylesheet, software for processing the application, or something else? All of these are possible, but none of them are required for any particular XML application. Some applications have DTDs; some don't. Some applications have schemas; some don't. Some applications have stylesheets; some don't. Thus, for the most part, namespaces have been purely formal identifiers. They do not actually locate or identify anything.

Namespaces in XML specifically states that "The namespace name, to serve its intended purpose, should have the characteristics of uniqueness and persistence. It is not a goal that it be directly usable for retrieval of a schema (if any exists)." That is, it is not required that there be anything in particular, such as a DTD or a schema, at the end of the namespace URL. Indeed, it's not even required that the namespace name be potentially resolvable. It might be an irresolvable URN such as urn:isbn:1565922247. On the other hand, this doesn't say that there can't be anything at the end of a namespace URL, just that there doesn't have to be.

Nonetheless, this hasn't stopped numerous developers from typing namespace URLs into their web browser location bars and filling the error logs at the W3C and elsewhere with 404 Not Found errors. It hasn't stopped weekly questions on the xml-dev mailing list about whether it's possible to parse an XML document on a system that's disconnected from the Net. Eventually, the membership of the xml-dev mailing list reached consensus that it was time to put something at the end of namespace URLs, even if they didn't have to.

However, the question still remained, what to put there? All the reasons for not choosing any one thing to put at the end of a namespace URL still applied. Rick Jelliffe suggested fixing the problem by introducing an additional layer of indirection, and Tim Bray proposed doing it with XHTML and XLinks. Instead of putting just one of these at the end of the namespace URL, an XML document containing a list of all the things related to the XML application identified by that particular URL could be put at the end of the namespace URL.

Experience had proven that when presented with a string beginning with http, developers would type it into a browser location bar.[7] Therefore, the basic syntax of RDDL had to be something that looked reasonable when loaded into a browser: preferably, HTML. Furthermore, to make machine processing simple, it also had to be well-formed, perhaps valid, XML. Naturally, XHTML came to mind, and modular XHTML provided just enough extensibility to permit the extra syntax RDDL needed.

[7]In the immortal words of Claude L. Bullard, "All the handwaving about URN/URI/URL doesn't avoid the simple fact that if one puts http:// anywhere in browser display space, the system colors it blue and puts up a finger. The monkey expects a resource and when it doesn't get one, it shocks the monkey. Monkeys don't read specs to find out why they should be shocked. They turn red and put up a finger."

Previous | Home | Next
13.5. Choosing Between CSS and XSL-FO | Book Index | 14.2. RDDL Syntax
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


RDDL Syntax (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

14.2. RDDL Syntax

A RDDL document is an XHTML Basic document, plus one new element, rddl:resource. XHTML Basic is a subset of XHTML that includes the Structure, Text, Hypertext, List, Basic Forms, Basic Tables, Image, Object, Metainformation, Link, and Base modules. There are no frames or deprecated presentational elements like font and bold. However, this is enough to write pretty much anything you'd reasonably want to write about an XML application.

In addition, a RDDL document contains one new element, resource, which is placed in the http://www.rddl.org/ namespace. This URL is normally mapped to the rddl prefix. The prefix can change as long as the URL remains the same. However, the RDDL DTD declares the resource element with the name rddl:resource, so a RDDL document will only be valid if it uses the prefix rddl.

A rddl:resource element is a simple XLink whose xlink:href attribute points to the related resource and whose xlink:role and xlink:arcrole attributes identify the nature and purpose of that related resource. The rddl:resource element can appear anywhere a p element can appear and contain anything a div element can contain. Web browsers generally ignore the rddl:resource start- and end-tags, but will display their content. Automated software searching for related resources only pays attention to the rddl:resource elements and their attributes, while ignoring all the XHTML.

Recall the person vocabulary used several times in this book. When last seen in Chapter 8, it looked as shown in Example 14-1. All elements in this document are in the default namespace http://www.cafeconleche.org/namespaces/people.

Example 14-1. An XML document describing two people that uses a default namespace

<?xml version="1.0"?>
<people xmlns="http://www.cafeconleche.org/namespaces/people">

  <person born="1912" died="1954">
    <name>
      <first_name>Alan</first_name>
      <last_name>Turing</last_name>
    </name>
    <profession>computer scientist</profession>
    <profession>mathematician</profession>
    <profession>cryptographer</profession>
  </person>

  <person born="1918" died="1988">
    <name>
      <first_name>Richard</first_name>
      <middle_initial>P</middle_initial>
      <last_name>Feynman</last_name>
    </name>
    <profession>physicist</profession>
    <hobby>Playing the bongoes</hobby>
  </person>

</people>

Various chapters have developed stylesheets, DTDs, and (still-to-come) schemas for this application. Example 14-2 is a very simple RDDL document that brings these all together. This document should be placed at the namespace for that application, http://www.cafeconleche.org/namespaces/people. The DOCTYPE declaration loads the RDDL DTD rather than the XHTML Basic DTD, but the difference is only in the addition of the single rddl:resource element. This document is both valid and well-formed. Figure 14-1 shows this document in Mozilla, where it looks like any other HTML document.

Example 14-2. A RDDL document

<!DOCTYPE html PUBLIC "-//XML-DEV//DTD XHTML RDDL 1.0//EN"
                      "http://www.rddl.org/rddl-xhtml.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      xmlns:rddl="http://www.rddl.org/">
<head>
  <title>An XML Application Describing People</title>

</head>
<body>
<h1>An XML Application Describing People</h1>
<rddl:resource xlink:type="simple"
  xlink:href="urn:isbn:1565922247"
  xlink:role="http://dublincore.org/documents/dcmi-type-vocabulary/#text"
  xlink:arcrole="http://www.rddl.org/purposes#normative-reference">
  <p>
    http://www.cafeconleche.org/namespaces/people is the namespace URL
    for an XML application describing people in a data-oriented
    fashion used as an example in <cite>XML in a Nutshell</cite>, second
    edition by Elliotte Rusty Harold and W. Scott Means (O'Reilly &amp;
    Associates, 2002).
  </p>
</rddl:resource>

<h2>Related Resources</h2>
<p>
 Several examples in this book address this application in one way or
 another. These include:
</p>

<ul>
<li>
 <rddl:resource xlink:type="simple"
  xlink:href="http://www.cafeconleche.org/books/xian2/examples/03/3-5.dtd"
  xlink:role="http://www.isi.edu/in-notes/iana/assignments/media-types/application/xml-dtd"
  xlink:arcrole="http://www.rddl.org/purposes#validation">
    <a href="http://www.cafeconleche.org/books/xian2/examples/03/3-5.dtd">
      Example 3-5</a>: A data oriented DTD describing people
   </rddl:resource>
</li>
<li>
<rddl:resource xlink:type="simple"
 xlink:href="http://www.cafeconleche.org/books/xian2/examples/08/8-15.xsl"
 xlink:role="http://www.w3.org/1999/XSL/Transform"
 xlink:arcrole="http://www.isi.edu/in-notes/iana/assignments/media-types/text/html">
  <a href="http://www.cafeconleche.org/books/xian2/examples/08/8-15.xsl">
    Example 8-15</a>: An XSLT stylesheet for people documents
 </rddl:resource>
</li>
</ul>

<p>
  This document itself is
  <a href="http://www.cafeconleche.org/books/xian/examples/14/14-2.html">
   Example 14-2</a> from the RDDL chapter.
</p>

</body>
</html>

Figure 14-1. To a web browser, a RDDL document is just another HTML document

This document contains three rddl:resource elements indicating related resources. Two of them are inside list items, and one of them contains a paragraph at the top of the page. The first one links to the unofficial specification for the people application, this book itself. The xlink:href attribute uses an isbn URI to identify the book. The xlink:role contains a URL from the Dublin Core that indicates that the nature of this resource is text. The xlink:arcrole attribute contains a well-known URL defined in the RDDL specification to indicate that the purpose of this resource is normative reference.

The second rddl:resource element points to the DTD first defined in Chapter 3. Its nature is indicated by a MIME media type URL, and its purpose is validation. The xlink:href attribute links to the actual location of the DTD. However, a typical browser won't recognize this, so the rddl:resource element contains an ordinary HTML a link that the browser will color blue and the user can click on. It's not uncommon to duplicate markup in a RDDL document--one set of tags for the machines and another set of tags for the humans--each of which say pretty much the same thing.

The final rddl:resource element points to the XSLT stylesheet defined in Chapter 8. Here the xlink:role attribute contains the namespace URI for XSLT, indicating that this resource is an XSLT stylesheet. The xlink:arcrole attribute contains the MIME media type URL for HTML, indicating that this stylesheet will transform documents into HTML.

This is a very simple example. A real-world RDDL document would contain a lot more HTML to tell people reading it in a browser just what the application was about. Machines will ignore the HTML and look at the xlink:role and xlink:arcrole attributes to figure out exactly what they can do with each related resource.

Previous | Home | Next
14. Resource Directory Description Language | Book Index | 14.3. Natures
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Natures (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

14.3. Natures

The nature of a related resource says what the resource is. For example, the nature of a web page might be HTML, and the nature of an image might be JPEG. The nature is indicated by a URL. Normally this nature URL is a namespace URL for XML applications and a MIME media type URL for everything else. For instance, the XSLT nature is written as http://www.w3.org/1999/XSL/Transform. The JPEG nature is written as http://www.isi.edu/in-notes/iana/assignments/media-types/image/jpeg.

The RDDL specification specifies two dozen natures that can be used in xlink:role attributes. In addition, you are welcome to define your own, but when possible, you should use the standard natures so that automated software can understand your documents and locate the related resources it needs to locate. These are the standard natures and their URLs:

CSS stylesheet | http://www.isi.edu/in-notes/iana/assignments/media-types/text/css
DTD | http://www.isi.edu/in-notes/iana/assignments/media-types/application/xml-dtd
A mailbox | http://www.rddl.org/natures#mailbox
Generic HTML | http://www.isi.edu/in-notes/iana/assignments/media-types/text/html
HTML 4.0 | http://www.w3.org/TR/html4/
HTML 4 Strict | http://www.w3.org/TR/html4/strict
HTML 4 Transitional | http://www.w3.org/TR/html4/transitional
HTML 4 Frameset | http://www.w3.org/TR/html4/frameset
XHTML | http://www.w3.org/1999/xhtml
XHTML 1.0 Strict | http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict
XHTML 1.0 Transitional | http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional
RDF schema | http://www.w3.org/2000/01/rdf-schema#
RELAX core grammar | http://www.xml.gr.jp/xmlns/relaxCore
RELAX namespace grammar | http://www.xml.gr.jp/xmlns/relaxNamespace
Schematron schema | http://www.ascc.net/xml/schematron
OASIS Open Catalog | http://www.rddl.org/natures#SOCAT
W3C XML Schema Language schema | http://www.w3.org/2000/10/XMLSchema
XML character data | http://www.w3.org/TR/REC-xml.html#dt-chardata
XML escaped text | http://www.w3.org/TR/REC-xml.html#dt-escape
XML unparsed entity | http://www.w3.org/TR/REC-xml.html#dt-unparsed
IETF RFC | http://www.ietf.org/rfc/rfc2026.txt
ISO standard | http://www.iso.ch/
Python software | http://www.rddl.org/natures/software#python
Java software | http://www.rddl.org/natures/software#java

Many other natures can be reasonably derived by following these examples. For instance, a PNG image could be given the nature because http://www.isi.edu/in-notes/iana/assignments/media-types/image/png because PNG documents have the MIME media type image/png. Software written in Ruby could be given the nature http://www.rddl.org/natures/software#ruby. An RDF document can have the nature http://www.w3.org/1999/02/22-rdf-syntax-ns# taken from its namespace, and so forth.

Previous | Home | Next
14.2. RDDL Syntax | Book Index | 14.4. Purposes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Purposes (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

14.4. Purposes

The purpose of a related resource indicates what the resource will be used for. Purposes distinguish between resources with the same natures used for different things. For example, DocBook has multiple XSLT stylesheets for transforming DocBook documents into HTML, XHTML, chunked HTML, and XSL-FO. These are all related resources with the same nature but different purposes. Unlike natures, purposes are optional. You don't have to use them if you don't need to distinguish between resources with the same natures, but you can if you'd like.

Purpose names are URLs. These URLs are placed in xlink:arcrole attributes of a rddl:resource element. The RDDL specification defines almost 20 different well-known purpose URLs, mostly in the form http://www.rddl.org/purposes#purpose. In addition, you are welcome to define your own; but you should use the standard URLs for the standard purposes so that automated software can understand your documents and locate the related resources it needs to locate. These are the well-known purposes:

Validation | http://www.rddl.org/purposes#validation
Schema-validation | http://www.rddl.org/purposes#schema-validation
DTD module | http://www.rddl.org/purposes#module
Schema module | http://www.rddl.org/purposes#schema-module
DTD notations module | http://www.rddl.org/purposes#notations
Software module | http://www.rddl.org/purposes#software-module
Software package | http://www.rddl.org/purposes#software-package
Software project | http://www.rddl.org/purposes#software-project
JAR | http://www.rddl.org/purposes#JAR
XSLT extension | http://www.rddl.org/purposes/software#xslt-extension
Reference | http://www.rddl.org/purposes#reference
Normative reference | http://www.rddl.org/purposes#normative-reference
Non-normative reference | http://www.rddl.org/purposes#non-normative-reference
Prior-version | http://www.rddl.org/purposes#prior-version
Definition | http://www.rddl.org/purposes#definition
Icon | http://www.rddl.org/purposes#icon
Alternate | http://www.rddl.org/purposes#alternate
RDDL Directory | http://www.rddl.org/purposes#directory

Furthermore, the purpose of an XSLT transform is often the URI for the nature of the resource that is produced by the transform. For instance, the purpose of a stylesheet that converted documents into strict XHTML would probably be http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.

Previous | Home | Next
14.3. Natures | Book Index | III. Data-Centric XML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Data-Centric XML (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Part III. Data-Centric XML

Previous | Home | Next
14.4. Purposes | Book Index | 15. XML as a Data Format
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML as a Data Format (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 15. XML as a Data Format

Contents:

Why Use XML for Data?
Developing Data-Oriented XML Formats
Sharing Your XML format

Despite its document roots, the most common applications of XML today involve the storage and transmission of information for use by different software applications and systems. New technologies and frameworks (such as Web Services) depend heavily on XML content to communicate and negotiate between dissimilar applications.

The appropriate techniques used to design, build, and maintain a data-centric XML application vary greatly, depending on the required functionality and intended audience. This chapter discusses the different concerns, techniques, and technologies that should be considered when designing a new data-centric XML application.

15.1. Why Use XML for Data?

Before XML, individual programmers had to determine how data would be formatted whenever they needed to store or transmit program data. In most cases, the data was never intended for use outside the original program, so programmers would store it in the most convenient format they could devise. A few de facto file formats evolved over the years (RTF, CSV, and the ubiquitous Windows .ini file format), but the data written by one program could usually be read only by that same program. In fact, it was often possible for only that specific version of the same program to read the data.

The rapid proliferation of XML and free XML tools throughout the programming community has given developers an obvious choice when the time comes to select a data-storage or transmission format for their application. For all but the most trivial applications, the benefits of using XML to store and retrieve data far outweigh the additional overhead of including an XML parser in your application. The unique strengths of using XML as a software data format include:

Simple syntax
Easy to generate and parse.

Support for nesting
Tags easily allow programs to represent structures with nested elements.

Easy to debug
Human-readable data format is easy to explore and create with a basic text editor.

Language and platform independent
XML and Unicode guarantee that your datafile will be portable across virtually every popular computer architecture and language combination in use today.

Building on these basic strengths, XML can make possible new types of applications that would have been previously impossible (or very costly) to implement.

TIP: There are a few technologies that seek to achieve similar cross-program compatibility but use binary formats. Abstract Syntax Notation One (ASN.1) is probably the most prominent of these. ISO and ITU-T are developing standards for working with XML and ASN.1 in various combinations; more information on these developments is available from http://asn1.elibel.tm.fr/en/xml/.

15.1.1. Mixed Environments

Modern enterprise applications often involve software running on different computer systems under various operating systems. Choosing a communication protocol involves finding the lowest common denominator available on each system. With the large number of XML parsers that can be freely integrated with your application, XML is becoming a popular format for enterprise data sharing.

Imagine a typical enterprise application that needs to display data from a mainframe to users connected to a corporate web site. In this case, XML acts as the "glue" to connect a web server with a legacy application on a mainframe. The simple XML interface application accepts requests from the web server, calls the legacy application, and converts the result to XML. Using a technology like XSLT, the web server can then transform the XML into a number of acceptable web formats. By adopting XML as the common language of your enterprise, it becomes easier to reuse existing data in new ways.

Even on smaller systems, XML can be useful for sharing information between applications written in different languages or running in different environments. If a Perl program and a Java program need to communicate, generating and processing XML can be simpler than the alternatives. The XML can also serve as a record to their communications or provide a gateway to other systems that need to join the conversation.

15.1.2. Communications

Building flexible communications protocols that link disparate systems has always been a difficult area in computing. With the proliferation of computer networking and the Internet, building distributed systems has become even more important.

While XML itself is only a data format, not a protocol, XML's flexibility and cross-platform usability has inspired some new developments on the protocol front. XML messaging started even before the XML specification was finished, and various forms of XML messaging have continued to evolve.

One of the earliest approaches, and still a common one, was transmitting XML over HTTP POST requests. The sender would assemble an XML document and send it much like HTML form data, and the recipient would process the XML and send back a response, also in XML. Some developers create custom vocabularies for these transactions, while others have moved to standardized vocabularies such as XML-RPC and SOAP.

XML-RPC is a very simple protocol, which uses XML messages traveling on HTTP to represent client-server remote procedure calls (RPC). The XML messages identify methods, parameters, and the results of calling the methods. The XML documents use simple but effective set of data types (including arrays and structs) to pass information between computers. For more information on XML-RPC, see http://www.xmlrpc.com/.

SOAP offers much more flexibility than XML-RPC, but is much more complex as well. SOAP (formerly the Simple Object Access Protocol, but now an acronym without meaning) uses XML to encapsulate information being sent between programs. SOAP is no longer bound to an HTTP transport, but HTTP is commonly used. It offers both an RPC approach and a document-oriented approach and uses XML Schema data types (with some of its own extensions for things like arrays) to identify type information. SOAP is often grouped with Web Services Description Language (WSDL) and Universal Description, Discovery, and Integration (UDDI) in discussions of "Web Services." For information on SOAP and Web Services, see http://www.w3.org/2002/ws/.

TIP: Some developers are promoting the use of HTTP-based alternatives to SOAP and XML-RPC, under the banner of Representational State Transfer (REST). For more information on this architectural approach and the perspective it offers, see http://internet.conveyor.com/RESTwiki/moin.cgi.

The Blocks Extensible Exchange Protocol (BEEP) takes a very different approach from SOAP and XML-RPC. Rather than building documents that travel over existing protocols, BEEP uses XML to build protocols on TCP sockets. BEEP supports HTTP-style message-and-reply, as well as more complex synchronous and asychronous modes of communication. SOAP messages can be transmitted over BEEP, and so can a wide variety of other XML and binary information. More information on BEEP is available at http://www.beepcore.org.

15.1.3. Object Serialization

Like the issue of communications, the question of where and how to store the state of persistent objects has been answered in various ways over the years. With the popular adoption of object-oriented languages, such as C++ and Java, the language and runtime environment frequently handle object-serialization mechanics. Unfortunately, most of these technologies predate XML.

Most existing serialization methods are highly language- and architecture-specific. The serialized object is most often stored in a binary format that is not human readable. These files break easily if corrupted, and maintaining compatibility as the object's structure changes frequently requires custom work on the part of the programmer.

The features that make XML popular as a communications protocol also make it popular as a format for serializing object contents. Viewing the object's contents, making manual modifications, and even repairing damaged files is easy. XML's flexible nature allows the file format to expand ad infinitum while maintaining backward compatibility with older file versions. XML's labeled hierarchies are a clean fit for nested object structures, and conversions from objects to XML and back can be reasonably transparent. (Mapping arbitrary XML to object structures is a much harder problem.)

A number of tools serialize objects written in various environments as XML documents and can recreate the objects from the XML. Java 1.4, for example, adds an "API for Long-Term Persistence" to its java.beans package, giving developers an alternative to its existing (and still supported) opaque binary serialization format. The XML vocabulary looks a lot like Java and is clearly designed for use within a Java framework, though other environments may import and export the serialization. For more information on this API and the XML it produces, see http://java.sun.com/j2se/1.4/docs/guide/beans/changes14.html#ltp.Microsoft's .NET framework includes similar capabilities but uses an XML Schema-based approach.

15.1.4. Data Storage/Retrieval

The line between an XML file and a database can be blurred. Though XML documents are too verbose and searching is too inefficient for high-performance large-scale database applications, they may be used as a simple, self-contained data store for small sets of information.

XML can play a role in the communications between databases and other software, providing usable chunks of information in a form more easily reused than a typical query response. On the client side, XML data files can be used to offload some nontransactional data-search and -retrieval applications from busy web servers down to the desktop web browser. On the server side, XML can be used as an alternate delivery mechanism for query results.

XML is also finding use as a supplement to information stored in relational databases, and more and more relational databases include native support for XML both as a data-retrieval format and a data type. Native XML databases, which store XML documents and provide querying and retrieval tools, are also becoming more widely available. For more information on the wide variety of XML and data-management tools available, see http://www.rpbourret.com/xml/XMLDatabaseProds.htm.

Previous | Home | Next
III. Data-Centric XML | Book Index | 15.2. Developing Data-Oriented XML Formats
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Developing Data-Oriented XML Formats (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

15.2. Developing Data-Oriented XML Formats

Despite the mature status of most of XML's core technologies, XML application development is only now being recognized as a distinct discipline. Many architects and XML developers are attempting to turn existing design methodologies (like UML) and design patterns to the problem of constructing markup languages, but a widely accepted design process for creating XML applications still does not exist.

TIP: The term "XML application" is often used in XML contexts to describe an XML vocabulary for a particular domain rather than the software used to process it. This may seem a little strange to developers used to creating software applications, but it makes sense if you think about integrating a software application with an XML application, for instance.

XML applications can range in scope from a proprietary vocabulary used to store a single computer program's configuration settings to an industry-wide standard for storing consumer loan applications. Although the specifics and sometimes the sequence will vary, the basic steps involved in creating a new XML application are as follows:

  • Determine the requirements of the application.

  • Look for existing applications that might meet those requirements.

  • Choose a validation model.

  • Decide on a namespace structure.

  • Plan for expansion.

  • Consider the impact of the design on application developers.

  • Determine how old and new versions of the application will coexist.

The following sections explore each of these steps in greater depth.

15.2.1. Basic Application Requirements

The first step in designing a new XML application is like the first step in many design methodologies. Before the application can be designed, it is important to determine exactly what needs the application will fulfill. Some basic questions must be answered before proceeding.

15.2.1.1. Where and how will new documents be created?

Documents that will be created automatically by a software application or database server will be structured differently than those that need to be created by humans using an XML editor. While software wouldn't have a problem generating 100 elements with intricate attributes and cross-references, a human being probably would.

If you already have an application or a legacy format to which you're adding XML, you may already have data structures you need to map to the XML. Depending on the other requirements for the application, you may be able to base your XML format on the existing structures. If you're starting from scratch or need to share the information with other programs that don't share those structures, you probably need to look at the data itself and build the application creating the XML around the information.

15.2.1.2. How complex will the document be?

Obviously the complexity of the data that will be modeled by the XML document has some impact on how the application will be designed. A document containing a few, simple element types is much easier to describe than one that contains dozens of different elements with complex data type requirements. The complexity of an application will affect what type of validation should be used and how documents will be created and processed.

15.2.1.3. How will documents be consumed?

If the XML documents using this vocabulary will only pass between similar programs, it may make sense to model the XML documents directly on the internal structures of the programs without much concern for how easy or difficult that makes using the document for other programs or for humans. If there's a substantial chance that this information needs to be reused by other applications, read by humans (for debugging purposes or for direct access to information), or will be stored for unknown future use, it probably makes sense to ensure that the document is easy to read and process even if that makes creating the document a slightly more difficult task.

15.2.1.4. How widely will the resulting documents be distributed?

Generally, the audience of a new XML application is known in advance. Some documents are created and read by the same application without ever leaving a single system. Other documents will be used to transmit important business information between the IT systems of different organizations. Some documents are created for publication on the Web to be viewed by hundreds or even thousands of people around the world. XML formats that will be shared widely typically need comprehensive documentation made readily available to potential users. Formal validation models may also be more important for documents that are shared outside of a small community of trusted participants.

15.2.1.5. Will others need to incorporate this document structure into their own applications?

Some XML applications are never intended for use and are only useful when incorporated into other XML applications. Others are useful standards on their own but are also suitable for inclusion in other applications. A few different methods that might be used to incorporate markup from one application into another:

Simple inclusion
Markup from one application is included within a container element of another application. Embedding XHTML content in another document is a common example of this.

Mixed element inclusion
Markup from one application is mixed inline with content from another application. This can complicate validation and makes the including application sensitive to changes in the included application. The Global Document Annotation (GDA) Initiative application provides an example of this type of application (http://www.oasis-open.org/cover/gda.html).

Mixed attribute inclusion
Some XML applications are comprised of attributes that may be attached to elements from the host application. XML Linking (XLink) is a prime example of this type of application, defining only attributes that may be used in other vocabularies.

Answering these questions will provide a basic set of requirements to keep in mind when deciding whether to build a new application, acquire an existing application, or some combination of the two.

15.2.2. Investigating Available Options

Before committing to designing and implementing a new XML application, it is a good idea to take a few minutes to search the Internet for prior art. Since the first version of the XML recommendation was released in 1998, thousands of new XML applications have been developed and released around the world. Although the quality and completeness of these applications vary greatly, it is often more efficient to start with an existing DTD or schema (however imperfect) rather than starting from scratch. In some cases supporting software is already available, potentially saving software development work as well.

15.2.2.1. XML vocabulary development

It is also possible that the work your application needs to do may fit into an existing generic framework, such as XML-RPC or SOAP. If this is the case, you may or may not need to create your own XML vocabulary. XML-RPC only uses its own vocabulary, while different styles of SOAP may reduce the amount of work your vocabulary needs to perform.

There are several XML application registries available on the Internet, and a good "metadirectory" of DTD and schema directories can be found on O'Reilly's XML site, http://www.xml.com. These repositories list applications for various disciplines and topics with varying licensing requirements. The XML Cover Pages, at http://xml.coverpages.org, also provide information about a wide variety of XML-related vocabularies, software, and projects. The search for existing applications may also find potential collaborators, potentially helpful if the XML format is intended for use across multiple organizations.

15.2.3. Planning for Growth

Some applications may not need to evolve over time (a vocabulary describing basic DNA strands, for instance), but some thought should be given as to how users of the application would be able to extend it to meet their own needs. In DTD-based applications, this is done by providing parameter entity "hooks" into the document type definition, which could either be referenced or redefined by an instance document. Take the simple DTD shown in Example 15-1.

Example 15-1. extensible.dtd

<!ENTITY % varContent "(EMPTY)">
<!ELEMENT variable %varContent;>

This fragment is not a very interesting application by itself, but since it provides the capability for extension, the document author can make it more useful by providing an alternative entity declaration for the content of the variable element, as shown in Example 15-2.

Example 15-2. Document extending extensible.dtd

<?xml version="1.0"?>
<!DOCTYPE variable SYSTEM "extensible.dtd"
[
<!ENTITY % varContent "(#PCDATA)">
]>
<variable>Useful content.</variable>

The XML schema language provides more comprehensive and controlled support for extending markup using the extension, include, redefine, and import elements. These two mechanisms can be used in conjunction to create very powerful, customizable application frameworks.

15.2.4. Choosing a Validation Method

The first major implementation decision of designing a new XML application is what type of validation (if any) will be performed on instance documents. In many cases, prototyping a set of instance documents is the best way to determine what level of validation must be performed.

If your application is simply saving some internal program state between invocations (such as window positions or menu configurations within a GUI application), going to the trouble of building a schema and validating documents may not be necessary. Since these configuration documents will always be written and read by the same program, the structure is fixed by the program logic itself. The only conceivable purpose for validating a document like this would be to detect file corruption, which would be likely to generate a well-formedness error in any case.

An example of an application that would require some level of validation is where XML documents are exchanged between different related systems that are not maintained by the same development organization. In this case, a DTD or schema can serve as a definitive blueprint to ensure that all systems are sending and receiving information in the expected formats.

The most rigorous type of validation is required when developing a new XML standard that will be implemented independently by many different vendors without any explicit control or restrictions. For example, the XHTML 1.1 standard is enforced by a very explict and well-documented DTD that is hosted by the W3C. This well-known public DTD allows tool and application vendors to ensure that their systems will interoperate as long as instance documents conform to the standard.

After determining the level of validation for a particular application, it must be decided what validation language will be used. The DTD mechanism of XML 1.0 is still the most widely supported standard, although it lacks the expressive power that is required by sophisticated data-oriented applications. The W3C XML schema recommendation provides very rich type and content model expression, but brings with it a commensurate level of complexity.

Developers can also provide both DTDs and XML schemas, or even combine them with other vocabularies for describing XML structures, notably RELAX NG (http://www.oasis-open.org/committees/relax-ng/ ) and Schematron (http://www.ascc.net/xml/resource/schematron/schematron.html).RDDL, described in Chapter 14, provides a set of tools for supporting and explaining such combinations for formats that use namespaces.

15.2.5. Namespace Support

Virtually every XML application that will be shared with the public should include at least a basic level of namespace support. Even if there are no current plans to release a particular document application to the outside world, it is much simpler to implement namespaces from the ground up than it is to retrofit an existing application with a namespace.

Namespaces affect everything from how the document is validated to how it is transformed (using a stylesheet language such as XSLT). Here are a few namespace issues to consider before selecting a URI and starting work.

15.2.5.1. Will instance documents need to be validated using a DTD?

If so, some planning of how namespace prefixes will be assigned and incorporated into the DTD is necessary. DTDs are not namespace aware, so strategic use of parameter entities can make modification of prefixes much simpler down the road.

15.2.5.2. Will markup from this application need to be embedded in other applications?

If so, some thought needs to be given to potential tag-name collisions. The safest approach is to force every element from your application to be explicitly qualified with a namespace. This can be done within an XML schema by setting the elementFormDefault and attributeFormDefault attributes of the schema element to qualified.

15.2.5.3. Are there legacy documents to support?

If an application will be used to validate existing XML documents, some thought should be given to the effort involved in migrating them. In most cases, simply adding a default namespace declaration will be sufficient. If the new application includes markup from different namespaces, however, some thought must be given to how to update old documents.

15.2.6. Maintaining Compatibility

Maintaining backward compatibility with existing documents is a primary concern for XML applications that are widely used by diverse audiences. The difficulties faced by standards organizations when dealing with the task of updating a popular application (such as HTML) are formidable. While most applications may not become as widespread as HTML, some thought should be given in advance as to how new versions of a schema or DTD will interact with existing documents.

One possible approach to maintaining backward compatibility is to create a new, distinct namespace that will be used to mark new element declarations or perhaps to change the namespace of the entire document to reflect a substantially changed version. Another possible strategy is only to extend existing applications without removing prior functionality. The most important thing is to ensure that each instance document for an application has some readily identifiable marker that associates it with a particular version of a DTD or schema. The good news is that the highly transformable nature of XML makes it very easy to migrate old documents to new document formats.

Removing functionality is possible, but frequently difficult, once a format is widely used. Deprecating functionality--marking it as a likely target for removal a version or several before it is actually removed--is one approach. While deprecated features often linger in implementations long after they've been targeted for removals, they change the expectations of developers building new applications and make it possible, if slow, to remove functionality.

Previous | Home | Next
15. XML as a Data Format | Book Index | 15.3. Sharing Your XML format
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Sharing Your XML format (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

15.3. Sharing Your XML format

Creating a data format is often only the first step in making it useful. If an XML vocabulary is only used for a particular process inside a software application, there may not be much reason to publish information about how it works, except for future developers who may work on that application. If, on the other hand, the data format is intended for widespread use by people or organizations who may not normally interact with each other beyond the exchange of messages, then it probably makes sense to provide much more support for the format.

There are a variety of different kinds of information about a data format that are frequently worth sharing:

  • Human-readable documentation, perhaps even in a variety of languages

  • Schemas and DTDs formally defining the structures and content

  • Stylesheets and transformations for presenting the data or converting it from one format to another

  • Code for processing the data, perhaps even in a variety of languages or environments

The first two approaches--human-readable documentation and schemas--are typically the foundations. Formal definitions and rough understandings of what goes where often work for formats that are used by individual programmers or small groups, but sharing formats widely often requires further explanation. Stylesheets and code are additional options that may simplify adoption for developers.

The appropriate level of publicity for an XML vocabulary can vary widely, from no publicity at all to publishing a RDDL document or a support site, to registering the format in one of the XML application registries, or to creating a working group at some kind of standards body or consortium.

Previous | Home | Next
15.2. Developing Data-Oriented XML Formats | Book Index | 16. XML Schemas
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Schemas (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 16. XML Schemas

Contents:

Overview
Schema Basics
Working with Namespaces
Complex Types
Empty Elements
Simple Content
Mixed Content
Allowing Any Content
Controlling Type Derivation

Although Document Type Definitions can enforce basic structural rules on documents, many applications need a more powerful and expressive validation method. The W3C developed the XML Schema Recommendation, released on May 2, 2001 after a long incubation period, to address these needs. Schemas can describe complex restrictions on elements and attributes. Multiple schemas can be combined to validate documents that use multiple XML vocabularies. This chapter provides a rapid introduction to key W3C XML Schema concepts and usage.

This chapter progressively introduces the structures and concepts of XML Schemas, beginning with the fundamental structure that is common to all schemas. The chapter begins with a very simple schema and proceeds to add more functionality to it until ever major feature of XML Schemas has been introduced.

16.1. Overview

A schema is a formal description of what comprises a valid document. An XML schema is an XML document containing a formal description of what comprises a valid XML document. A W3C XML Schema Language schema is an XML schema written in the particular syntax recommended by the W3C.

TIP: In this chapter when we use the word schema without further qualification, we are referring specifically to a schema written in the W3C XML schema language. However, there are numerous other XML schema languages, including RELAX NG and Schematron, each with their own strengths and weaknesses.

An XML document described by a schema is called an instance document. If a document satisfies all the constraints specified by the schema, it is considered to be schema-valid. The schema document is associated with an instance document through one of the following methods:

  • An xsi:schemaLocation attribute on an element contains a list of namespaces used within that element and the URLs of the schemas with which to validate elements in those namespaces.

  • An xsi:noNamespaceSchemaLocation attribute contains a URL for the schema used to validate elements that are not in any namespace.

  • The validating parser may attempt to locate the schema using the namespace of the element itself in one of these ways: directly by looking for a schema at that namespace, indirectly by looking for a RDDL document at that namespace, or implicitly by knowing in advance which schema is right for that namespace.

  • A validating parser may be instructed to validate a given document against an explicitly provided schema, ignoring any hints that might be provided within the document itself.

16.1.1. Schemas Versus DTDs

DTDs provide the capability to do basic validation of the following items in XML documents:

  • Element nesting

  • Element occurrence constraints

  • Permitted attributes

  • Attribute types and default values

However, DTDs do not provide fine control over the format and data types of element and attribute values. Other than the various special attribute types (ID, IDREF, ENTITY, NMTOKEN, and so forth), once an element or attribute has been declared to contain character data, no limits may be placed on the length, type, or format of that content. For narrative documents (such as web pages, book chapters, newsletters, etc.), this level of control is probably good enough.

But as XML makes inroads into more data-intensive applications (such as web services using SOAP), more precise control over the text content of elements and attributes becomes important. The W3C XML Schema standard includes the following features:

  • Simple and complex data types

  • Type derivation and inheritance

  • Element occurrence constraints

  • Namespace-aware element and attribute declarations

The most important of these features is the addition of simple data types for parsed character data and attribute values. Unlike DTDs, schemas can enforce specific rules about the contents of elements and attributes. In addition to a wide range of built-in simple types (such as string, integer, decimal, and dateTime), the schema language provides a framework for declaring new data types, deriving new types from old types, and reusing types from other schemas.

Besides simple data types, schemas add the ability to place more explicit restrictions on the number and sequence of child elements that can appear in a given location. This is even true when elements are mixed with character data, unlike the mixed content model (#PCDATA) supported by DTDs.

WARNING: There are a few things that DTDs do that XML Schema can't do. Defining general entities for use in documents is one of these. XML Inclusions (XInclude) may be able to replace some uses of general entities, but DTDs remain extremely convenient for short entities.

16.1.2. Namespace Issues

As XML documents are exchanged between different people and organizations around the world, proper use of namespaces becomes critical to prevent misunderstandings. Depending on what type of document is being viewed, a simple element like <fullName>Zoe</fullName> could have widely different meanings. It could be a person's name, a pet's name, or the name of a ship that recently docked. By associating every element with a namespace URI, it is possible to distinguish between two elements with the same local name.

Because the Namespaces in XML recommendation was released after the XML 1.0 recommendation, DTDs do not provide explicit support for declaring namespace-aware XML applications. Unlike DTDs (where element and attribute declarations must include a namespace prefix), schemas validate against the combination of the namespace URI and local name rather than the prefixed name.

Namespaces are also used within instance documents to include directives to the schema processor. For example, the special attributes that are used to associate an element with a schema (schemaLocation and noNamespaceSchemaLocation) must be associated with the official XML Schema instance namespace URI (http://www.w3.org/2001/XMLSchema-instance) in order for the schema processor to recognize it as an instruction to itself.

Previous | Home | Next
15.3. Sharing Your XML format | Book Index | 16.2. Schema Basics
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schema Basics (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.2. Schema Basics

This section will construct, step by step, a simple schema document representing a typical address book entry, introducing different features of the XML Schema language as needed. Example 16-1 shows a very simple well-formed XML document.

Example 16-1. addressdoc.xml

<?xml version="1.0"?>
<fullName>Scott Means</fullName>

Assuming that the fullName element can only contain a simple string value, the schema for this document would look like Example 16-2.

Example 16-2. address-schema.xsd

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xs:element name="fullName" type="xs:string"/>
</xs:schema>

It is also common to associate the sample instance document explicitly with the schema document. Since the fullName element is not in any namespace, the xsi:noNamespaceSchemaLocation attribute is used as shown in Example 16-3.

Example 16-3. addressdoc.xml with schema reference

<?xml version="1.0"?>
<fullName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="address-schema.xsd">Scott Means</fullName>

Validating the simple document against its schema requires a validating XML parser that supports schemas such as the open source Xerces parser from the Apache XML Project (http://xml.apache.org/xerces-j/ ). This is written in Java and includes a command-line program called dom.DOMWriter that can be used to validate addressdoc.xml like this:

% java dom.DOMWriter -V -S addressdoc.xml

Since the document is valid, DOMWriter will simply echo the input document to standard output. An invalid document will cause the parser to generate an error message. For instance, adding b elements to the contents of the fullName element violates the schema rules:

<?xml version="1.0"?>
<fullName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="address-schema.xsd"
  >Scott <b>Means</b></fullName>

If this document were validated with DOMWriter, the following validity errors would be detected by Xerces:

[Error] addressdoc.xml:4:13: Element type "b" must be declared.
[Error] addressdoc.xml:4:31: Datatype error: In element 'fullName' : Can not
have element children within a simple type content.

16.2.1. Document Organization

Now that there is a basic schema and a valid document from which to work, it is time to examine the structure of a schema document and its contents. Every schema document consists of a single root xs:schema element. This element contains declarations for all elements and attributes that may appear in a valid instance document.

TIP: The XML elements that make up an XML schema must belong to the XML Schema namespace (http://www.w3.org/2001/XMLSchema), which is frequently associated with the xs: prefix. For the remainder of this chapter, all schema elements will be written using the xs: prefix to indicate that they belong to the Schema namespace.

Instance elements declared by top-level elements in the schema (immediate child elements of the xs:schema element) are considered global elements. For example, the simple schema in Example 16-2 globally declares one element: fullName. According to the rules of schema construction, any element that is declared globally may appear as the root element of an instance document.

In this case, since only one element has been declared, that shouldn't be a problem. But when building more complex schemas, this side effect must be taken into consideration. If more than one element is declared globally, a schema-valid document may not contain the root element you expect.

Naming conflicts are another potential problem with multiple global declarations. When writing schema declarations, it is an error to declare two things of the same type at the same scope. For instance, trying to declare two global elements called fullName would generate an error. But declaring an element and an attribute with the same name would not create a conflict, because the two names are not used in the same way.

16.2.2. Annotations

Now that there is a working schema, it's good practice to include some documentary material about who authored it, what it was for, any copyright restrictions, etc. Since an XML schema document is an XML document in its own right, one simple option would be to use XML comments to include documentary information.

The major drawback to using XML comments is that parsers are not obliged to keep comments intact when parsing XML documents, and applications have to do a lot of work to negotiate their internal structures. This increases the likelihood that, at some point, important documentation will be lost during an otherwise harmless transformation or editing procedure. Encoding documentation as markup inline with the element and type declarations they refer to opens up endless possibilities for automatic documentation generation.

To accommodate this extra information, most schema elements may contain an optional xs:annotation element as their first child element. The annotation element may then, in turn, contain any combination of xs:documentation and xs:appinfo elements, which are provided to contain extra human-readable and machine-readable information, respectively.

16.2.2.1. The xs:documentation element

As a concrete example, let's add some authorship and copyright information to the simple schema document, as shown in Example 16-4.

Example 16-4. address-schema.xsd with annotation

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

 <xs:annotation>
  <xs:documentation xml:lang="en-us">
    Simple schema example from O'Reilly's
    <a href="http://www.oreilly.com/catalog/xmlnut">XML in a Nutshell.</a>
    Copyright 2002 O'Reilly &amp; Associates
  </xs:documentation>
 </xs:annotation>

 <xs:element name="fullName" type="xs:string"/>

</xs:schema>

The xs:documentation element permits an xml:lang attribute to identify the language of the brief message. This attribute can also be applied to the xs:schema element to set the default language for the entire document. For more information about using the xml:lang attribute, see Chapter 5 and Chapter 20.

Also, notice that the documentation element contains additional markup: an a element (à la HTML). The xs:documentation element is allowed to contain any well-formed XML, not just schema elements. The Section 16.8 later in this chapter explains how this can be done in your own documents.

16.2.2.2. The xs:appinfo element

In reality, there is little difference between the xs:documentation element and the xs:appinfo element. Either one can contain any combination of character data or markup the schema author wants to include. But the developers of the schema specification intended the xs:documentation element to contain human-readable content, while the xs:appinfo element would contain application-specific extension information related to a particular schema element.

For example, let's say that it is necessary to encode context-sensitive help text with each of the elements declared in a schema. This text might be used to generate tool-tips in a GUI or system prompts in a voicemail system. Either way, it would be very convenient to associate this information directly with the particular element in question using the xs:appinfo element, like this:

. . .
<xs:element name="fullName" type="xs:string">
  <xs:annotation>
    <xs:appinfo>
      <help-text>Enter the person's full name.</help-text>
    </xs:appinfo>
  </xs:annotation>
 </xs:element>
. . .

Although schemas allow very sophisticated and powerful rules to be expressed, they cannot possibly encompass every conceivable need that a schema developer might face. That is why it is important to remember that there is a facility that can be used to include your own application-specific information directly within the actual schema declarations.

TIP: Schematron is especially well-suited to use in annotations and is capable of checking a wide variety of conditions well beyond the bounds of XML Schema. For more information about Schematron, see http://www.ascc.net/xml/resource/schematron/schematron.html.

16.2.3. Element Declarations

XML documents are composed primarily of nested elements, and the xs:element element is one of the most often-used declarations in a typical schema. The simple example schema already includes a single global element declaration that tells the schema processor that instance documents must consist of a single element called fullName:

<xs:element name="fullName" type="xs:string">

This declaration uses two attributes to describe the element that can appear in the instance document: name and type. The name attribute is self-explanatory, but the type attribute requires some additional explanation.

16.2.4. Simple Types

Schemas support two different types of content: simple and complex. Simple content equates with basic data types that are found in most modern programming languages (strings, integers, dates, times, etc.). Simple types cannot, by definition, contain nested element content.

In the previous example, the type="xs:string" attribute/value pair tells the schema processor that this element can only contain simple content of the built-in type xs:string. Table 16-1 lists a representative sample of the built-in simple types that are defined by the schema specification. See Chapter 21 for a complete listing.

Table 16-1. Built-in simple schema types

Type

Description

anyURI

A Uniform Resource Identifier

base64Binary

Base64 content-encoded binary data

boolean

May contain either true or false, 0 or 1

byte

A signed byte quantity >= -128 and <= 127

dateTime

An absolute date and time value combination

duration

A relative amount of time, expressed in units of years, months, days, hours, etc

ID, IDREF, IDREFS,
ENTITY, ENTITIES, NOTATION,
NMTOKEN, NMTOKENS

Same values as defined in the attribute declaration section of the XML 1.0 recommendation

integer

Any positive or negative counting number

language

May contain same values as xml:lang attribute from XML 1.0 recommendation

Name

An XML name

normalizedString

String with newline, tab, and carriage-return characters normalized to spaces

string

Unicode string

token

Same as normalizedString with multiple spaces collapsed and leading and trailing spaces removed

Since attribute values cannot contain elements, attributes must always be declared with simple types. Also, an element that is declared to have a simple type cannot have any attributes. This means that if an attribute must be added to the fullName element, some fairly significant changes to the element declaration are required.

16.2.5. Attribute Declarations

To make the fullName element more informative, it would be nice to add a language attribute to provide a hint as to how it should be pronounced. Although adding an attribute to an element sounds like a fairly simple task, it is complicated by the fact that elements with simple types (like xs:string) cannot have attribute values.

Attributes are declared using the xs:attribute element. Attributes may be declared globally by top-level xs:attribute elements (which may be referenced from anywhere within the schema) or locally as part of a complex type definition that is associated with a particular element.

To incorporate a language attribute into the fullName element declaration, a new complex type based on the built-in xs:string type must be created. To do this, three new schema elements must be used: xs:complexType, xs:simpleContent, and xs:extension:

<xs:element name="fullName">

  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="language" type="xs:language"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

</xs:element>

This declaration no longer has a type attribute. Instead it has an xs:complexType child element. This element tells the schema processor that the fullName element may have attributes, but the xs:simpleContent element tells the processor that the content of the element is a simple type. To specify what type of simple content, it uses the base attribute of the xs:extension element to derive a new type from the built-in xs:string type. The xs:attribute element within the xs:extension element indicates that this derived type may have an attribute called language that contains values conforming to the built-in simple type xs:language (mentioned in Table 16-1). Type derivation is an important part of schema creation and will be covered in more detail later in this chapter.

16.2.5.1. Attribute groups

In DTDs, parameter entities are used to encapsulate repeated groups of attribute declarations that are shared between different element types. Schemas provide the same functionality in a more formal fashion using the xs:attributeGroup element.

An attribute group is simply a named group of xs:attribute declarations (or references to other attribute groups) that can be referenced from within a complex type definition. The attribute group must be declared as a global element with a unique name attribute. The group is referenced within a complex type definition by including another xs:attributeGroup element with a ref attribute that matches the desired top-level attribute group name.

Within the fullName schema, an attribute group could be used to create a package of attributes related to a person's nationality. This package of attributes could be used on several elements, including the fullName element, without repeating the same attribute declarations. Then, if it were later necessary to extend this collection of attributes, it could be done in a single location:

<xs:element name="fullName">
. . .
      <xs:extension base="xs:string">
        <xs:attributeGroup ref="nationality"/>
      </xs:extension>
. . .
</xs:element>

<xs:attributeGroup name="nationality">
  <xs:attribute name="language" type="xs:language"/>
</xs:attributeGroup>
Previous | Home | Next
16. XML Schemas | Book Index | 16.3. Working with Namespaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Working with Namespaces (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.3. Working with Namespaces

So far, namespaces have only been dealt with as they relate to the schema processor and schema language itself. But the schema specification was written with the intention that schemas could support and describe XML namespaces. In an ideal world, any XML parser with access to the Internet would be able to validate any XML document, given only that document's namespace. In fact, the Resource Directory Description Language (RDDL) standard is an attempt to build the framework that will enable this functionality and is described in detail in Chapter 14.

16.3.1. Target Namespaces

Associating a schema with a particular XML namespace is extremely simple: add a targetNamespace attribute to the root xs:schema element, like so:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://namespaces.oreilly.com/xmlnut/address">
TIP: It is important to remember that many XML 1.0 documents are not associated with namespaces at all. To validate these documents, it is necessary to use a schema that doesn't have a targetNamespace attribute. When developing schemas that are not associated with a target namespace, you should explicitly qualify schema elements (like xs:element) to keep them from being confused with global declarations for your application.

However, making that simple change impacts numerous other parts of the example application. Trying to validate the addressdoc.xml document as it stands (with the xsi:noNamespaceSchemaLocation attribute) causes the Xerces schema processor to report this validity error:

General Schema Error: Schema in address-schema.xsd has a different target
namespace from the one specified in the instance document :.

To rectify this, it is necessary to change the instance document to reference the new, namespace-enabled schema properly. This is done using the xsi:schemaLocation attribute, like so:

<fullName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
    address-schema.xsd"
  language="en">Scott Means</fullName>

Notice that the schemaLocation attribute value contains two tokens. The first is the target namespace URI that matches the target namespace of the schema document. The second is the physical location of the actual schema document.

Unfortunately, there are still problems. If this document is validated, the validator will report errors like these two:

Element type "fullName" must be declared.
Attribute "language" must be declared for element type "fullName".

This is because, even though a schema location has been declared, the element still doesn't actually belong to a namespace. Either a default namespace must be declared or a namespace prefix that matches the target namespace of the schema must be used. The following document uses a default namespace:

<fullName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
    address-schema.xsd"
  xmlns="http://namespaces.oreilly.com/xmlnut/address"
  language="en">Scott Means</fullName>

But before this document can be successfully validated, it is necessary to fix one other problem that was introduced when a target namespace was added to the schema. Within the element declaration for the fullName element, there is a reference to the nationality attribute group. By associating the schema with a target namespace, every global declaration has been implicitly associated with that namespace. This means that the ref attribute of the attribute group element in the element declaration must be updated to point to an attribute group that belongs to the new target namespace.

The clearest way to do this is to declare a new namespace prefix in the schema that maps to the target namespace and use it to prefix any references to global declarations:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://namespaces.oreilly.com/xmlnut/address"
  xmlns:addr="http://namespaces.oreilly.com/xmlnut/address">
. . .
        <xs:attributeGroup ref="addr:nationality"/>
. . .

Now, having made these three simple changes, the document will once again validate cleanly against the schema.

TIP: The obvious lesson from this is that namespaces should be incorporated into your schema design as early as possible. If not, there will likely be a large amount of cleanup involved as various assumptions that used to be true are no longer valid.

16.3.2. Controlling Qualification

One of the major headaches with DTDs is that they have no explicit support for namespace prefixes since they predate the Namespaces in XML recommendation. Although Namespaces in XML went to great pains to explain that prefixes were only placeholders and only the namespace URIs really matter, it was painful and awkward to design a DTD that could support arbitrary prefixes. Schemas correct this by validating against namespace URIs and local names rather than prefixed names.

The elementFormDefault and attributeFormDefault attributes of the xs:schema element control whether locally declared elements and attributes must be namespace-qualified within instance documents. Suppose the attributeFormDefault attribute is set to qualified in the schema like this:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://namespaces.oreilly.com/xmlnut/address"
  xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
  attributeFormDefault="qualified">

Now, if addressdoc.xml is validated against the schema, the validator reports the following error:

Attribute "language" must be declared for element type "fullName".

Since the default attribute form has been set to qualified, the schema processor doesn't recognize the unqualified language attribute as belonging to the same schema as the fullName element. This is because attributes, unlike elements, don't inherit the default namespace from the xmlns="..." attribute. They must always be explicitly prefixed if they need to belong to a particular namespace.

The easiest way to fix the instance document is to declare an explicit namespace prefix and use it to qualify the element and attribute, as shown in Example 16-5.

Example 16-5. addressdoc.xml with explicit namespace prefix

<?xml version="1.0"?>
<addr:fullName xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
    address-schema.xsd"
  xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
  addr:language="en">Scott Means</addr:fullName>

The elementFormDefault attribute serves the same function in regards to namespace qualification of nested elements. If it is set to qualified, nested elements must belong to the target namespace of the schema (either through a default namespace declaration or an explicit prefix).

Previous | Home | Next
16.2. Schema Basics | Book Index | 16.4. Complex Types
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Complex Types (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.4. Complex Types

A schema assigns a type to each element and attribute it declares. In Example 16-5, the fullName element has a complex type. Elements with complex types may contain nested elements and have attributes. Only elements can have complex types. Attributes always have simple types.

Since the type is declared using an xs:complexType element embedded directly in the element declaration, it is also an anonymous type rather than a named type.

New types are defined using xs:complexType or xs:simpleType elements. If a new type is declared globally with a top-level element, it needs to be given a name so that it can be referenced from element and attribute declarations within the schema. If a type is declared inline (inside an element or attribute declaration), it does not need to be named. But since it has no name, it cannot be referenced by other element or attribute declarations. When building large and complex schemas, data types will need to be shared among multiple different elements. To facilitate this reuse, it is necessary to create named types.

To show how named types and complex content interact, let's expand the example schema. A new address element will contain the fullName element, and the person's name will be divided into a first- and last-name component. A typical instance document would look like Example 16-6.

Example 16-6. addressdoc.xml after adding address, first, and last elements

<?xml version="1.0"?>
<addr:address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
      address-schema.xsd"
    xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
    addr:language="en">
  <addr:fullName>
    <addr:first>Scott</addr:first>
    <addr:last>Means</addr:last>
  </addr:fullName>
</addr:address>

To accommodate this new format, fairly substantial structural changes to the schema are required, as shown in Example 16-7.

Example 16-7. address-schema.xsd to support address element

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://namespaces.oreilly.com/xmlnut/address"
  xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
  attributeFormDefault="qualified" elementFormDefault="qualified">
<xs:element name="address">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="fullName">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="first" type="addr:nameComponent"/>
            <xs:element name="last" type="addr:nameComponent"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  <xs:attributeGroup ref="addr:nationality"/>
  </xs:complexType>
 </xs:element>

 <xs:complexType name="nameComponent">
  <xs:simpleContent>
    <xs:extension base="xs:string"/>
  </xs:simpleContent>
 </xs:complexType>
</xs:schema>

The first major difference between this schema and the previous version is that the root element name has been changed from fullName to address. The same result could have been accomplished by creating a new top-level element declaration for the new address element, but that would have opened a loophole allowing a valid instance document to contain only a fullName element and nothing else.

Within the address element declaration, a new anonymous complex type is declared. Unlike the old declaration, this complex type is declared to contain complex content using the xs:sequence element. The sequence element tells the schema processor that the contained list of elements must appear in the target document in the exact order they are given. In this case, the sequence contains only one element declaration.

The nested element declaration is for the fullName element, which then repeats the xs:complexType and xs:sequence declaration process. Within this nested sequence, two element declarations appear for the first and last elements.

These two element declarations, unlike all prior element declarations, explicitly reference a new complex type that's declared in the schema, the addr:nameComponent type. It is fully qualified to differentiate it from possible conflicts with built-in schema data types.

The nameComponent type is declared by the xs:complexType element immediately following the address element declaration. It is identified as a named type by the presence of the name attribute, but in every other way it is constructed the same way it would have been as an anonymous type.

16.4.1. Occurrence Constraints

One feature of schemas that should be welcome to DTD developers is the ability to explicitly set the minimum and maximum number of times an element may occur at a particular point in a document using minOccurs and maxOccurs attributes of the xs:element element. For example, this declaration adds an optional middle name to the fullName element:

<xs:element name="fullName">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="first" type="addr:nameComponent"/>
      <xs:element name="middle" type="addr:nameComponent"
          minOccurs="0"/>
      <xs:element name="last" type="addr:nameComponent"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Notice that the element declaration for the middle element has a minOccurs value of 0. The default value for both minOccurs and maxOccurs is 1, if they are not provided explicitly. Therefore, setting minOccurs to 0 means that the middle element may appear 0 to 1 times. This is equivalent to using the ? operator in a DTD declaration. Another possible value for the maxOccurs attribute is unbounded, which indicates that the element in question may appear an unlimited number of times. This value is used to produce the same effect as the * and + operators in a DTD declaration.

16.4.2. Types of Element Content

So far you have seen elements that contain only character data and elements that contain only other elements. The next several sections cover each of the possible types of element content individually, from most restrictive to least restrictive:

  • Empty

  • Simple content

  • Mixed content

  • Any type

Previous | Home | Next
16.3. Working with Namespaces | Book Index | 16.5. Empty Elements
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Empty Elements (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.5. Empty Elements

In many cases, it is useful to declare an element that cannot contain anything. Most of these elements convey all of their information via attributes or simply by their position in relation to other elements (e.g., the br element from XHTML).

Let's add a contact-information element to the address element that will be used to contain a list of ways to contact a person. Example 16-8 shows the sample instance document after adding the new contacts element and a sample phone entry.

Example 16-8. addressdoc.xml with contact element

<?xml version="1.0"?>
<addr:address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
      address-schema.xsd"
    xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
    addr:language="en">
  <addr:fullName>
    <addr:first>William</addr:first>
    <addr:middle>Scott</addr:middle>
    <addr:last>Means</addr:last>
  </addr:fullName>
  <addr:contacts>
    <addr:phone addr:label="888.737.1752"/>
  </addr:contacts>
</addr:address>

Supporting this new content requires further modifications to the schema document. Although it would be possible to declare the new element inline within the existing address-element declaration, for clarity it makes sense to create a new global type and reference it by name:

<xs:element name="address">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="fullName">
. . .
      </xs:element>
      <xs:element name="contacts" type="addr:contactsType" minOccurs="0"/>
    </xs:sequence>
  <xs:attributeGroup ref="addr:nationality"/>
  </xs:complexType>
</xs:element>

The declaration for the new contactsType complex type looks like this:

<xs:complexType name="contactsType">
  <xs:sequence>
    <xs:element name="phone" minOccurs="0">
      <xs:complexType>
        <xs:attribute name="number" type="xs:string"/>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

The syntax used to declare an empty element is actually very simple. Notice that the xs:element declaration for the previous phone element contains a complex type definition that only includes a single attribute declaration. This tells the schema processor that the phone element may only contain complex content (elements), and since no additional nested element declarations are provided, it must remain empty.

16.5.1. complexContent

The preceding example actually took a shortcut with the schema language. One of the early fullName element declarations used the xs:simpleContent element to indicate that the element could only contain simple content (no nested elements). There is a corresponding content-declaration element that specifies that a complex type can only contain complex content (elements). This is the xs:complexContent element.

When the phone element was declared using an xs:complexType element with no nested element declarations, the schema processor automatically inferred that it should contain only complex content. The phone element declaration could be rewritten like so, using the xs:complexContent element:

<xs:element name="phone" minOccurs="0">
  <xs:complexType>
    <xs:complexContent>
      <xs:restriction base="xs:anyType">
        <xs:attribute name="number" type="xs:string"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
</xs:element>

The most common reason to use the xs:complexContent element is to derive a complex type from an existing type. This example derives a new type by restriction from the built-in xs:anyType type. xs:anyType is the root of all of the built-in schema types and represents an unrestricted sequence of characters and markup. Since the xs:complexType indicates that the element can only contain element content, the effect of this restriction is to prevent the element from containing either character data or markup.

Previous | Home | Next
16.4. Complex Types | Book Index | 16.6. Simple Content
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Simple Content (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.6. Simple Content

Earlier, the xs:simpleContent element was used to declare an element that could only contain simple content:

<xs:element name="fullName">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="language" type="xs:language"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
 </xs:element>

The base type for the extension in this case was the built-in xs:string data type. But simple types are not limited to the predefined types. The xs:simpleType element can define new simple data types, which can be referenced by element and attribute declarations within the schema.

16.6.1. Defining New Simple Types

To show how new simple types can be defined, let's extend the phone element from the example application to support a new attribute called location. This attribute will be used to differentiate between work and home phone numbers. This attribute will have a new simple type called locationType, which will be referenced from the contactsType definition:

<xs:complexType name="contactsType">
  <xs:sequence>
    <xs:element name="phone" minOccurs="0">
      <xs:complexType>
        <xs:attribute name="number" type="xs:string"/>
         <xs:attribute name="location" type="addr:locationType"/>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

<xs:simpleType name="locationType">
  <xs:restriction base="xs:string"/>
</xs:simpleType>

Of course, a location type that just maps to the built-in xs:string type isn't particularly useful. Fortunately, schemas can strictly control the possible values of simple types through a mechanism called facets.

16.6.2. Facets

In schema-speak, a facet is an aspect of a possible value for a simple data type. Depending on the base type, some facets make more sense than others. For example, a numeric data type can be restricted by the minimum and maximum possible values it could contain. But these types of restrictions wouldn't make sense for a boolean value. The following list covers the different facet types that are supported by a schema processor:

  • length (or minLength and maxLength)

  • pattern

  • enumeration

  • whiteSpace

  • maxInclusive and maxExclusive

  • minInclusive and minExclusive

  • totalDigits

  • fractionDigits

Facets are applied to simple types using the xs:restriction element. Each facet is expressed as a distinct element within the restriction block, and multiple facets can be combined to further restrict potential values of the simple type.

16.6.2.1. Handling whitespace

The whiteSpace facet controls how the schema processor will deal with any whitespace within the target data. Whitespace normalization takes place before any of the other facets are processed. There are three possible values for the whiteSpace facet:

preserve
Keep all whitespace exactly as it was in the source document (basic XML 1.0 whitespace handling for content within elements).

replace
Replace occurrences of #x9 (tab), #xA (line feed), and #xD (carriage return) characters with #x20 (space) characters.

collapse
Perform the replace step first, then collapse multiple-space characters into a single space.

16.6.2.2. Restricting length

The length-restriction facets are fairly easy to understand. The length facet forces a value to be exactly the length given. The minLength and maxLength facets can be used to set a definite range for the lengths of values of the type given. For example, take the nameComponent type from the schema. What if a name component could not exceed 50 characters (because of a database limitation, for instance)? This rule can be enforced by using the maxLength facet. Incorporating this facet requires a new simple type to reference from within the nameComponent complex type definition:

<xs:complexType name="nameComponent">
  <xs:simpleContent>
    <xs:extension base="addr:nameString"/>
  </xs:simpleContent>
 </xs:complexType>

 <xs:simpleType name="nameString">
  <xs:restriction base="xs:string">
    <xs:maxLength value="50"/>
  </xs:restriction>
 </xs:simpleType>

The new nameString simple type is derived from the built-in xs:string type, but can contain no more than 50 characters (the default is unlimited). The same approach can be used with the length and minLength facets.

16.6.2.3. Enumerations

One of the more useful types of restriction is the simple enumeration. In many cases, it is sufficient to restrict possible values for an element or attribute to a member of a predefined list. For example, values of the new locationType simple type defined earlier could be restricted to a list of valid options like so:

<xs:simpleType name="locationType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="work"/>
    <xs:enumeration value="home"/>
    <xs:enumeration value="mobile"/>
  </xs:restriction>
</xs:simpleType>

Then, if the location attribute in any instance document contained a value not found in the list of enumeration values, the schema processor would generate a validity error.

16.6.2.4. Numeric Facets

Almost half of the of built-in data types defined by the schema specification represent numeric data of one type or another. More might be called numeric since the date/time and duration types are considered to be scalar quantities as well. The following two sections cover all of the numeric facets available, but for a comprehensive list of which of these facets are applicable to which data types, see Chapter 21.

16.6.2.4.1. Minimum and maximum values

Four facets control the minimum and maximum values of items:

  • minInclusive

  • minExclusive

  • maxInclusive

  • maxExclusive

The primary difference between the inclusive and exclusive flavors of the min and max facets is whether the value given is considered part of the set of allowable values. For example, the following two facet declarations are equivalent:

<xs:maxInclusive value="1"/>
<xs:maxExclusive value="0"/>

The difference between inclusive and exclusive becomes more significant when dealing with decimal or floating point values. For example, if minExclusive were set to 5.0, the equivalent minInclusive value would require an infinite number of nines to the right of the decimal point (4.99999). These facets can also be applied to date and time values.

16.6.2.4.2. Length and precision

There are two facets that control the length and precision of decimal numeric values: totalDigits and fractionDigits . The totalDigits facet determines the total number of digits (only digits are counted, not signs or decimal points) that are allowed in a complete number. fractionDigits determines the number of those digits that must appear to the right of the decimal point in the number.

16.6.2.5. Enforcing format

The xs:pattern facet can place very sophisticated restrictions on the format of string values. The pattern facet compares the value in question against a regular expression, and if the value doesn't conform to the expression, it generates a validation error. For example, this xs:simpleType element declares a social security number simple type using the pattern facet:

<xs:simpleType name="ssn">
  <xs:restriction base="xs:string">
    <xs:pattern value="\d\d\d-\d\d-\d\d\d\d"/>
  </xs:restriction>
 </xs:simpleType>

This new simple type enforces the rule that a social security number consists of three digits, a dash followed by two digits, another dash, and finally four more digits. The actual regular-expression language is very similar to that of the Perl programming language, but it also supports a wide range of Unicode characters. See Chapter 21 for more information on the full pattern-matching language.

16.6.2.6. Lists

XML 1.0 provided a few very simple list types that could be declared as possible attribute values: IDREFS, ENTITIES, and NMTOKENS. Schemas have generalized the concept of lists and provide the ability to declare lists of arbitrary types.

These list types are themselves simple types and may be used in the same places other simple types are used. For example, if the fullName element were to be expanded to accommodate multiple middle names, one approach would be to declare the middle element to contain a list of nameString values:

 <xs:element name="middle" type="addr:nameList" minOccurs="0"/>
. . .
<xs:complexType name="nameList">
  <xs:simpleContent>
    <xs:extension base="addr:nameListType"/>
  </xs:simpleContent>
 </xs:complexType>

 <xs:simpleType name="nameListType">
  <xs:list itemType="addr:nameString"/>
 </xs:simpleType>

After this change has been made, the middle element of an instance document can contain an unlimited list of names, each of which can contain up to 50 characters separated by whitespace. The use of xs:complextype here will greatly simplify adding attributes later.

16.6.2.7. Unions

In some cases, it is useful to allow potential values for elements and attributes to have any of several types. The xs:union element allows a type to be declared that can draw from multiple type spaces. For example, it might be useful to allow users to enter their own one-word descriptions into the location attribute of the phone element, as well as to choose from a list. The location attribute declaration could be modified to include a union that incorporated the locationType type and the xs:NMTOKEN types:

<xs:attribute name="location">
  <xs:simpleType>
    <xs:union memberTypes="addr:locationType xs:NMTOKEN"/>
  </xs:simpleType>
</xs:attribute>

Now the location attribute can contain either addr:locationType or xs:NMTOKEN content.

Previous | Home | Next
16.5. Empty Elements | Book Index | 16.7. Mixed Content
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Mixed Content (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.7. Mixed Content

XML 1.0 provided the ability to declare an element that could contain parsed character data (#PCDATA) and unlimited occurrences of elements drawn from a provided list. Schemas provide the same functionality plus the ability to control the number and sequence in which elements appear within character data.

16.7.1. Allowing Mixed Content

The mixed attribute of the complexType element controls whether character data may appear within the body of the element with which it is associated. To illustrate this concept, Example 16-9 gives us a new schema that will be used to validate form-letter documents.

Example 16-9. formletter.xsd

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="letter">
    <xs:complexType mixed="true"/>
  </xs:element>
</xs:schema>

This schema seems to declare a single element called body that may contain character data and nothing else. But attempting to validate the following document produces an error, as shown in Example 16-10.

Example 16-10. formletterdoc.xml

<letter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="formletter.xsd">Hello!</letter>

The following error is generated:

The content of element type "letter" must match "EMPTY".

This is because there's no complex content for the letter element. Setting mixed to true is not the same as declaring an element that may contain a string. The character data may only appear in relation to other complex content, which leads to the subject of relative element positioning.

16.7.2. Controlling Element Placement

You have already seen the xs:sequence element, which dictates that the elements it contains must appear in exactly the same order in which they appear within the sequence element. In addition to xs:sequence, schemas also provide the xs:choice and xs:all elements to control the order in which elements may appear. These elements may be nested to create sophisticated element structures.

Expanding the form-letter example, a sequence adds support for various letter components to the formletter.xsd schema:

<xs:element name="letter">
  <xs:complexType mixed="true">
    <xs:sequence>
      <xs:element name="greeting"/>
      <xs:element name="body"/>
      <xs:element name="closing"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

Now, thanks to the xs:sequence element, a letter must include a greeting element, a body element, and a closing element, in that order. But in some cases, what is desired is that one and only one element appear from a collection of possibilities. The xs:choice element supports this. For example, if the greeting element needed to be restricted to contain only one salutation out of a permissible list, it could be declared to do so using xs:choice:

<xs:element name="greeting">
  <xs:complexType mixed="true">
    <xs:choice>
      <xs:element name="hello"/>
      <xs:element name="hi"/>
      <xs:element name="dear"/>
    </xs:choice>
  </xs:complexType>
</xs:element>

Now one of the permitted salutations must appear in the greeting element for the letter to be considered valid.

The remaining element-order enforcement construct is the xs:all element. Unlike the xs:sequence and xs:choice elements, the xs:all element must appear at the top of the content model and can only contain elements that are optional or appear only once. The xs:all construct tells the schema processor that each of the contained elements must appear once in the target document, but can appear in any order. This could be applied in the form-letter example. If the form letter had certain elements that had to appear in the body element, but not in any particular order, xs:all could be used to control their appearance:

<xs:element name="body">
  <xs:complexType mixed="true">
    <xs:all>
      <xs:element name="item"/>
      <xs:element name="price"/>
      <xs:element name="arrivalDate"/>
    </xs:all>
  </xs:complexType>
</xs:element>

This would allow the letter author to mix these elements into the narrative without being restricted as to any particular order. Also, it would prevent the author from inserting multiple references to the same value by accident. A valid document instance, including the new body content, might look like Example 16-11.

Example 16-11. formletterdoc.xml

<letter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="formletter.xsd">
  <greeting><hello/> Bob!</greeting>
  <body>
    Thank you for ordering the <item/> ($<price/>), it should arrive
    by <arrivalDate/>.
  </body>
  <closing/>
</letter>
TIP: The element order constructs are not just limited to complex types with mixed content. If the mixed attribute is not present, the declared sequence of child elements is still enforced, but no character data is permitted between them.

16.7.3. Using Groups

Just as the xs:attributeGroup element allows commonly used attributes to be grouped together and referenced as a unit, the xs:group element allows sequences, choices, and model groups of individual element declarations to be grouped together and given a unique name. These groups can then be included in another element-content model using an xs:group element with the ref attribute set to the same value as the name attribute of the source group.

Previous | Home | Next
16.6. Simple Content | Book Index | 16.8. Allowing Any Content
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Allowing Any Content (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.8. Allowing Any Content

It is often necessary to allow users to include any type of markup content they see fit. Also, it is useful to tell the schema processor to validate the content of a particular element against another application's schema. Incorporating XHTML content into another document is an example of this usage.

These applications are supported by the xs:any element. This element accepts attributes that indicate what level of validation should be performed on the included content, if any. Also, it accepts a target namespace that can be used to limit the vocabulary of included content. For instance, going back to the address-book example, to associate a rich-text notes element with an address entry, you could add the following element declaration to the address element declaration:

<xs:element name="notes" minOccurs="0">
  <xs:complexType>
    <xs:sequence>
      <xs:any namespace="http://www.w3.org/1999/xhtml"
           minOccurs="0" maxOccurs="unbounded"
           processContents="skip"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

The attributes of the xs:any element tell the schema processor that zero or more elements belonging to the XHTML namespace (http://www.w3.org/1999/xhtml) may occur at this location. Notice that this is done by setting minOcccurs to 0 and maxOccurs to unbounded. It also states that these elements should be skipped. This means that no validation will be performed against the actual XHTML namespace by the parser. Other possible values for the processContents attribute are lax and strict. When set to lax, the processor will attempt to validate any element it can find a declaration for and silently ignore any unrecognized elements. The strict option requires every element to be declared and valid per the schema associated with the namespace given.

There is also support in schemas to declare that any attribute may appear within a given element. The xs:anyAttribute element may include the namespace and processContents attributes, which perform the same function as they do in the xs:any element. For example, adding the following markup to the address element would allow any XLink attributes to appear in an instance document:

<xs:element name="address">
  <xs:complexType>
. . .
  <xs:attributeGroup ref="addr:nationality"/>
  <xs:attribute name="ssn" type="addr:ssn"/>
  <xs:anyAttribute namespace="http://www.w3.org/1999/xlink"
      processContents="skip"/>
  </xs:complexType>
 </xs:element>

As an application grows and becomes more complex, it is important to take steps to maintain readability and extensibility. Things like separating a large schema into multiple documents, importing declarations from external schemas, and deriving new types from existing types are all typical tasks that will face designers of real-world schemas.

16.8.1. Using Multiple Documents

Just as large computer programs are separated into multiple physical source files, large schemas can be separated into smaller, self-contained schema documents. Although a single large schema could be arbitrarily separated into multiple smaller documents, taking the time to group related declarations into reusable modules can simplify future schema development.

There are three mechanisms that include declarations from external schemas for use within a given schema: xs:include, xs:redefine, and xs:import. The next three sections will discuss the differences between these methods and when and where they should be used.

16.8.1.1. Including external declarations

The xs:include element is the most straightforward way to bring content from an external document into a schema. To demonstrate how xs:include might be used, Example 16-12 shows a new schema document called physical-address.xsd that contains a declaration for a new complex type called physicalAddressType.

Example 16-12. physical-address.xsd

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://namespaces.oreilly.com/xmlnut/address"
  xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
  attributeFormDefault="qualified" elementFormDefault="qualified">

  <xs:annotation>
    <xs:documentation xml:lang="en-us">
      Simple schema example from O'Reilly's
      <a href="http://www.oreilly.com/catalog/xmlnut">XML in a
        Nutshell.</a>
      Copyright 2002 O'Reilly &amp; Associates
    </xs:documentation>
  </xs:annotation>

  <xs:complexType name="physicalAddressType">
    <xs:sequence>
      <xs:element name="street" type="xs:string" maxOccurs="3"/>
      <xs:element name="city" type="xs:string"/>
      <xs:element name="state" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

</xs:schema>

The address-book.xsd schema document can include and reference this declaration:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://namespaces.oreilly.com/xmlnut/address"
  xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
  attributeFormDefault="qualified" elementFormDefault="qualified">
. . .

 <xs:include schemaLocation="physical-address.xsd"/>

 <xs:element name="address">
  <xs:complexType>
    <xs:sequence>
. . .
      <xs:element name="physicalAddress"
         type="addr:physicalAddressType"/>
. . .
    </xs:sequence>
. . .
  </xs:complexType>
 </xs:element>

Content that has been included using the xs:include element is treated as though it were actually a part of the including schema document. But unlike external entities, the included document must be a valid schema in its own right. That means that it must be a well-formed XML document and have an xs:schema element as its root element. Also, the target namespace of the included schema must match that of the including document.

16.8.1.2. Modifying external declarations

The xs:include element allows external declarations to be included and used as-is by another schema document. But sometimes it is useful to extend and modify types and declarations from another schema, which is where the xs:redefine element comes in.

Functionally, the xs:redefine elements works very much like the xs:include element. The major difference is that within the scope of the xs:redefine element, types from the included schema may be redefined without generating an error from the schema processor. For example, the xs:redefine element could extend the physicalAddressType type to include longitude and latitude attributes without modifying the original declaration in physical-address.xs:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://namespaces.oreilly.com/xmlnut/address"
  xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
  attributeFormDefault="qualified" elementFormDefault="qualified">
. . .
<xs:redefine schemaLocation="physical-address.xsd">
  <xs:complexType name="physicalAddressType">
    <xs:complexContent>
      <xs:extension base="addr:physicalAddressType">
        <xs:attribute name="latitude" type="xs:decimal"/>
        <xs:attribute name="longitude" type="xs:decimal"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
 </xs:redefine>
. . .
</xs:schema>

16.8.1.3. Importing schemas for other namespaces

The xs:include and xs:redefine elements are useful when the declarations are all part of the same application. But as more public schemas become available, incorporating declarations from external sources into custom applications will be important. The xs:import element is provided for this purpose.

Using xs:import, it is possible to make the global types and elements that are declared by a schema belonging to another namespace accessible from within an arbitrary schema. The W3C has used this functionality to create type libraries. A sample type library was developed by the schema working group and can be viewed on the W3C web site at http://www.w3.org/2001/03/XMLSchema/TypeLibrary.xsd.

To use some of the types from this library in a schema, include the following xs:import element as a child of the root schema element:

<xs:import namespace="http://www.w3.org/2001/03/XMLSchema/TypeLibrary"
    schemaLocation="http://www.w3.org/2001/03/XMLSchema/TypeLibrary.xsd"/>

16.8.2. Derived Complex Types

We have been using the xs:extension and xs:restriction elements without going too deeply into how or why they work. The schema language provides functionality for extending existing types, which is conceptually similar to that of inheritance in object-oriented programming. The extension and restriction elements allow new types to be defined either by expanding or limiting the potential values of existing types.

16.8.2.1. Deriving by extension

When deriving a new type from an existing type, the resulting type is equivalent to appending the contents of the new declaration to the contents of the base declaration. For instance, the following example declares a new type called mailingAddressType that extends the physicalAddressType type to include a zip code:

<xs:complexType name="mailingAddressType">
  <xs:complexContent>
    <xs:extension base="addr:physicalAddressType">
      <xs:sequence>
        <xs:element name="zipCode" type="xs:string"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

This declaration appends a required element, zipCode, to the existing physicalAddressType type. The biggest benefit of this approach is that as new declarations are added to the underlying type, the derived type will automatically inherit them.

16.8.2.2. Deriving by restriction

When a new type is a logical subset of an existing type, the xs:restriction element allows this relationship to be expressed directly. Like the xs:extension type, it allows a new type to be created based on an existing type. In the case of simple types, this restriction is a straightforward application of additional constraints on the value of that simple value.

In the case of complex types, it is not quite so straightforward. Unlike the extension process, it is necessary to completely reproduce the parent type definition as part of the restriction definition. By omitting parts of the parent definition, the restriction element creates a new, constrained type. As an example, this xs:complexType element derives a new type from the physicalAddressType that only allows a single street element to contain the street address. The original physicalAddressType looks like:

<xs:complexType name="physicalAddressType">
  <xs:sequence>
    <xs:element name="street" type="xs:string" maxOccurs="3"/>
    <xs:element name="city" type="xs:string"/>
    <xs:element name="state" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

The restricted version looks like:

<xs:complexType name="simplePhysicalAddressType">
  <xs:complexContent>
    <xs:restriction base="addr:physicalAddressType">
      <xs:sequence>
        <xs:element name="street" type="xs:string"/>
        <xs:element name="city" type="xs:string"/>
        <xs:element name="state" type="xs:string"/>
      </xs:sequence>
    </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

Notice that this type very closely resembles the physicalAddressType, except the maxOccurs="3" attribute has been removed from the street element declaration.

16.8.2.3. Using derived types

One of the chief benefits of creating derived types is that the derived type may appear in place of the parent type within an instance document. The xsi:type attribute tells the schema processor that the element on which it appears conforms to a type that is derived from the normal type expected. For example, take the instance document in Example 16-13, which conforms to the address schema.

Example 16-13. addressdoc.xml using a derived type

<?xml version="1.0"?>
<addr:address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://namespaces.oreilly.com/xmlnut/address
      address-schema.xsd"
    xmlns:addr="http://namespaces.oreilly.com/xmlnut/address"
    addr:language="en"
    addr:ssn="123-45-6789">
. . .
  <physicalAddress addr:latitude="34.003855" addr:longitude="-81.034808"
    xsi:type="addr:simplePhysicalAddressType">
    <street>1400 Main St.</street>
    <city>Columbia</city>
    <state>SC</state>
  </physicalAddress>
. . .
</addr:address>

Notice that the physicalAddress element has an xsi:type attribute that informs the validator that the current element conforms to the simplePhysicalAddressType, rather than the physicalAddressType that would normally be expected. This feature is particularly useful when developing internationalized applications, as distinct address types could be derived for each country and then flagged in the instance document for proper validation.

16.8.3. Substitution Groups

A feature that is closely related to derived types is the substitution group. A substitution group is a collection of elements that are all interchangeable with a particular element, called the head element, within an instance document. To create a substitution group, all that is required is that an element declaration include a substitutionGroup attribute that names the head element for that group. Then, anywhere that the head element's declaration is referenced in the schema, any member of the substitution group may also appear. Unlike derived types, it isn't necessary to use the xsi:type attribute in an instance document to identify the type of the substituted element.

TIP: The primary restriction on substitution groups is that every element in the group must either be of the same type as or derived from the head element's type. Declaring a numeric element and trying to add it to a substitution group based on a string element would generate an error from the schema processor. The elements must also be declared globally and in the target namespace of the schema.

Previous | Home | Next
16.7. Mixed Content | Book Index | 16.9. Controlling Type Derivation
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Controlling Type Derivation (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

16.9. Controlling Type Derivation

Just as some object-oriented programming languages allow the creator of an object to dictate the limits on how an object can be extended, the schema language allows schema authors to place restrictions on type extension and restriction.

16.9.1. Abstract Elements and Types

The abstract attribute applies to type and element declarations. When it is set to true, that element or type cannot appear directly in an instance document. If an element is declared as abstract, a member of a substitution group based on that element must appear. If a type is declared as abstract, no element declared with that type may appear in an instance document.

16.9.2. The Final Attribute

Until now, the schema has placed no restrictions on how other types or elements could be derived from its elements and types. The final attribute can be added to a complex type definition and set to either #all, extension, or restriction. On a simple type definition it can be set to #all or a list containing any combination of the values list, union, and/or restriction, in any order. When a type is derived from another type that has the final attribute set, the schema processor verifies that the desired derivation is legal. For example, a final attribute could prevent the physicalAddressType type from being extended:

<xs:complexType name="physicalAddressType" final="extension">

Since the main schema in address-schema.xsd attempts to redefine the physicalAddressType in an xs:redefine block, the schema processor generates the following errors when it attempts to validate the instance document:

ComplexType 'physicalAddressType': cos-ct-extends.1.1: Derivation by
extension is forbidden by either the base type physicalAddressType_redefined
or the schema.
Attribute "addr:latitude" must be declared for element type "physicalAddress".
Attribute "addr:longitude" must be declared for element type
"physicalAddress".

The first error is a result of trying to extend a type that has been marked to prevent extension. The next two errors occur because the new, extended type was not parsed and applied to the content in the document. Now that you've seen how this works, removing this particular "feature" from the physicalAddressType definition gets the schema working again.

16.9.3. Setting fixed Facets

Similar to the final attribute, the fixed attribute is provided to mark certain facets of simple types as immutable. Facets that have been marked as fixed="true" cannot be overridden in derived types.

16.9.4. Uniqueness and Keys

Perhaps one of the most welcome features of schemas is the ability to express more sophisticated relationships between values in elements and attributes of a document. The limitations of the primitive index capability provided by the XML 1.0 ID and IDREF attributes became readily apparent as documents began to include multiple distinct types of element data with complex data keys. The two facilities for enforcing element uniqueness in schemas are the xs:unique and xs:key elements.

16.9.4.1. Forcing uniqueness

The xs:unique element enforces element and attribute value uniqueness for a specified set of elements in a schema document. This uniqueness constraint is constructed in two phases. First, the set of all of the elements to be evaluated is defined using a restricted XPath expression. Next, the precise element and attribute values that must be unique are defined.

To illustrate, let's add logic to the address schema to prevent the same phone number from appearing multiple times within a given contacts element. To add this restriction, the element declaration for contacts includes a uniqueness constraint:

<xs:element name="contacts" type="addr:contactsType" minOccurs="0">
  <xs:unique name="phoneNums">
    <xs:selector xpath="phone"/>
    <xs:field xpath="@addr:number"/>
  </xs:unique>
</xs:element>

Now, if a given contacts element contains two phone elements with the same value for their number attributes, the schema processor will generate an error.

This is the basic algorithm that the schema processor follows to enforce these restrictions:

  1. Use the xpath attribute of the single xs:selector element to build a set of all of the elements to which the restriction will apply.

  2. Logically combine the values referenced by each xs:field element for each selected element. Compare the combinations of values that you get for each of the elements.

  3. Report any conflicts as a validity error.

TIP: The very perceptive among you, are right: the contactsType type definition only permits a single phone child element. So this particular restriction would not be very useful. Modifying the contactsType definition to permit multiple child elements is not difficult.

16.9.4.2. Keys and references

The xs:key element is closely related to the xs:unique element. Logically, the xs:key element functions exactly the same way the xs:unique element does. It uses the xs:selector element to define a set of elements it applies to, then one or more xs:field elements are used to define which values make up this particular key. The major difference is that, in the case of the xs:key element, uniqueness is not the only desired property of these elements. The goal of the xs:key element is to define a set of elements that can be referenced using the xs:keyref element. Having created a fairly full-featured address element, creating a collection of these elements called addressBook would be an excellent way to show this feature in operation.

First, the new addressBook element is declared, including a key based on the ssn attribute of each address entry:

<xs:element name="addressBook">
  <xs:complexType>
    <xs:sequence maxOccurs="unbounded">
      <xs:element ref="addr:address"/>
    </xs:sequence>
  </xs:complexType>
  <xs:key name="ssnKey">
    <xs:selector xpath="addr:address"/>
    <xs:field xpath="@addr:ssn"/>
  </xs:key>
 </xs:element>

Now that the key is defined, you can add a new element to the address element declaration that connects a particular address record with another record. For example, to list references to the children of a particular person in the address book, add the following declaration for a kids element:

<xs:element name="address">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="fullName">
. . .
      </xs:element>
      <xs:element name="kids" minOccurs="0">
        <xs:complexType>
          <xs:sequence maxOccurs="unbounded">
            <xs:element name="kid">
              <xs:complexType>
                <xs:attribute name="ssn" type="addr:ssn"/>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
. . .
    </xs:sequence>
  <xs:attributeGroup ref="addr:nationality"/>
  <xs:attribute name="ssn" type="addr:ssn"/>
  <xs:anyAttribute namespace="http://www.w3.org/1999/xlink"
      processContents="skip"/>
  </xs:complexType>
 </xs:element>

Now, an xs:keyref element in the addressBook element declaration enforces the constraint that the ssn attribute of a particular kid element must match an ssn attribute on an address element in the current document:

<xs:element name="addressBook">
. . .
  <xs:key name="ssnKey">
    <xs:selector xpath="addr:address"/>
    <xs:field xpath="@addr:ssn"/>
  </xs:key>
  <xs:keyref name="kidSSN" refer="addr:ssnKey">
    <xs:selector xpath="addr:address/kids/kid"/>
    <xs:field xpath="@addr:ssn"/>
  </xs:keyref>
 </xs:element>

Now, if any kid element in an instance document refers to a nonexistent address record, the schema validat or will generate an error.

Previous | Home | Next
16.8. Allowing Any Content | Book Index | 17. Programming Models
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Programming Models (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 17. Programming Models

Contents:

Common XML Processing Models
Common XML Processing Issues

This chapter briefly explains the most popular programming models for parsing and manipulating XML data in use today. XML processing includes a diverse set of tools, which require different approaches but offer distinct advantages and disadvantages.

TIP: XML processors of all kinds are available in a wide variety of languages, including C, C#, C++, COBOL, Haskell, Java, JavaScript (ECMAScript/JScript), Pascal, Perl, Python, Ruby, SmallTalk, Tcl, and Visual Basic. If you can't find XML support built into your programming environment, a quick search will likely locate a library. XML.com maintains a list of XML resources that may be a good place to start at http://www.xml.com/resourceguide/.

17.1. Common XML Processing Models

XML's structured and labeled text can be processed by developers in several of ways. Programs can look at XML as text, as a stream of events, as a tree, or as a serialization of some other structure. Tools supporting all of these options are widely available.

17.1.1. Treating XML as Text

At their foundation, XML documents are text. The content and markup are both represented as text, and text-editing tools can be extremely useful for XML document inspection, creation, and modification. XML's textual foundations make it possible for developers to work with XML directly, using XML-specific tools only when they choose.

Despite this textual nature, however, XML presents some serious limitations for programs that attempt to process XML documents as text documents. It is possible to process extremely simple XML documents reliably using basic textual tools like regular expressions, but this becomes much more difficult as features such as attribute defaulting, entity processing, and namespaces are added to documents. Using these features is extremely difficult when treating a document purely as text.

Textual tools are a key part of the XML toolset, however. Many developers use text editors such as vi, Emacs, NotePad, WordPad, BBEdit, and UltraEdit to create or modify XML documents. Regular expressions -- in environments such as sed, grep, Perl, and Python -- can be used for search and replace or for tweaking documents prior to XML parsing or XSLT processing. These tools can also be very useful for searching and querying the information in XML documents, even without an understanding of the surrounding structure.

Textual tools may also be applied to the results of an XML parser. Regular expressions and similar text-processing tools can be applied usefully to the results of an XML parse, working on the document when its XML-specific nature has already been resolved. The W3C's XML Schema, for instance, includes regular-expression matching as one mechanism for validating data types, as discussed in Chapter 16. A smart search and replace or spell checker might process only the contents of elements (and perhaps attributes), not the markup that defines the structures.

Text-based processing can be preformed in conjunction with other XML processing. Parsing and then reserializing XML documents after other processing has taken place doesn't always produce the desired results. XSLT, for instance, will remove entity references and replace them with entity content. Preserving entities requires replacing them in the original document with unique placeholders, and then replacing the placeholder as it appears in the result. With regular expressions, this is quite easy to do. Developers may also need to replace particular characters with references to images; this approach can be very useful where an obscure or nonstandard glyph is needed in XHTML.

WARNING: XML's dependence on Unicode means that developers need to be careful about the text-processing tools they choose. Many development environments have been upgraded to support Unicode, but there are still tools available that don't. Before using text-processing tools on the results of an XML parse, make sure they support Unicode. Text-processing tools being applied to raw XML documents must support the character encoding used for the document.

17.1.2. Treating XML as Events

As an XML parser reads a document, it moves from the beginning of the document to the end. It may pause to retrieve external resources--for a DTD or an external entity, for instance--but it builds an understanding of the document as it moves along. Enforcing well-formedness and validity constraints and applying namespaces requires keeping track of context; applying attribute defaults and entities requires keeping a list of appropriate content to insert; but the end result is a complete "reading" of the XML document.

Event-based parsers report this reading as it happens, in a stream of events representing the information in the document. The "events" are, for example, the start of an element, the content of an element, and the end of an element. For example, given this document:

<name><given>Keith</given><family>Johnson</family></name>

an event-based parser might report events such as this:

startElement:name
startElement:given
content: Keith
endElement:given
startElement:family
content:Johnson
endElement:family
endElement:name

The list and structure of events can become much more complex as features, such as namespaces, attributes, whitespace between elements, comments, processing instructions, and entities are added, but the basic mechanism is quite simple and generally very efficient.

Event-based parsers only have to keep track of a limited amount of information. They need to understand the contents of DTDs (and possibly schemas), if the documents use them, and they need to maintain context stacks for element names and namespace declarations. They don't need to build a complete record of the document as they parse it, which minimizes the amount of memory needed for the parse.

Event-based parsers require the consumer of the events to do a lot more work, however. Processing events typically means the creation of a state machine, i.e., code that understands current context and can route the information in the events to the proper consumer. Because events occur as the document is read, applications must be prepared to discard results should a fatal error occur partway through the document. Applications can't depend on information that occurs later in a document to interpret the current event, either, making it hard to use some kinds of XPaths, for instance, in an event-based environment. These factors can make it difficult to work directly with event-based parsers.

Despite the potential difficulty, event-based parsers are very useful for a wide variety of tasks. Filters can process and modify events before passing them to another processor, efficiently performing a wide range of transformations. Filters can be stacked, providing a relatively simple means of building XML processing pipelines, where the information from one processor flows directly into another. Applications that want to feed information directly from XML documents into their own internal structures may find events to be the most efficient means of doing that. Even parsers that report XML documents as complete trees, as described in the next section, typically build those trees from a stream of events.

TIP: The Simple API for XML (SAX), described in Chapter 19 and Chapter 25, is the most commonly used event-based API. SAX2, the current version of SAX, is hosted at http://www.saxproject.org.Expat, which is a widely used XML parser written in C, also uses an event-based API. For more information on the expat parser and its API, see http://www.jclark.com/xml/expat.html.

17.1.3. Treating XML as Tree Models

XML documents, because of the requirements for well-formedness, describe tree structures. Documents typically contain an element that then contains text, attributes, and other elements, and these may contain elements, text, and attributes, and so on. Declarations, comments, and processing instructions enrich the mix, but all basically hold positions in the overall tree.

There are a wide variety of tree models for XML documents. XPath (described in Chapter 9), used in XSLT transformations, has a slightly different set of expectations than does the Document Object Model (DOM) API, which is also different from the XML Information Set (Infoset), another W3C project. XML Schema (described in Chapter 16 and Chapter 21) defines a Post-Schema Validation Infoset (PSVI), which has more information in it (derived from the XML Schema) than any of the others.

Developers who want to manipulate documents from their programs typically use APIs that provide access to an object model representing the XML document. Tree-based APIs typically present a model of an entire document to an application once parsing has successfully concluded. Applications don't have to worry about figuring out context or dealing with rollback when an error is encountered, since the tree model and parsing already address those issues. Rather than following a stream of events, an application can just navigate a tree to find the desired pieces of a document. Browsers and editors can present or modify the tree in conformance with user or script requests, using the tree as a persistent reference to the current content of the document.

Working with a tree model of a document isn't very different conceptually from working with a document as text. The entire document is always available, and moving around well-formed portions of a document or modifying them is fairly easy. The complete set of context for any given part of the document is always available. Developers can use XPath expressions to locate content and make decisions based on content anywhere in the document where APIs support XPath. (DOM Level 3 adds formal support for XPath, and various implementations provide their own support.)

Tree models of documents have a few drawbacks. They can take up large chunks of memory, typically multiplying the original document's size. Navigating documents can require additional processing after the parse, as developers have more options available to them. (Tree models don't impose the same kinds of discipline as event-based processing.) Both of these issues can make it difficult to scale and share applications that rely on tree models, though they may still be appropriate where small numbers of documents or small documents are being used.

TIP: The Document Object Model (DOM), described in Chapter 18 and Chapter 24, is the most common tree-based API. JDOM (http://jdom.org/) and DOM4J (http://dom4j.org/) are Java-only alternatives.

17.1.4. Transformations

Another facility available to the XML programmer is a form of the XML transformation library. The Extensible Stylesheet Language Transformation (XSLT) language, covered in Chapter 8, is the most popular tool currently available for transforming XML to HTML, XML, or any other regular language that can be expressed in XSLT. In some cases, using a transformation to perform pre- or post-processing on XML data when processing it with either DOM or SAX might be simpler or more efficient. For instance, XSLT could be used as a preprocessor for a screen-scraping application that starts from XHTML documents. A script could extract the meaningful features from the XHTML document and pour them into an application-specific XML format.

Transformations may be used by themselves, in browsers, or at the command line, but many XSLT implementations and other transformation tools offer SAX or DOM interfaces, simplifying the task of using them to build pipelines.

17.1.5. Abstracting XML Away

Developers who want to take advantage of XML's cross-platform benefits but have no patience for the details of markup can use various tools that rely on XML but don't require direct exposure to XML's structures. Web Services, mentioned in Chapter 15, can be seen as a move in this direction. You can still touch the XML directly if you need to, but toolkits make it easier to avoid doing so.

These kinds of applications are generally built as a layer on top of event- or tree-based processing, presenting their own API to the underlying information. This level of abstraction may be very useful in some cases or an inefficient inconvenience in others. It's probably helpful to understand more direct connections to XML if you need to evaluate the advantages and disadvantages of abstraction, as well as provide a bridge to systems that don't support a particular abstraction layer but still need access to the information.

17.1.6. Standards and Extensions

The SAX and DOM specifications, along with the various core XML specifications, provide a foundation for XML processing. Implementations of these standards, especially implementations of the DOM, sometimes vary from the specification. Some extensions are themselves formally specified--Scalable Vector Graphics (SVG), for instance, specifies extensions to the DOM that are specific to working with SVG. Others are just kind of tacked on, adding functionality that a programmer or vendor felt was important but wasn't in the original specification. The multiple levels and modules of the DOM have also led to developers claiming support for the DOM, but actually supporting particular subsets (or extensions) of the available specifications.

Porting standards also leads to variations. SAX was developed for Java, and the core SAX project only defines a Java API. The DOM uses Interface Definition Language (IDL) to define its API, but different implementations have interpreted the IDL slightly differently. SAX2 and the DOM are somewhat portable, but moving between environments may require some unlearning and relearning.

Some environments also offer libraries well outside the SAX and DOM interfaces. Perl and Python both offer libraries that combine event and tree processing--for instance, permitting applications to work on partial trees rather than SAX events or full DOM trees. Microsoft .NET's XMLReader offers similarly flexible processing. These approaches do not make moving between environments easy, but they can be very useful.

17.1.7. Combining Approaches

While text, events, trees, and transformations may seem very different, it isn't unusual to combine them. Most parsers that produce DOM trees also offer the option of SAX events, and there are a number of tools that can create DOM trees from SAX events or vice versa. Some tools that accept and generate SAX events actually build internal trees--many XSLT processors operate this way, using optimized internal models for their trees rather than the generic DOM. XSLT processors themselves often accept either SAX events or DOM trees as input and can produce these models (or text) for their output.

Most programmers who want direct access to XML documents start with DOM trees, which are easier to figure out initially. If they have problems that are better solved in event-based environments, they can either rewrite their code for events--it's a big change--or mix and match event processing with tree processing.

Previous | Home | Next
16.9. Controlling Type Derivation | Book Index | 17.2. Common XML Processing Issues
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Common XML Processing Issues (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

17.2. Common XML Processing Issues

As with any technology, there are several ways to accomplish most design goals when developing a new XML application, as well as a few potential problems worth knowing about ahead of time. An understanding of the intended uses for these features can help ensure that new applications will be compatible not only with their intended target audience, but also with other XML processing systems that may not even exist yet.

17.2.1. What You Get Is Not What You Saw

The XML specification provides several loopholes that permit XML parsers to play fast and loose with your document's literal contents, while retaining the semantic meaning. Comments can be omitted and entity references silently replaced by the parser without any warning to the client application. Nonvalidating parsers aren't required to retrieve external DTDs or entities, though the parser should at least warn applications that this is happening. While reconstructing an XML document with exactly the same logical structure and content is possible, guaranteeing that it will match the original in a byte-by-byte comparison is not.

TIP: XML Canonicalization defines a form of XML and a process for getting there that permits a much higher degree of predictability in reconstructing documents from their logical model. For details, see http://www.w3.org/TR/xml-c14n.

Authors of simple XML processing tools that act on data without storing or modifying it might not consider these constraints particularly restrictive. The ability to reconstruct an XML document precisely from in-memory data structures, however, becomes more critical for authors of XML editing tools and content-management solutions. While no parser is required to make all comments, whitespace, and entity references available from the parse stream, many do or can be made to do so with the proper configuration options.

The only real option to ensure that your parser reports documents as you want, and not just the minimum required by the XML 1.0 specification, is to check its documentation and configure (or choose) your parser accordingly.

17.2.2. Comments

Despite a long history in HTML of using comments for tasks like Server-Side Includes (SSI) and for hiding JavaScript code and Cascading Style Sheets, using comments for anything other than human-readable notes is generally a bad idea in XML. XML parsers may (and frequently do) discard comments entirely, keeping them from reaching an application at all. Transformations generally discard comments as well.

17.2.3. Processing Instructions

XML parsers are required to provide client applications access to XML processing instructions. Processing instructions provide a mechanism for document authors to communicate with XML-aware applications behind the scenes in a way that doesn't interfere with the content of the documentation. DTD and schema validation both ignore processing instructions, making it possible to use them anywhere in a document structure without changing the DTD or schema. The processing instruction's most widely recognized application is its ability to embed stylesheet references inside XML documents. The following XML fragment shows a stylesheet reference:

<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="test.css"?>

An XML-aware application, such as Internet Explorer 5.5, would be capable of recognizing the XML author's intention to display the document using the test.css stylesheet. This processing instruction can also be used for XSLT stylesheets or other kinds of stylesheets not yet developed, though the application needs to understand how to process them to make this work. Applications that do not understand the processing instructions can still parse and use the information in the XML document while ignoring the unfamiliar processing instruction.

TIP: For more information on how to use processing instructions to designate a stylesheet(s), see the W3C Recommendation Associating Style Sheets with XML Documents at http://www.w3.org/TR/xml-stylesheet/.

The furniture example from Chapter 20 (see Figure 20-1) gives a hypothetical application of processing instructions. A processing instruction in the bookcase.xml file signals the furniture example's processor to verify the parts list from the document against the true list of parts required to build the furniture item:

    <parts_list>
        <part_name id="A" count="1">END PANEL</part_name>
        <part_name id="B" count="2">SIDE PANEL</part_name>
        <part_name id="C" count="1">BACK PANEL</part_name>
        <part_name id="D" count="4">SHELF</part_name>
        <part_name id="E" count="8">HIDDEN CONNECTORS</part_name>
        <part_name id="F" count="8">CONNECTOR SCREWS</part_name>
        <part_name id="G" count="22">7/16" TACKS</part_name>
        <part_name id="H" count="16">SHELF PEGS</part_name>
    </parts_list>

<?furniture_app    verify_parts_list?>

This processing instruction is meaningless unless the parsing application understands the given type of processing instruction.

The XML specification also permits the association of the processing instruction's target--the XML name immediately after the <? with a notation, as described in the next section--but this is not required and is rarely used in XML.

17.2.4. Notations

The notation syntax of XML provides a way for the document author to specify an external unparsed entity's type within the XML document's framework. If an application requires access to external data that cannot be represented in XML, consider declaring a notation name and using it where appropriate when declaring external unparsed entities. For example, if an XML application were an annotated Java source-code format, the compiled bytecode could then be referenced as an external unparsed entity.

Notations effectively provide metadata, identifiers that applications may apply to information. Using notations requires making declarations in the DTD, as described in Chapter 3. One use of notations is with NOTATION-type attributes. For example, if a document contained various scripts designed for different environments, it might declare some notations and then use an attribute on a containing element to identify what kind of script it contained:

<!NOTATION DOS PUBLIC "-//MS/DOS Batch File/">
<!NOTATION BASH PUBLIC "-//UNIX/BASH Shell Script/">
<!ELEMENT batch_code (#PCDATA)*>
<!ATTLIST batch_code
    lang NOTATION (DOS | BASH)>
. . .
<batch_code lang="DOS">
  echo Hello, world!
</batch_code>

Applications that read this document and recognized the public identifier could interpret the foreign element data correctly, based on its type. (Notations can also have system identifiers, and applications can use either approach.)

Categorizing processing instructions is the other use of notations important to custom XML applications. For instance, the previous furniture_app processing-instruction example could have been declared as a notation in the DTD:

<!NOTATION furniture_app SYSTEM "http://namespaces.example.com/furniture">

Then the furniture-document processing application could verify that the processing instruction was actually intended for itself and not for another application that used a processing instruction with the same name.

17.2.5. Unparsed Entities

Unparsed entities combine attribute and notation declarations to define references to content that will require further (unspecified) processing by the application. Unparsed entities are described in more detail in Chapter 3, but though they are a feature available to applications, they are also rarely used and not generally considered interoperable among XML processors. The linking and referencing tools described in the next section are more commonly used instead.

17.2.6. Links and References

The ability to create links between and within documents is important to XML's long-term success, both on the World Wide Web and for other applications concerned about the relationships between information. The XLink specification, described in Chapter 10, defines the semantics of how these links can be created. Unlike simple HTML links, XLinks can express sophisticated relationships between the source and target elements of a link.

If an XML application requires the ability to encode relationships between various parts of an XML document, or between different documents, implementing this functionality using the XLinks recommendation should be considered. Not only would it save the effort of defining a new (and incompatible) linking scheme, the resulting documents would be intelligible to new XML authoring tools and browsers as XLinks support becomes more widespread. RDDL, described in Chapter 14, makes extensive use of XLink for machine-readable linking.

Previous | Home | Next
17. Programming Models | Book Index | 18. Document Object Model
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Document Object Model (DOM) (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 18. Document Object Model (DOM)

Contents:

DOM Foundations
Structure of the DOM Core
Node and Other Generic Interfaces
Specific Node-Type Interfaces
The DOMImplementation Interface
Parsing a Document with DOM
A Simple DOM Application

The Document Object Model (DOM) defines an API for accessing and manipulating XML documents as tree structures. The DOM is defined by a set of W3C Recommendations that describe a programming language-neutral object model used to store hierarchical documents in memory. The most recently completed standard, DOM Level 2, provides models for manipulating XML documents, HTML documents, and CSS stylesheets. This chapter covers only the parts of the DOM that are applicable to processing XML documents.

This chapter is based on the Document Object Model (DOM) Level 2 Core Specification, which was released on November 13, 2000. This version of the recommendation, along with any errata that have been reported, is available on the W3C web site (http://www.w3.org/TR/DOM-Level-2-Core/ ). At the time of this writing, the latest DOM Level 3 Core working draft had been released on January 14, 2002. The working draft corrects omissions and deficiencies in the Level 2 recommendation and includes some basic support for integrating validation into DOM API document manipulation. Additional modules of DOM Level 3 add support for content models (DTDs and schemas), as well as support for loading and saving XML into and out of DOM.

18.1. DOM Foundations

At its heart, the DOM is a set of APIs. Various DOM implementations use their own objects to support the interfaces defined in the DOM specification. The DOM interfaces themselves are specified in modules, making it possible for implementations to support parts of the DOM without having to support all of it. XML parsers, for instance, aren't required to provide support for the HTML-specific parts of the DOM, and modularization has provided a simple mechanism that allows software developers to identify which parts of the DOM are supported or are not supported by a particular implementation.

Successive versions of the DOM are defined as levels. The Level 1 DOM was the W3C's first release, and it focused on working with HTML and XML in a browser context. Effectively, it supported dynamic HTML and provided a base for XML document processing. Because it expected documents to exist already in a browser context, Level 1 only described an object structure and how to manipulate it, not how to load a document into that structure or reserialize a document from that structure.

Subsequent levels have added functionality. DOM Level 2, which was published as a set of specifications, one per module, includes updates for the Core and HTML modules of Level 1, as well as new modules for Views, Events, Style, Traversal, and Range. DOM Level 3 will add Abstract Schemas, Load, Save, XPath, and updates to the Core and Events modules.

Other W3C specifications have defined extensions to the DOM particular to their own needs. Mathematical Markup Language (MathML), Scalable Vector Graphics (SVG), Synchronized Multimedia Integration Language (SMIL), and SMIL Animation have all defined DOMs that provide access to details of their own vocabularies.

TIP: For a complete picture of the requirements that all of these modules are supposed to address, see http://www.w3.org/TR/DOM-Requirements. For a listing of all of the DOM specifications, including those still in progress, see http://www.w3.org/DOM/DOMTR. The DOM has also been included by reference in a variety of other specifications, notably the Java API for XML Processing (JAXP).

Developers using the DOM for XML processing typically rely on the Core module as the foundation for their work.

18.1.1. DOM Notation

The Document Object Model is intended to be operating system- and language- neutral; therefore, all DOM interfaces are specified using the Interface Description Language (IDL) notation defined by the Object Management Group organization (http://www.omg.org). To conform to the language of the specification, this chapter and Chapter 24 will use IDL terminology when discussing interface specifics. For example, the word "attribute" in IDL-speak refers to what would be a member variable in C++. This should not be confused with the XML term "attribute," which is a name-value pair that appears within an element's start-tag.

The language-independent IDL interface must then be translated (according to the rules set down by the OMG) into a specific language binding. Take the following interface, for example:

interface NodeList {
  Node               item(in unsigned long index);
  readonly attribute unsigned long    length;
};

This interface would be expressed as a Java interface like this:

package org.w3c.dom;

   public interface NodeList {
       public Node item(int index);

       public int getLength( );

   }

The same interface would be described for ECMAScript this way:

Object NodeList
   The NodeList object has the following properties:
     length
       This read-only property is of type Number.
   The NodeList object has the following methods:
     item(index)
       This method returns a Node object.
       The index parameter is of type Number.
       Note: This object can also be dereferenced using square
       bracket notation (e.g. obj[1]). Dereferencing with an
       integer index is equivalent to invoking the item method
       with that index.

The tables in this chapter represent the information DOM presents as IDL conveying both the available features and when they became available. DOM implementations vary in their implementation of these features--be sure to check the document of the implementation you choose for details on how precisely it supports the DOM interfaces.

18.1.2. DOM Strengths and Weaknesses

Like all programming tools, the DOM is better for addressing some classes of problems than others. Since the DOM object hierarchy stores references between the various nodes in a document, the entire document must be read and parsed before it is available to a DOM application. This step also demands that the entire document be stored in memory, often with a significant amount of overhead. Some early DOM implementations required many times the original document's size when stored in memory. This memory usage model makes DOM unsuitable for applications that deal with very large documents or have a need to perform some intermediate processing on a document before it has been completely parsed.

However, for applications that require random access to different portions of a document at different times or applications that need to modify the structure of an XML document on the fly, DOM is one of the most mature and best-supported technologies available.

Previous | Home | Next
17.2. Common XML Processing Issues | Book Index | 18.2. Structure of the DOM Core
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Structure of the DOM Core (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

18.2. Structure of the DOM Core

The DOM Core interfaces provide generic access to all supported document content types. For example, the DOM defines a set of HTML-specific interfaces that expose specific document structures, such as tables, paragraphs, and <img> elements, directly. Besides using these specialized interfaces, you can access the same information using the generic interfaces defined in the core.

Since XML is designed as a venue for creating new, unique, structured markup languages, standards bodies cannot define application-specific interfaces in advance. Instead, the DOM Core interfaces are provided to manipulate document elements in a completely application-independent manner.

The DOM Core is further segregated into the Fundamental and Extended Interfaces. The Fundamental Interfaces are relevant to both XML and HTML documents, whereas the Extended Interfaces deal with XML-only document structures, such as entity declarations and processing instructions. All DOM Core interfaces are derived from the Node interface, which provides a generic set of interfaces for accessing a document or document fragment's structure and content.

18.2.1. Generic Versus Specific DOM Interfaces

To simplify different types of document processing and enable efficient implementation of DOM by some programming languages, there are actually two distinct methods for accessing a document tree from within the DOM Core: through the generic Node interface and through specific interfaces for each node type. Although there are several distinct types of markup that may appear within an XML document (elements, attributes, processing instructions, and so on), the relationships between these different document features can be expressed as a typical hierarchical tree structure. Elements are linked to both their predecessors and successors, as well as their parent and child nodes. Although there are many different types of nodes, the basic parent, child, and sibling relationships are common to everything in an XML document.

The generic Node interface captures the minimal set of attributes and methods that are required to express this tree structure. A given Node contains all of the tree pointers required to locate its parent node, child nodes, and siblings. The next section describes the Node interface in detail.

In addition to the generic Node interface, the DOM also defines a set of XML-specific interfaces that represent distinct document features, such as elements, attributes, processing instructions, and so on. All of the specific interfaces are derived from the generic Node interface, which means that a particular application can switch methods for accessing data within a DOM tree at will by casting between the generic Node interface and the actual specific object type it represents. Section 18.4 later in this chapter discusses the specific interfaces and their relationship to the generic Node interface.

Previous | Home | Next
18. Document Object Model | Book Index | 18.3. Node and Other Generic Interfaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Node and Other Generic Interfaces (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

18.3. Node and Other Generic Interfaces

The Node interface is the DOM Core class hierarchy's root. Though never instantiated directly, it is the root interface of all specific interfaces, and you can use it to extract information from any DOM object without knowing its actual type. It is possible to access a document's complete structure and content using only the methods and properties exposed by the Node interface. As shown in Table 18-1, this interface contains information about the type, location, name, and value of the corresponding underlying document data.

Table 18-1. Node interface

Type |  | Name | Read-only | DOM 2.0
Attributes |  |  |  |
DOMString |  | nodeName | Figure |
DOMString |  | nodeValue |  |
Short |  | Unsigned type | Figure |
Node |  | parentNode | Figure |
NodeList |  | childNodes | Figure |
Node |  | firstChild | Figure |
Node |  | lastChild | Figure |
Node |  | previousSibling | Figure |
Node |  | nextSibling | Figure |
NamedNodeMap |  | attributes | Figure |
Document |  | ownerDocument | Figure | Figure
DOMString |  | namespaceURI | Figure | Figure
DOMString |  | Prefix |  | Figure
DOMString |  | localName | Figure | Figure
Methods |  |  |  |
Boolean |  | hasAttributes |  | Figure
Node |  | insertBefore |  |
 | Node | newChild |  |
 | Node | refChild |  |
Node |  | replaceChild |  |
 | Node | newChild |  |
 | Node | oldChild |  |
Node |  | removeChild |  |
 | Node | oldChild |  |
Node |  | appendChild |  |
 | Node | newChild |  |
Boolean |  | hasChildNodes |  |
Node |  | cloneNode |  |
 | Boolean | Deep |  |
Void |  | normalize |  | Figure
Boolean |  | isSupported |  | Figure
 | DOMString | Feature |  | Figure
 | DOMString | Version |  | Figure

Since the Node interface is never instantiated directly, the nodeType attribute contains a value that indicates the given instance's specific object type. Based on the nodeType, it is possible to cast a generic Node reference safely to a specific interface for further processing. Table 18-2 shows the node type values and their corresponding DOM interfaces, and Table 18-3 shows the values they provide for nodeName, nodeValue, and attributes attributes.

Table 18-2. DOM node types and interfaces

Node type | DOM interface
ATTRIBUTE_NODE | Attr
CDATA_SECTION_NODE | CDATASection
COMMENT_NODE | Comment
DOCUMENT_FRAGMENT_NODE | DocumentFragment
DOCUMENT_NODE | Document
DOCUMENT_TYPE_NODE | DocumentType
ELEMENT_NODE | Element
ENTITY_NODE | Entity
ENTITY_REFERENCE_NODE | EntityReference
NOTATION_NODE | Notation
PROCESSING_INSTRUCTION_NODE | ProcessingInstruction
TEXT_NODE | Text

Table 18-3. DOM node types and method results

Node type | nodeName | nodeValue | Attributes
ATTRIBUTE_NODE | att name | att value | null
CDATA_SECTION_NODE | #cdata-section | content | null
COMMENT_NODE | #comment | content | null
DOCUMENT_FRAGMENT_NODE | #document-fragment | null | null
DOCUMENT_NODE | #document | null | null
DOCUMENT_TYPE_NODE | document type name | null | null
ELEMENT_NODE | tag name | null | NamedNodeMap
ENTITY_NODE | entity name | null | null
ENTITY_REFERENCE_NODE | name of entity referenced | null | null
NOTATION_NODE | notation name | null | null
PROCESSING_INSTRUCTION_NODE | target | content excluding the target | null
TEXT_NODE | #text | content | null

Note that the nodeValue attribute returns the contents of simple text and comment nodes, but returns nothing for elements. Retrieving the text of an element requires inspecting the text nodes it contains.

18.3.1. The NodeList Interface

The NodeList interface provides access to the ordered content of a node. Most frequently, it is used to retrieve text nodes and child elements of element nodes. See Table 18-4 for a summary of the NodeList interface.

Table 18-4. NodeList interface

Type |  | Name | Read-only | DOM 2.0
Attributes |  |
Long |  | length | Figure |
Methods |  |  |  |
Node |  | item |  |
 | Long | index |  |

The NodeList interface is extremely basic and is generally combined with a loop to iterate over the children of a node.

18.3.2. The NamedNodeMap Interface

The NamedNodeMap interface is used for unordered collections whose contents are identified by name. In practice, this interface is used to access attributes. See Table 18-5 for a summary of the NamedNodeMap interface.

Table 18-5. NamedNodeMap interface

Type |  | Name | Read-only | DOM 2.0
Attributes |  |  |  |
Long |  | length | Figure |
Methods |  |  |  |
Node |  | getNamedItem |  |
 | DOMString | name |  |
Node |  | setNamedItem |  |
 | Node | arg |  |
Node |  | removeNamedItem |  |
 | DOMString | name |  |
Node |  | getNamedItemNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure
Node |  | setNamedItemNS |  | Figure
 | Node | arg |  | Figure
Node |  | removeNamedItemNS |  |
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure

18.3.3. Relating Document Structure to Nodes

Although the DOM doesn't specify an interface to cause a document to be parsed, it does specify how the document's syntax structures are encoded as DOM objects. A document is stored as a hierarchical tree structure, with each item in the tree linked to its parent, children, and siblings:

<sample bogus="value"><text_node>Test data.</text_node></sample>

Figure 18-1 shows how the preceding short sample document would be stored by a DOM parser.

Figure 18-1. Document storage and linkages

Each Node-derived object in a parsed DOM document contains references to its parent, child, and sibling nodes. These references make it possible for applications to enumerate document data using any number of standard tree-traversal algorithms. "Walking the tree" is a common approach to finding information stored in a DOM and is demonstrated in Example 18-1 at the end of this chapter.

Previous | Home | Next
18.2. Structure of the DOM Core | Book Index | 18.4. Specific Node-Type Interfaces
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Specific Node-Type Interfaces (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

18.4. Specific Node-Type Interfaces

Though it is possible to access the data from the original XML document using only the Node interface, the DOM Core provides a number of specific node-type interfaces that simplify common programming tasks. These specific node types can be divided into two broad types: structural nodes and content nodes.

18.4.1. Structural Nodes

Within an XML document, a number of syntax structures exist that are not formally part of the content. The following interfaces provide access to the portions of the document that are not related to character or element data.

18.4.1.1. DocumentType

The DocumentType interface provides access to the XML document type definition's notations, entities, internal subset, public ID, and system ID. Since a document can have only one !DOCTYPE declaration, only one DocumentType node can exist for a given document. It is accessed via the doctype attribute of the Document interface. The definition of the DocumentType interface is shown in Table 18-6.

Table 18-6. DocumentType interface, derived from Node

Type | Name | Read-only | DOM 2.0
Attributes |  |  |
NamedNodeMap | entities | Figure |
DOMString | name | Figure |
NamedNodeMap | notations | Figure |
DOMString | publicId | Figure | Figure
DOMString | systemId | Figure | Figure

Using additional fields available from DOM Level 2, it is now possible to fully reconstruct a parsed document using only the information provided with the DOM framework. No programmatic way to modify DocumentType node contents currently exists.

18.4.1.2. ProcessingInstruction

This node type provides direct access to an XML name processing instruction's contents. Though processing instructions appear in the document's text, they may also appear before or after the root element, as well as in DTDs. Table 18-7 describes the ProcessingInstruction node's attributes.

Table 18-7. ProcessingInstruction interface, derived from Node

Type | Name | Read-only | DOM 2.0
Attributes |  |  |
DOMString | data |  |
DOMString | target | Figure |

Though processing instructions resemble normal XML tags, remember that the only syntactically defined part is the target name, which is an XML name token. The remaining data (up to the terminating >) is free-form. See Chapter 17 for more information about uses (and potential misuses) of XML processing instructions.

18.4.1.3. Notation

XML notations formally declare the format for external unparsed entities and processing instruction targets. The list of all available notations is stored in a NamedNodeMap within the document's DOCTYPE node, which is accessed from the Document interface. The definition of the Notation interface is shown in Table 18-8.

Table 18-8. Notation interface, derived from Node

Type | Name | Read-only | DOM 2.0
Attributes |  |  |
DOMString | publicId | Figure |
DOMString | systemId | Figure |

18.4.1.4. Entity

The name of the Entity interface is somewhat ambiguous, but its meaning becomes clear when it is connected with the EntityReference interface, which is also part of the DOM Core. The Entity interface provides access to the entity declaration's notation name, public ID, and system ID. Parsed entity nodes have childNodes, while unparsed entities have a notationName. The definition of this interface is shown in Table 18-9.

Table 18-9. Entity interface, derived from Node

Type | Name | Read-only | DOM 2.0
Attributes |  |  |
DOMString | notationName | Figure |
DOMString | publicId | Figure |
DOMString | systemId | Figure |

All members of this interface are read-only and cannot be modified at runtime.

18.4.2. Content Nodes

The actual data conveyed by an XML document is contained completely within the document element. The following node types map directly to the XML document's nonstructural parts, such as character data, elements, and attribute values.

18.4.2.1. Document

Each parsed document causes the creation of a single Document node in memory. (Empty Document nodes can be created through the DOMImplementation interface.) This interface provides access to the document type information and the single, top-level Element node that contains the entire body of the parsed document. It also provides access to the class factory methods that allow an application to create new content nodes that were not created by parsing a document. Table 18-10 shows all attributes and methods of the Document interface.

Table 18-10. Document interface, derived from Node

Type |  | Name | Read-only | DOM 2.0
Attributes |  |  |  |
DocumentType |  | doctype | Figure |
DOMImplementation |  | implementation | Figure |
Element |  | documentElement | Figure |
Methods |  |  |
Attr |  | createAttribute |  |
 | DOMString | name |  |
Attr |  | createAttributeNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | qualifiedName |  | Figure
CDATASection |  | createCDATASection |  |
 | DOMString | data |  |
Comment |  | createComment |  |
 | DOMString | data |  |
DocumentFragment |  | createDocumentFragment |  |
Element |  | createElement |  |
 | DOMString | tagName |  |
Element |  | createElementNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | qualifiedName |  | Figure
EntityReference |  | createEntityReference |  |
 | DOMString | name |  |
ProcessingInstruction |  | createProcessingInstruction |  |
 | DOMString | target |  |
 | DOMString | data |  |
Text |  | createTextNode |  |
 | DOMString | data |  |
Element |  | getElementById |  | Figure
 | DOMString | elementId |  |
NodeList |  | getElementsByTagName |  |
 | DOMString | tagname |  |
NodeList |  | getElementsByTagNameNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure
Node |  | importNode |  | Figure
 | Node | importedNode |  | Figure
 | Boolean | deep |  | Figure

The various create...( ) methods are important for applications that wish to modify the structure of a document that was previously parsed. Note that nodes created using one Document instance may only be inserted into the document tree belonging to the Document that created them. DOM Level 2 provides a new importNode( ) method that allows a node, and possibly its children, to be essentially copied from one document to another.

Besides the various node-creation methods, some methods can locate specific XML elements or lists of elements. The getElementsByTagName( ) and getElementsByTagNameNS( ) methods return a list of all XML elements with the name, and possibly namespace, specified. The getElementById( ) method returns the single element with the given ID attribute.

18.4.2.2. DocumentFragment

Applications that allow real-time editing of XML documents sometimes need to temporarily park document nodes outside the hierarchy of the parsed document. A visual editor that wants to provide clipboard functionality is one example. When the time comes to implement the cut function, it is possible to move the cut nodes temporarily to a DocumentFragment node without deleting them, rather than having to leave them in place within the live document. Then when they need to be pasted back into the document, they can be moved back. The DocumentFragment interface, derived from Node, has no interface-specific attributes or methods.

18.4.2.3. Element

Element nodes are the most frequently encountered node type in a typical XML document. These nodes are parents for the Text, Comment, EntityReference, ProcessingInstruction, CDATASection, and child Element nodes that comprise the document's body. They also allow access to the Attr objects that contain the element's attributes. Table 18-11 shows all attributes and methods supported by the Element interface.

Table 18-11. Element interface, derived from Node

Type |  | Name | Read-only | DOM 2.0
Attributes |  |  |  |
DOMString |  | tagName | Figure |
Methods |  |  |  |
DOMString |  | getAttribute |  |
 | DOMString | name |  |
Attr |  | getAttributeNode |  |
 | DOMString | name |  |
Attr |  | getAttributeNodeNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure
DOMString |  | getAttributeNS | Figure |
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure
NodeList |  | getElementsByTagName |  |
 | DOMString | name |  |
NodeList |  | getElementsByTagNameNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure
Boolean |  | hasAttribute | Figure |
 | DOMString | name |  | Figure
Boolean |  | hasAttributeNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure
Void |  | removeAttribute |  |
 | DOMString | name |  |
Attr |  | removeAttributeNode |  |
 | Attr | oldAttr |  |
Attr |  | removeAttributeNS |  | Figure
 | DOMString | namespaceURI |  | Figure
 | DOMString | localName |  | Figure
Void |  | setAttribute |  |
 | DOMString | name |  |
Attr |  | setAttributeNode |  |
 | Attr | newAttr |  |
Attr |  | setAttributeNodeNS |  |
 | Attr | newAttr |  |
Attr |  | setAttributeNS | Figure |
 | DOMString | namespaceURI |  | Figure
 | DOMString | qualifiedName |  | Figure
 | DOMString | value |  | Figure

18.4.2.4. Attr

Since XML attributes may contain either text values or entity references, the DOM stores element attribute values as Node subtrees. The following XML fragment shows an element with two attributes:

<!ENTITY bookcase_pic SYSTEM "bookcase.gif" NDATA gif>
<!ELEMENT picture EMPTY>
<!ATTLIST picture
   src ENTITY #REQUIRED
   alt CDATA #IMPLIED>
. . .
<picture src="bookcase_pic" alt="3/4 view of bookcase"/>

The first attribute contains a reference to an unparsed entity; the second contains a simple string. Since the DOM framework stores element attributes as instances of the Attr interface, a few parsers make the contents of attributes available as actual subtrees of Node objects. In this example, the src attribute would contain an EntityReference object instance. Note that the nodeValue of the Attr node gives the flattened text value from the Attr node's children. Table 18-12 shows the attributes and methods supported by the Attr interface.

Table 18-12. Attr interface, derived from Node

Type | Name | Read-only | DOM 2.0
Attributes |  |  |
DOMString | name | Figure |
Element | ownerElement | Figure | Figure
Boolean | specified | Figure |
DOMString | value |  |

Besides the attribute name and value, the Attr interface exposes the specified flag that indicates whether this particular attribute instance was included explicitly in the XML document or inherited from the !ATTLIST declaration of the DTD. There is also a back pointer to the Element node that owns this attribute object.

18.4.2.5. CharacterData

Several types of data within a DOM node tree represent blocks of character data that do not include markup. CharacterData is an abstract interface that supports common text-manipulation methods that are used by the concrete interfaces Comment, Text, and CDATASection. Table 18-13 shows the attributes and methods supported by the CharacterData interface.

Table 18-13. CharacterData interface, derived from Node

Type |  | Name | Read-only | DOM 2.0
Attributes |  |  |  |
DOMString |  | data |  |
Unsigned long |  | length | Figure |
Methods |  |  |  |
Void |  | appendData |  |
 | DOMString | arg |  |
Void |  | deleteData |  |
 | Unsigned long | offset |  |
 | Unsigned long | count |  |
Void |  | insertData |  |
 | Unsigned long | offset |  |
 | DOMString | arg |  |
Void |  | replaceData |  |
 | Unsigned long | offset |  |
 | Unsigned long | count |  |
 | DOMString | arg |  |

18.4.2.6. Comment

DOM parsers are not required to make the contents of XML comments available after parsing, and relying on comment data in your application is poor programming practice at best. If your application requires access to metadata that should not be part of the basic XML document, consider using processing instructions instead. The Comment interface, derived from CharacterData, has no interface-specific attributes or methods.

18.4.2.7. EntityReference

If an XML document contains references to general entities within the body of its elements, the DOM-compliant parser may pass these references along as EntityReference nodes. This behavior is not guaranteed because the parser is free to expand any entity or character reference included with the actual Unicode character sequence it represents. The EntityReference interface, derived from Node, has no interface-specific attributes or methods.

18.4.2.8. Text

The character data of an XML document is stored within Text nodes. Text nodes are children of either Element or Attr nodes. After parsing, every contiguous block of character data from the original XML document is translated directly into a single Text node. Once the document has been parsed, however, it is possible that the client application may insert, delete, and split Text nodes so that Text nodes may be side by side within the document tree. Table 18-14 describes the Text interface.

Table 18-14. Text interface, derived from CharacterData

Type |  | Name | DOM 2.0
Methods |  |  |
Text |  | splitText |
 | Unsigned long | offset |

The splitText method provides a way to split a single Text node into two nodes at a given point. This split would be useful if an editing application wished to insert additional markup nodes into an existing island of character data. After the split, it is possible to insert additional nodes into the resulting gap.

18.4.2.9. CDATASection

CDATA sections provide a simplified way to include characters that would normally be considered markup in an XML document. These sections are stored within a DOM document tree as CDATASection nodes. The CDATASection interface, derived from Text, has no interface-specific attributes or methods.

Previous | Home | Next
18.3. Node and Other Generic Interfaces | Book Index | 18.5. The DOMImplementation Interface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The DOMImplementation Interface (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

18.5. The DOMImplementation Interface

This interface could be considered the highest level interface in the DOM. It exposes the hasFeature( ) method, which allows a programmer using a given DOM implementation to detect if specific features are available. In DOM Level 2, it also provides facilities for creating new DocumentType nodes, which can then be used to create new Document instances. Table 18-15 describes the DomImplementation interface.

Table 18-15. DOMImplementation interface

Type |  | Name | DOM 2.0
Methods |  |  |
Document |  | createDocument | Figure
 | DOMString | namespaceURI | Figure
 | DOMString | qualifiedName | Figure
 | DocumentType | doctype | Figure
DocumentType |  | createDocumentType | Figure
 | DOMString | qualifiedName | Figure
 | DOMString | publicId | Figure
 | DOMString | systemId | Figure
Boolean |  | hasFeature |
 | DOMString | feature |
 | DOMString | version |

Previous | Home | Next
18.4. Specific Node-Type Interfaces | Book Index | 18.6. Parsing a Document with DOM
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Parsing a Document with DOM (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

18.6. Parsing a Document with DOM

Though the DOM standard doesn't specify an actual interface for parsing a document, most implementations provide a simple parsing interface that accepts a reference to an XML document file, stream, or URI. After this interface successfully parses and validates the document (if it is a validating parser), it generally provides a mechanism for getting a reference to the Document interface's instance for the parsed document. The following code fragment shows how to parse a document using the Apache Xerces XML DOM parser:

// create a new parser
DOMParser dp = new DOMParser( );

// parse the document and get the DOM Document interface
dp.parse("http://www.w3.org/TR/2000/REC-xml-20001006.xml");
Document doc = dp.getDocument( );
TIP: DOM Level 3 will be adding standard mechanisms for loading XML documents and reserializing (saving) DOM trees as XML. JAXP also provides standardized approaches for these processes in Java, though JAXP and DOM Level 3 may offer different approaches.

Previous | Home | Next
18.5. The DOMImplementation Interface | Book Index | 18.7. A Simple DOM Application
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Simple DOM Application (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

18.7. A Simple DOM Application

Example 18-1 illustrates how you might use the interfaces discussed in this chapter in a typical programming situation. This application takes a document that uses the furniture.dtd sample DTD from Chapter 20 and validates that the parts list included in the document matches the actual parts used within the document.

Example 18-1. Parts checker application

/**
 * PartsCheck.java
 *
 * DOM Usage example from the O'Reilly _XML in a Nutshell_ book.
 *
 */

// we'll use the Apache Software Foundation's Xerces parser.
import org.apache.xerces.parsers.*;
import org.apache.xerces.framework.*;

// import the DOM and SAX interfaces
import org.w3c.dom.*;
import org.xml.sax.*;

// get the necessary java support classes
import java.io.*;
import java.util.*;

/**
 * This class is designed to check the parts list of an XML document that
 * represents a piece of furniture for validity.  It uses the DOM to
 * analyze the actual furniture description and then check it against the
 * parts list that is embedded in the document.
 */
public class PartsCheck {
  // static constants
  public static final String FURNITURE_NS =
      "http://namespaces.oreilly.com/furniture/";
  // contains the true part count, keyed by part number
  HashMap m_hmTruePartsList = new HashMap( );

  /**
   * The main function that allows this class to be invoked from the command
   * line.  Check each document provided on the command line for validity.
   */
  public static void main(String[] args) {
    PartsCheck pc = new PartsCheck( );

    try {
      for (int i = 0; i < args.length; i++) {
        pc.validatePartsList(args[i]);
      }
    } catch (Exception e) {
      System.err.println(e);
    }
  }

  /**
   * Given a system identifier for an XML document, this function compares
   * the actual parts used to the declared parts list within the document.  It
   * prints warnings to standard error if the lists don't agree.
   */
  public void validatePartsList(String strXMLSysID) throws IOException,
      SAXException
  {
    // create a new parser
    DOMParser dp = new DOMParser( );

    // parse the document and get the DOM Document interface
    dp.parse(strXMLSysID);
    Document doc = dp.getDocument( );

    // get an accurate parts list count
    countParts(doc.getDocumentElement( ), 1);

    // compare it to the parts list in the document
    reconcilePartsList(doc);
  }

  /**
   * Updates the true parts list by adding the count to the current count
   * for the part number given.
   */
  private void recordPart(String strPartNum, int cCount)
  {
    if (!m_hmTruePartsList.containsKey(strPartNum)) {
      // this part isn't listed yet
      m_hmTruePartsList.put(strPartNum, new Integer(cCount));
    } else {
      // update the count
      Integer cUpdate = (Integer)m_hmTruePartsList.get(strPartNum);
      m_hmTruePartsList.put(strPartNum, new Integer(cUpdate.intValue( ) + cCount));
    }
  }

  /**
   * Counts the parts referenced by and below the given node.
   */
  private void countParts(Node nd, int cRepeat)
  {
    // start the local repeat count at 1
    int cLocalRepeat = 1;

    // make sure we should process this element
    if (FURNITURE_NS.equals(nd.getNamespaceURI( ))) {
      Node ndTemp;

      if ((ndTemp = nd.getAttributes( ).getNamedItem("repeat")) != null) {
        // this node specifies a repeat count for its children
        cLocalRepeat = Integer.parseInt(ndTemp.getNodeValue( ));
      }

      if ((ndTemp = nd.getAttributes( ).getNamedItem("part_num")) != null) {
        // start the count at 1
        int cCount = 1;
        String strPartNum = ndTemp.getNodeValue( );

        if ((ndTemp = nd.getAttributes( ).getNamedItem("count")) != null) {
          // more than one part needed by this node
          cCount = Integer.parseInt(ndTemp.getNodeValue( ));
        }

        // multiply the local count by the repeat passed in from the parent
        cCount *= cRepeat;

        // add the new parts count to the total
        recordPart(strPartNum, cCount);
      }
    }

    // now process the children
    NodeList nl = nd.getChildNodes( );
    Node ndCur;

    for (int i = 0; i < nl.getLength( ); i++) {
      ndCur = nl.item(i);

      if (ndCur.getNodeType( ) == Node.ELEMENT_NODE) {
        // recursively count the parts for the child, using the local repeat
        countParts(ndCur, cLocalRepeat);
      }
    }
  }

  /**
   * This method reconciles the true parts list against the list in the document.
   */
  private void reconcilePartsList(Document doc)
  {
    Iterator iReal = m_hmTruePartsList.keySet().iterator( );

    String strPartNum;
    int cReal;
    Node ndCheck;

    // loop through all of the parts in the true parts list
    while (iReal.hasNext( )) {
      strPartNum = (String)iReal.next( );
      cReal = ((Integer)m_hmTruePartsList.get(strPartNum)).intValue( );

      // find the part list element in the document
      ndCheck = doc.getElementById(strPartNum);

      if (ndCheck == null) {
        // this part isn't even listed!
        System.err.println("missing <part_name> element for part #" +
            strPartNum + " (count " + cReal + ")");
      } else {
        Node ndTemp;

        if ((ndTemp = ndCheck.getAttributes( ).getNamedItem("count")) != null) {
          int cCheck = Integer.parseInt(ndTemp.getNodeValue( ));

          if (cCheck != cReal) {
            // counts don't agree
            System.err.println("<part_name> element for part #" +
                strPartNum + " is incorrect:  true part count = " + cReal +
                " (count in document is " + cCheck + ")");
          }
        } else {
          // they didn't provide a count for this part!
          System.err.println("missing count attribute for part #" +
              strPartNum + " (count " + cReal + ")");
        }
      }
    }
  }
}

When this application is run over the bookcase.xml sample document from Chapter 20, it generates the following output:

missing count attribute for part #HC (count 8)

<part_name> element for part #A is incorrect:  true part count = 2 (count in document is 1)

To compile and use this sample application, download and install the Xerces Java Parser from the Apache-XML project (http://xml.apache.org/xerces-j). The code was compiled and tested with Sun's JDK Version 1.3.1.

Previous | Home | Next
18.6. Parsing a Document with DOM | Book Index | 19. Simple API for XML
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Simple API for XML (SAX) (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 19. Simple API for XML (SAX)

Contents:

The ContentHandler Interface
SAX Features and Properties
Filters

The Simple API for XML (SAX) is a straightforward, event-based API for reading XML documents. Many different XML parsers, including Xerces, Crimson, MSXML, the Oracle XML Parser for Java, and Ælfred, implement the SAX API. SAX was originally defined as a Java API and is primarily intended for parsers written in Java. Therefore, this chapter focuses on the Java version of the API. However, SAX has been ported to most other major object-oriented languages, including C++, Python, Perl, and Eiffel. The translation from Java is usually fairly obvious.

The SAX API is unusual among XML APIs because it's an event-based push model rather than a tree-based pull model. As the XML parser reads an XML document, it sends your program information from the document in real time. Each time the parser sees a start-tag, an end-tag, character data, or a processing instruction, it tells your program. The document is presented to your program one piece at a time from beginning to end. You can either save the pieces you're interested in until the entire document has been read or process the information as soon as you receive it. You do not have to wait for the entire document to be read before acting on the data at the beginning of the document. Most importantly, the entire document does not have to reside in memory. This feature makes SAX the API of choice for very large documents that do not fit into available memory.

TIP: This chapter covers SAX2 exclusively. In 2002 all major parsers that support SAX support SAX2. The major change in SAX2 from SAX1 is the addition of namespace support. This addition necessitated changing the names and signatures of almost every method and class in SAX. The old SAX1 methods and classes are still available, but they're now deprecated, and you shouldn't use them.

SAX is primarily a collection of interfaces in the org.xml.sax package. One such interface is XMLReader . This interface represents the XML parser. It declares methods to parse a document and configure the parsing process, for instance, by turning validation on or off. To parse a document with SAX, first create an instance of XMLReader with the XMLReaderFactory class in the org.xml.sax.helpers package. This class has a static createXMLReader( ) factory method that produces the parser-specific implementation of the XMLReader interface. The Java system property org.xml.sax.driver specifies the concrete class to instantiate:

try {
  XMLReader parser = XMLReaderFactory.createXMLReader( );
  // parse the document...
}
catch (SAXException e) {
  // couldn't create the XMLReader
}

The call to XMLReaderFactory.createXMLReader( ) is wrapped in a try-catch block that catches SAXException . This is the generic checked exception superclass for almost anything that can go wrong while parsing an XML document. In this case, it means either that the org.xml.sax.driver system property wasn't set or that it was set to the name of a class that Java couldn't find in the class path.

You can choose which concrete class to instantiate by passing its name as a string to the createXMLReader( ) method. This code fragment instantiates the Xerces parser by name:

try {
  XMLReader parser = XMLReaderFactory.createXMLReader(
   "org.apache.xerces.parsers.SAXParser");
  // parse the document...
}
catch (SAXException e) {
  // couldn't create the XMLReader
}

Now that you've created a parser, you're ready to parse some documents with it. Pass the system ID of the document you want to parse to the parse( ) method. The system ID is either an absolute or a relative URL encoded in a string. For example, this code fragment parses the document at http://www.slashdot.org/slashdot.xml:

try {
  XMLReader parser = XMLReaderFactory.createXMLReader( );
  parser.parse("http://www.slashdot.org/slashdot.xml");
}
catch (SAXParseException e) {
  // Well-formedness error
}
catch (SAXException e) {
  // Could not find an XMLReader implementation class
}
catch (IOException e) {
  // Some sort of I/O error prevented the document from being completely
  // downloaded from the server
}

The parse( ) method throws a SAXParseException if the document is malformed, an IOException if an I/O error such as a broken socket occurs while the document is being read, and a SAXException if anything else goes wrong. Otherwise, it returns void. To receive information from the parser as it reads the document, you must configure it with a ContentHandler.

19.1. The ContentHandler Interface

ContentHandler, shown in stripped-down form in Example 19-1, is an interface in the org.xml.sax package. You implement this interface in a class of your own devising. Next, you configure an XMLReader with an instance of your implementation. As the XMLReader reads the document, it invokes the methods in your object to tell your program what's in the XML document. You can respond to these method invocations in any way you see fit.

TIP: This class has no relation to the moribund java.net.ContentHandler class. However, you may encounter a name conflict if you import both java.net.* and org.xml.sax.* in the same class. It's better to import just the java.net classes you actually need, rather than the entire package.

Example 19-1. The org.xml.sax.ContentHandler Interface

package org.xml.sax;

public interface ContentHandler {
    public void setDocumentLocator(Locator locator);
    public void startDocument( ) throws SAXException;
    public void endDocument( ) throws SAXException;
    public void startPrefixMapping(String prefix, String uri)
     throws SAXException;
    public void endPrefixMapping(String prefix) throws SAXException;
    public void startElement(String namespaceURI, String localName,
     String qualifiedName, Attributes atts) throws SAXException;
    public void endElement(String namespaceURI, String localName,
     String qualifiedName) throws SAXException;
    public void characters(char[] text, int start, int length)
     throws SAXException;
    public void ignorableWhitespace(char[] text, int start, int length)
     throws SAXException;
    public void processingInstruction(String target, String data)
     throws SAXException;
    public void skippedEntity(String name) throws SAXException;

}

Every time the XMLReader reads a piece of the document, it calls a method in its ContentHandler. Suppose a parser reads the simple document shown in Example 19-2.

Example 19-2. A simple XML document

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>

The parser will call these methods in its ContentHandler with these arguments in this order. The values of the arguments passed to each method are given after each method name:

  1. setDocumentLocator(Locator locator)
locator: org.apache.xerces.readers.DefaultEntityHandler@1f953d
  2. startDocument( )
  3. processingInstruction(String target, String data)
target: "xml-stylesheet"
data: "type='text/css' href='person.css'"
  4. startPrefixMapping(String prefix, String namespaceURI)
prefix: ""
namespaceURI: "http://xml.oreilly.com/person"
  5. startElement(String namespaceURI, String localName,
String qualifiedName, Attributes atts)
namespaceURI: "http://xml.oreilly.com/person"
localName: "person"
qualifiedName: "person"
atts: {} (no attributes, an empty list)
  6. ignorableWhitespace(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 181
length: 3
  7. startPrefixMapping(String prefix, String uri)
prefix: "name"
uri: "http://xml.oreilly.com/name")
  8. startElement(String namespaceURI, String localName,
String qualifiedName, Attributes atts)
namespaceURI: "http://xml.oreilly.com/name"
localName: "name"
qualifiedName: "name:name"
atts: {} (no attributes, an empty list)
  9. ignorableWhitespace(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 236
length: 5
  10. startElement(String namespaceURI, String localName,
String qualifiedName, Attributes atts)
namespaceURI: "http://xml.oreilly.com/name"
localName: "first"
qualifiedName: "name:first"
atts: {} (no attributes, an empty list)
  11. characters(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 253
length: 6
  12. endElement(String namespaceURI, String localName, String qualifiedName)
namespaceURI: "http://xml.oreilly.com/name"
localName: "first"
qualifiedName: "name:first"
  13. ignorableWhitespace(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 272
length: 5
  14. startElement(String namespaceURI, String localName, String qualifiedName, Attributes atts)
namespaceURI: "http://xml.oreilly.com/name"
localName: "last"
qualifiedName: "name:last"
atts: {} (no attributes, an empty list)
  15. characters(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 288
length: 3
  16. endElement(String namespaceURI, String localName, String qualifiedName)
namespaceURI: "http://xml.oreilly.com/name"
localName: "last"
qualifiedName: "name:last"
  17. ignorableWhitespace(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 303
length: 3
  18. endElement(String namespaceURI, String localName, String qualifiedName)
namespaceURI: "http://xml.oreilly.com/name"
localName: "name"
qualifiedName: "name:name"
  19. endPrefixMapping(String prefix)
prefix: "name"
  20. ignorableWhitespace(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 318
length: 3
  21. startElement(String namespaceURI, String localName, String qualifiedName, Attributes atts)
namespaceURI: "http://xml.oreilly.com/person"
localName: "assignment"
qualifiedName: "assignment
atts: {project_id="p2"}
  22. endElement(String namespaceURI, String localName, String qualifiedName)
namespaceURI: "http://xml.oreilly.com/person"
localName: "assignment"
qualifiedName: "assignment"
  23. ignorableWhitespace(char[] text, int start, int length)
text: <?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type='text/css' href='person.css'?>
<!DOCTYPE person SYSTEM "person.dtd">
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"/>
</person>
start: 350
length: 1
  24. endElement(String namespaceURI, String localName, String qualifiedName)
namespaceURI: "http://xml.oreilly.com/person"
localName: "person"
qualifiedName: "person"
  25. endPrefixMapping(String prefix)
prefix: ""
  26. endDocument( )

Some pieces of this are not deterministic. Note that the char array passed to each call to characters( ) and ignorableWhitespace( ) actually contains the entire document! The specific text block that the parser really returns is indicated by the second two arguments. This is an optimization that Xerces-J performs. Other parsers are free to pass different char arrays as long as they set the start and length arguments to match. Indeed, the parser is also free to split a long run of plain text across multiple calls to characters( ) or ignorableWhitespace( ), so you cannot assume that these methods necessarily return the longest possible contiguous run of plain text. Other details that may change from parser to parser include attribute order within a tag and whether a Locator object is provided by calling setDocumentLocator( ).

Suppose you want to count the number of elements, attributes, processing instructions, and characters of plain text that exist in a given XML document. To do so, first write a class that implements the ContentHandler interface. The current count of each of the four items of interest is stored in a field. The field values are initialized to zero in the startDocument( ) method, which is called exactly once for each document parsed. Each callback method in the class increments the relevant field. The endDocument( ) method reports the total for that document. Example 19-3 is such a class.

Example 19-3. The XMLCounter ContentHandler

import org.xml.sax.*;

public class XMLCounter implements ContentHandler {

  private int numberOfElements;
  private int numberOfAttributes;
  private int numberOfProcessingInstructions;
  private int numberOfCharacters;

  public void startDocument( ) throws SAXException {
    numberOfElements = 0;
    numberOfAttributes = 0;
    numberOfProcessingInstructions = 0;
    numberOfCharacters = 0;
  }

  // We should count either the start-tag of the element or the end-tag,
  // but not both. Empty elements are reported by each of these methods.
  public void startElement(String namespaceURI, String localName,
   String qualifiedName, Attributes atts) throws SAXException {
    numberOfElements++;
    numberOfAttributes += atts.getLength( );
  }

  public void endElement(String namespaceURI, String localName,
   String qualifiedName) throws SAXException {}

  public void characters(char[] text, int start, int length)
   throws SAXException {
    numberOfCharacters += length;
  }

  public void ignorableWhitespace(char[] text, int start, int length)
   throws SAXException {
    numberOfCharacters += length;
  }

  public void processingInstruction(String target, String data)
   throws SAXException {
    numberOfProcessingInstructions++;
  }

  // Now that the document is done, we can print out the final results
  public void endDocument( ) throws SAXException {
    System.out.println("Number of elements: " + numberOfElements);
    System.out.println("Number of attributes: " + numberOfAttributes);
    System.out.println("Number of processing instructions: "
     + numberOfProcessingInstructions);
    System.out.println("Number of characters of plain text: "
     + numberOfCharacters);
  }

  // Do-nothing methods we have to implement only to fulfill
  // the interface requirements:
  public void setDocumentLocator(Locator locator) {}
  public void startPrefixMapping(String prefix, String uri)
   throws SAXException {}
  public void endPrefixMapping(String prefix) throws SAXException {}
  public void skippedEntity(String name) throws SAXException {}

}
TIP: This class needs to override most methods in the ContentHandler interface. However, if you only really want to provide one or two ContentHandler methods, you may want to subclass the DefaultHandler class instead. This adapter class implements all methods in the ContentHandler interface with do-nothing methods, so you only have to override methods in which you're genuinely interested.

Next, build an XMLReader, and configure it with an instance of this class. Finally, parse the documents you want to count, as in Example 19-4.

Example 19-4. The DocumentStatistics driver class

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.IOException;

public class DocumentStatistics {

  public static void main(String[] args) {

    XMLReader parser;
    try {
     parser = XMLReaderFactory.createXMLReader( );
    }
    catch (SAXException e) {
      // fall back on Xerces parser by name
      try {
        parser = XMLReaderFactory.createXMLReader(
         "org.apache.xerces.parsers.SAXParser");
      }
      catch (SAXException ee) {
        System.err.println("Couldn't locate a SAX parser");
        return;
      }
    }

    if (args.length == 0) {
      System.out.println(
       "Usage: java DocumentStatistics URL1 URL2...");
    }

    // Install the Content Handler
    parser.setContentHandler(new XMLCounter( ));

    // start parsing...
    for (int i = 0; i < args.length; i++) {

      // command line should offer URIs or file names
      try {
        parser.parse(args[i]);
      }
      catch (SAXParseException e) { // well-formedness error
        System.out.println(args[i] + " is not well formed.");
        System.out.println(e.getMessage( )
         + " at line " + e.getLineNumber( )
         + ", column " + e.getColumnNumber( ));
      }
      catch (SAXException e) { // some other kind of error
        System.out.println(e.getMessage( ));
      }
      catch (IOException e) {
        System.out.println("Could not report on " + args[i]
         + " because of the IOException " + e);
      }

    }

  }

}

Running the program in Example 19-4 across the document in Example 19-2 results in the following output:

D:\books\xian\examples\18>java DocumentStatistics 18-2.xml
Number of elements: 5
Number of attributes: 1
Number of processing instructions: 1
Number of characters of plain text: 29

This generic program of Example 19-4 works on any well-formed XML document. Most SAX programs are more specific and only work with certain XML applications. They look for particular elements or attributes in particular places and respond to them accordingly. They may rely on patterns that are enforced by a validating parser. Still, this behavior comprises the fundamentals of SAX.

The complicated part of most SAX programs is the data structure you must build to store information returned by the parser until you're ready to use it. Sometimes this information can be as complicated as the XML document itself, in which case you may be better off using DOM, which at least provides a ready-made data structure for an XML document. You usually want only some information, though, and the data structure you construct should be less complex than the document itself.

Previous | Home | Next
18.7. A Simple DOM Application | Book Index | 19.2. SAX Features and Properties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX Features and Properties (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

19.2. SAX Features and Properties

SAX uses properties and features to control parser behavior. Each feature and property has a name that's an absolute URI. Like namespace URIs, these URIs are only used to name things and do not necessarily point to a real page you can load into a web browser. Features are either true or false; that is, they're Booleans. Properties have values of an appropriate Object type.

The http://xml.org/sax/features/validation feature controls whether a parser validates. If this feature is true, then the parser will report validity errors in the document to the registered ErrorHandler; otherwise, it won't. This feature is turned off by default. To turn a feature on, pass the feature's name and value to the XMLReader's setFeature( ) method:

try {
  parser.setFeature("http://xml.org/sax/features/validation", true);
}
catch (SAXNotSupportedException e) {
  System.out.println("Cannot turn on validation right now.");
}
catch (SAXNotRecognizedException e) {
  System.out.println("This is not a validating parser.");
}

Not all parsers can validate. If you try to turn on validation in a parser that doesn't validate or set any other feature the parser doesn't provide, setFeature( ) throws a SAXNotRecognizedException. If you try to set a feature the parser does recognize but cannot change at the current time--e.g., you try to turn on validation when the parser has already read half of the document--setFeature( ) throws a SAXNotSupportedException. Both are subclasses of SAXException.

You can check a feature's current value using XMLReader's getFeature( ) method. This method returns a boolean and throws the same exceptions for the same reasons as setFeature( ). If you want to know whether the parser validates, you can ask in the following manner:

try {
  boolean isValidating =
   parser.getFeature("http://xml.org/sax/features/validation");
}
catch (SAXException e) {
  System.out.println("This is not a validating parser");
}

Properties are similar to features, but they allow a broader choice than a simple Boolean on/off, true/false dichotomy. Each property value is an object of unspecified type. For example, if you want to know the literal string of data parsed to produce the current SAX event, you can ask by reading the http://xml.org/sax/properties/xml-string property with the getProperty( ) method:

try {
  String tag = (String) parser.getProperty(
   "http://xml.org/sax/properties/xml-string");
}
catch (SAXNotSupportedException e) {
  System.out.println("This parser does not provide the original data");
}
catch (SAXNotRecognizedException e) {
  System.out.println("Parser does not recognize the " +
   "http://xml.org/sax/properties/xml-string property");
}

You can change a property value by invoking the setProperty( ) method with two arguments. The first is the URI of the property to set. The second is the object specifying the value for the property. For example, this code fragment attempts to set the http://xml.org/sax/properties/LexicalHandler property to a new instance of the MyLexicalHandlerClass. The parser reports lexical events (comments, CDATA sections, and entity references) to the org.xml.sax.ext.LexicalHandler implementation object named by this property:

try {
  parser.setProperty(
    "http://xml.org/sax/properties/LexicalHandler",
    new MyLexicalHandlerClass( )
  );
}
catch (SAXException e) {
  System.out.println("This parser does not provide lexical events.");
}

If you pass in the wrong kind of object for a property (e.g., an object that does not implement the LexicalHandler interface for the http://xml.org/sax/properties/LexicalHandler property), then setProperty( ) throws a SAXNotSupportedException.

Not all features and properties can be set at all times. For example, you cannot suddenly decide to start validating when the parser is already halfway through a document. An attempt to do so will fail and throw a SAXNotSupportedException. However, you can change a parser's features in between documents -- after parsing one document, but before parsing the next. You can read most feature and property values at any time.

Previous | Home | Next
19. Simple API for XML | Book Index | 19.3. Filters
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Filters (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

19.3. Filters

A SAX filter sits in between the parser and the client application and intercepts the messages that these two objects pass to each other. It can pass these messages unchanged or modify, replace, or block them. To a client application, the filter looks like a parser, that is, an XMLReader. To the parser, the filter looks like a client application, that is, a ContentHandler.

SAX filters are implemented by subclassing the org.xml.sax.helpers.XMLFilterImpl class.[8] This class implements all the required interfaces of SAX for both parsers and client applications. That is, its signature is as follows:

[8]There's also an org.xml.sax.XMLFilter interface. However, this interface is arranged exactly backwards for most use cases. It filters messages from the client application to the parser, but not the much more important messages from the parser to the client application. Furthermore, implementing the XMLFilter interface directly requires a lot more work than subclassing XMLFilterImpl. Almost no experienced SAX programmer would choose to implement XMLFilter directly rather than subclassing the XMLFilterImpl adapter class.

public class XMLFilterImpl implements XMLFilter, XMLReader,
 ContentHandler, DTDHandler, ErrorHandler

Your own filters will extend this class and override those methods that correspond to the messages you want to filter. For example, if you wanted to filter out all processing instructions, you would write a filter that would override the processingInstruction( ) method to do nothing, as shown in Example 19-5.

Example 19-5. A SAX filter that removes processing instructions

import org.xml.sax.helpers.XMLFilterImpl;

public class ProcessingInstructionStripper extends XMLFilterImpl {

  public void processingInstruction(String target, String data) {
    // Because we do nothing, processing instructions read in the
    // document are *not* passed to client application
  }

}

If instead you wanted to replace a processing instruction with an element whose name was the same as the processing instruction's target and whose text content was the processing instruction's data, you'd call the startElement( ), characters( ), and endElement( ) methods from inside the processingInstruction( ) method after filling in the arguments with the relevant data from the processing instruction, as shown in Example 19-6.

Example 19-6. A SAX filter that converts processing instructions to elements

import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class ProcessingInstructionConverter extends XMLFilterImpl {

  public void processingInstruction(String target, String data)
   throws SAXException {

    // AttributesImpl is an adapter class in the org.xml.sax.ext package
    // for precisely this case. We don't really want to add any attributes
    // here, but we need to pass something as the fourth argument to
    // startElement( ).
    Attributes emptyAttributes = new AttributesImpl( );

    // We won't use any namespace for the element
    startElement("", target, target, emptyAttributes);
    // converts String data to char array
    char[] text = data.toCharArray( );
    characters(text, 0, text.length);

    endElement("", target, target);

  }

}

We used this filter before passing Example 19-2 into a program that echoes an XML document onto System.out and were a little surprised to see this come out:

<xml-stylesheet>type="text/css" href="person.css"</xml-stylesheet>
<person xmlns="http://xml.oreilly.com/person">
  <name:name xmlns:name="http://xml.oreilly.com/name">
    <name:first>Sydney</name:first>
    <name:last>Lee</name:last>
  </name:name>
  <assignment project_id="p2"></assignment>
</person>

This document is not well-formed! The specific problem is that there are two independent root elements. However, on further consideration that's really not too surprising. Well-formedness checking is normally done by the underlying parser when it reads the text form of an XML document. SAX filters should but are not absolutely required to provide well-formed XML data to client applications. Indeed, they can produce substantially more malformed data than this by including start-tags that are not matched by end-tags, text that contains illegal characters such as the formfeed or the vertical tab, and XML names that contain non-name characters such as * and §. You need to be very careful before assuming data you receive from a filter is valid or well-formed.

If you want to invoke a method without filtering it or you want to invoke the same method in the underlying handler, you can prefix a call to it with the super keyword. This invokes the variant of the method from the superclass. By default, each method in XMLFilterImpl just passes the same arguments to the equivalent method in the parent handler. Example 19-7 demonstrates with a filter that changes all character data to uppercase by overriding the characters( ) method.

Example 19-7. A SAX filter that converts text to uppercase

import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class UpperCaseFilter extends XMLFilterImpl {

  public void characters(char[] text, int start, int length)
   throws SAXException {

    String temp = new String(text, start, length);
    temp = temp.toUpperCase( );
    text = temp.toCharArray( );
    super.characters(text, 0, text.length);

  }

}

Actually, using a filter involves these steps:

  1. Create a filter object, normally by invoking its own constructor.

  2. Create the XMLReader that will actually parse the document, normally by calling XMLReaderFactory.createXMLReader( ).

  3. Attach the filter to the parser using the filter's setParent( ) method.

  4. Install a ContentHandler in the filter.

  5. Parse the document by calling the filter's parse( ) method.

Details can vary a little from application to application. For instance, you might install other handlers besides the ContentHandler or change the parent between documents. However, once the filter has been attached to the underlying XMLReader, you should not directly invoke any methods on this underlying parser; you should only talk to it through the filter. For example, this is how you'd use the filter in Example 19-7 to parse a document:

XMLFilter filter = new UpperCaseFilter( );
filter.setParent(XMLReaderFactory.createXMLReader( ));
filter.setContentHandler(yourContentHandlerObject);
filter.parse(document);

Notice specifically that you invoke the filter's parse( ) method, not the underlying parser's parse( ) method.

Previous | Home | Next
19.2. SAX Features and Properties | Book Index | IV. Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Part IV. Reference

Previous | Home | Next
19.3. Filters | Book Index | 20. XML 1.0 Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML 1.0 Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 20. XML 1.0 Reference

Contents:

How to Use This Reference
Annotated Sample Documents
XML Syntax
Constraints
XML Document Grammar

This chapter is intended to serve as a comprehensive reference to the Extensible Markup Language (XML) 1.0 W3C recommendation (Second Edition), dated 6 October 2000. We have made every effort to cover the contents of the official W3C document exhaustively. However, if you are implementing an XML parser, editor, or other tool, you should also review the latest revision of the recommendation on the Web at http://www.w3.org/TR/REC-xml.

20.1. How to Use This Reference

This chapter consists of examples of XML documents and DTDs, followed by detailed reference sections that describe every feature of the 1.0 specification and a listing of possible well-formedness and validity errors. The syntax elements of a valid XML document are introduced in the rough order in which they appear in an XML document. Each entry explains the syntactic structure, where it can be used, and the applicable validity and well-formedness constraints. Each reference section contains a description of the XML language structure, an informal syntax, and an example of the syntax's usage where appropriate.

Previous | Home | Next
IV. Reference | Book Index | 20.2. Annotated Sample Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Annotated Sample Documents (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

20.2. Annotated Sample Documents

These examples are intended as a mnemonic aid for XML syntax and as a quick map from a specific instance of an XML language construct to its corresponding XML syntax reference section. The sample document and DTD incorporate features defined in the XML 1.0 and Namespaces in XML recommendations.

The sample XML application describes the construction of a piece of furniture. Within the figures, each distinct language construct is enclosed in a box, with the relevant reference section name provided as a callout. By locating a construct in the sample, then locating the associated reference section, you can quickly recognize and learn about unfamiliar XML syntax. Four files make up this sample application:

bookcase.xml
The document shown in Figure 20-1 uses furniture.dtd to describe a simple bookcase.

Figure 20-1. bookcase.xml

furniture.dtd
The XML document type definition shown in Figure 20-2 provides a simple grammar for describing components and assembly details for a piece of furniture.

Figure 20-2. furniture.dtd

bookcase_ex.ent
The external entity file shown in Figure 20-3 contains additional bookcase-specific elements for the bookcase.xml document.

Figure 20-3. bookcase_ex.ent

parts_list.ent
Figure 20-4 contains an external parsed general entity example that contains the parts list for the bookcase example document.

Figure 20-4. parts_list.ent

Previous | Home | Next
20. XML 1.0 Reference | Book Index | 20.3. XML Syntax
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Syntax (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

20.3. XML Syntax

For each section of this reference that maps directly to an XML language structure, an informal syntax reference describes theat structure's form. The following conventions are used with these syntax blocks:

Format | Meaning
DOCTYPE | Bold text indicates literal characters that must appear as written within the document (e.g., DOCTYPE).
encoding-name | Italicized text indicates that the user must replace the text with real data. The item indicates what type of data should be inserted (e.g., encoding-name = en-us).
| | The vertical bar | indicates that only one out of a list of possible values can be selected.
[ ] | Square brackets indicate that a particular portion of the syntax is optional.

20.3.1. Global Syntax Structures

Every XML document is broken into two primary sections: the prolog and the document element. A few documents may also have comments or processing instructions that follow the root element in a sort of epilog (an unofficial term). The prolog contains structural information about the particular type of XML document you are writing, including the XML declaration and document type declaration. The prolog is optional, and if a document does not need to be validated against a DTD, it can be omitted completely. The only required structure in a well-formed XML document is the top-level document element itself.

The following syntax structures are common to the entire XML document. Unless otherwise noted within a subsequent reference item, the following structures can appear anywhere within an XML document.

Whitespace

Whitespace is defined as a space, tab, or empty line (which is composed of a carriage return, line feed, or combination of the two). Whitespace serves the same purpose in XML as it does in most programming and natural languages: to separate tokens and language elements from one another. XML has simplified the task of determining which whitespace is significant to an application and which is not. To an XML parser, all whitespace in element content is significant and will be passed to the client application. Whitespace within tags--for instance, between attributes--is not significant. Consider the following example:

<p>  This sentence has extraneous
  line breaks.</p>

After parsing, the character data from this example element is passed to the underlying application as:

   This sentence has extraneous
line breaks.

Though XML specifies that all whitespace in element content be preserved for use by the client application, an additional facility is available to the XML author to further hint that an element's character data's space and formatting should be preserved. For more information, see the discussion of the xml:space attribute in Special Attributes later in this chapter.

Names

To ease the burden of those who write XML parsers, XML names must adhere to the following lexical conventions:

  • Begin with a letter, _, or : character.

  • After the first character, be composed only of letters, digits, ., -, _, and : characters.

In this context, a letter is any Unicode character that matches the Letter production from the EBNF grammar at the end of this chapter.

According to the XML 1.0 specification, the : character may be used freely within names, although the character is now officially reserved as part of the Namespaces in XML recommendation. Even if a document does not use namespaces, the colon should still not be used within identifiers to maintain compatibility with namespace-aware parsers. See the Section 20.3.4 in this chapter for more information about how namespace-aware identifiers are formed.

Names should also avoid starting with the three-letter sequence X, M, L, unless specifically sanctioned by an XML specification.

Character References

&#decimal-number;
&#xhexadecimal-number;
All XML parsers are based on the Unicode character set, no matter what the external encoding of the XML file is. It is theoretically possible to author documents directly in Unicode, but many text-editing, storage, and delivery systems still use the ASCII character set. To allow XML authors to include Unicode characters in their documents' content without forcing them to abandon their existing editing tools, XML provides the character reference mechanism.

A character reference allows an author to insert a Unicode character by number into the output stream produced by the parser to an XML application. Consider an XML document that includes the following character data:

&#xa9; 2002 O'Reilly &#38; Associates

In this example, the parser would replace the character reference with the actual Unicode character and pass it to the client application:

© 2002 O'Reilly & Associates

Character references may not be used in element or attribute names, though they may be used in attribute values.

Predefined Entities

Besides user-defined entity references, XML includes the five named entity references shown in Table 20-1 that can be used without being declared. These references are a subset of those available in HTML documents.

Table 20-1. Predefined entities

Entity

Character

XML declaration

&lt;

<

<!ENTITY lt "&#38;#60;">

&gt;

>

<!ENTITY gt "&#62;">

&amp;

&

<!ENTITY amp "&#38;#38;">

&apos;

"

<!ENTITY apos "&#39;">

&quot;

"

<!ENTITY quot "&#34;">
The &lt; and &amp; entities must be used wherever < or & appear in document content. The &gt; entity is frequently used wherever > appears in document content, but is only mandatory to avoid putting the sequence ]]> into content. &apos; and &quot; are generally used only within attribute values to avoid conflicts between the value and the quotes used to contain the value.

Though the parser must recognize these entities regardless of whether they have been declared, you can declare them in your DTD without generating errors.

The presence of these "special" predefined entities creates a conundrum within an XML document. Because it is possible to use these references without declaring them, it is possible to have a valid XML document that includes references to entities that were never declared. The XML specification actually encourages document authors to declare these entities to maintain the integrity of the entity declaration-reference rule. In practical terms, declaring these entities only adds unnecessary complexity to your document.

CDATA (Character Data) Sections
<![CDATA[unescaped character & markup data]]>
XML documents consist of markup and character data. The < or & characters cannot be included inside normal character data without using a character or entity reference, such as &amp; or &#38;. By using a reference, the resulting < and & characters are not recognized as markup by the parser, but will become part of the data stream to the parser's client application.

For large blocks of character data--particularly if the data contains markup, such as an HTML or XML fragment--the CDATA section can be used. Within a CDATA block, every character between the opening and closing tag is considered character data. Thus, special characters can be included in a CDATA section with impunity, except for the CDATA closing sequence, ]]>.

CDATA sections are very useful for tasks such as enclosing XML or HTML documents inside of tutorials explaining how to use markup, but it is difficult to process the contents of CDATA sections using XSLT, the DOM, or SAX as anything other than text.

NOTE: CDATA sections cannot be nested. The character sequence ]]> cannot appear within data that is being escaped, or the CDATA block will be closed prematurely. This situation should not be a problem ordinarily, but if an application includes XML documents as unparsed character data, it is important to be aware of this constraint. If it is necessary to include the CDATA closing sequence in the data, close the open CDATA section, include the closing characters using character references to escape them, then reopen the CDATA section to contain the rest of the character data.

Entity References

An XML entity can best be understood as a macro replacement facility, in which the replacement can be either parsed (the text becomes part of the XML document) or unparsed. If unparsed, the entity declaration points to external binary data that cannot be parsed. Additionally, the replacement text for parsed entities can come from a string or the contents of an external file. During parsing, a parsed entity reference is replaced by the substitution text that is specified in the entity declaration. The replacement text is then reparsed until no more entity or character references remain.

To simplify document parsing, two distinct types of entities are used in different situations: general and parameter. The basic syntax for referencing both entity types is almost identical, but specific rules apply to where each type can be used.

Parameter Entity References
%name;

When an XML parser encounters a parameter entity reference within a document's DTD, it replaces the reference with the entity's text. Whether the replacement text is included as a literal or included from an external entity, the parser continues parsing the replacement text as if it had always been a part of the document. This parsing has interesting implications for nested entity references:

<!ENTITY % YEAR "2001">
<!ENTITY COPYRIGHT "&#xa9; %YEAR;">
. . .
<copyright_notice>&COPYRIGHT;</copyright_notice>

After the necessary entity replacements are made, the previous example would yield the following canonical element:

<copyright_notice>© 2001</copyright_notice>
WARNING: XML treats parameter entity references differently depending on where they appear within the DTD. References within the literal value of an entity declaration (such as Copyright &#xa9; %YEAR;) are valid only as part of the external subset. Within the internal subset, parameter entity references may occur only where a complete markup declaration could exist. In other words, within the internal subset, parameter references can be used only to include complete markup declarations.

Parameter entity references are recognized only within the DTD; therefore, the % character has no significance within character data and does not need to be escaped.

General Entity References
&name;

General entity references are recognized only within the parsed character data in the body of an XML document. They may appear within the parsed character data contained in an element start- and end-tag, or within the value of an attribute. They are not recognized within a document's DTD (except inside default values for attributes) or within CDATA sections.

NOTE: The sequence of operations that occurs when a parsed general entity is included by the XML parser can lead to interesting side effects. An entity's replacement text is, in turn, read by the parser. If character or general entity replacements exist in the entity replacement text, they are also parsed and included as parsing continues.

Comments
<!-- comment text -->
Comments can appear anywhere in your document or DTD, outside of other markup tags. XML parsers are not required to preserve contents of comment blocks, so they should be used only to store information that is not a part of your application. In reality, most information you might consider storing in a comment block probably should be made an official part of your XML application. Rather than storing data that will be read and acted on by an application in a comment, as is frequently done in HTML documents, you should store it within the element structure of the actual XML document. Enhancing the readability of a complex DTD or temporarily disabling blocks of markup are effective uses of comments.

NOTE: The character sequence -- cannot be included within a comment block, except as part of the tag closing text. Because comments cannot be nested, commenting out a comment block is impossible. If large blocks of markup that include comments must be temporarily disabled, consider wrapping them in a CDATA section to cause the parser to read them as simple text instead of markup.

Processing Instructions
<?target [processing-instruction data]?>
Processing instructions provide an escape mechanism that allows an XML application to include instructions to an XML processor that are not part of the XML markup or character data. The processing instruction target can be any legal XML name, except xml in any combination of upper- and lowercase (see Chapter 2). Linking to a stylesheet to provide formatting instructions for a document is a common use of this mechanism. According to the principles of XML, formatting instructions should remain separate from the actual content of a document, but some mechanism must associate the two. Processing instructions are significant only to applications that recognize them.

The notation facility can indicate exactly what type of processing instruction is included, and each individual XML application must decide what to do with the additional data. No action is required by an XML parser when it recognizes that a particular processing instruction matches a declared notation. When this facility is used, applications that do not recognize the public or system identifiers of a given processing instruction target should realize that they could not properly interpret its data portion.

Character Encoding Autodetection

The XML declaration must be the very first item in a document so that the XML parser can determine which character encoding was used to store the document. A chicken-and-egg problem exists, involving the XML declaration's encoding="..." clause: the parser can't parse the clause if it doesn't know what character encoding the document uses. However, since the first five characters of your document must be the string <?xml (if it includes an XML declaration), the parser can read the first few bytes of your document and, in most cases, determine the character encoding before it has read the encoding declaration.

XML Declaration
<?xml version="1.0" [encoding="encoding-name"][ standalone="yes|no"]?>
The XML declaration serves several purposes. It tells the parser what version of the specification was used, how the document is encoded, and whether the document is completely self-contained or has references to external entities.

The XML declaration, if included, must be the first thing that appears in an XML document. Nothing, except possibly a Unicode byte-order mark, may appear before this structure's initial < character.

Version Information
... version="1.0" ...

The version information attribute denotes which version of the XML specification was used to create the current document. At this time, the only valid version is 1.0.

Encoding Declaration
... encoding="encoding-name" ...

The encoding declaration, if present, indicates which character-encoding scheme was used to store the document. Although all XML documents are ultimately handled as Unicode by the parser, the external storage scheme may be anything from an ASCII text file using the Latin-1 character set (ISO-8859-1) to a file with native Japanese characters.

XML parsers may also recognize other encodings, but the XML specification only requires that they recognize UTF-8 and UTF-16 encoded documents. Many parsers also support additional character encodings. For a thorough discussion of character-encoding schemes, see Chapter 26.

Standalone Declaration
... standalone="yes|no" ...

If a document is completely self contained (the DTD, if there is one, is contained completely within the original document), then the standalone="yes" declaration may be used. If this declaration is not given, the value no is assumed, and all external entities are read and parsed. It is possible to convert any document in which standalone="no" to a standalone document by replacing each external entity reference with the text contained in the external entity file.

From the standpoint of an XML application developer, this flag has no effect on how a document is parsed. However, if it is given, it must be accurate. Setting standalone="yes" when a document does require DTD declarations that are not present in the main document file is a violation of XML validity rules.

20.3.2. DTD (Document Type Definition)

Chapter 2 explained the difference between well-formed and valid documents. Well-formed documents that include and conform to a given DTD are considered valid. Documents that include a DTD and violate the rules of that DTD are invalid. The DTD is comprised of the DOCTYPE declaration and both the internal subset (declarations contained directly within the document) and the external subset (declarations that are included from outside the main document).

Parameter Entities

The parameter entity mechanism is a simple macro replacement facility that is only valid within the context of the DTD. Parameter entities are declared and then referenced from within markup or possibly from within other entity declarations. The source of the entity replacement text can be either a literal string or the contents of an external file. Parameter entities simplify maintenance of large, complex documents by allowing authors to build libraries of commonly used entity declarations.

Parameter Entity Declarations
<!ENTITY % name "Replacement text.">
<!ENTITY % name SYSTEM
     "system-literal">
<!ENTITY % name PUBLIC "pubid-literal"
    "system-literal">
Parameter entities are declared within the document's DTD and must be declared before they are used. The declaration provides two key pieces of information:

  • The name of the entity, which is used when it is referenced

  • The replacement text, either directly or indirectly through a link to an external entity

Be aware that an XML parser performs some preprocessing on the replacement text before it is used in an entity reference. Most importantly, parameter entity references in the replacement text are recursively expanded before the final version of the replacement text is stored. Character references are also replaced immediately with the specified character. This replacement can lead to unexpected side effects, particularly when constructing parameter entities that declare other parameter entities. For full disclosure of how entity replacement is implemented by an XML parser and what kinds of unexpected side effects can occur, see Appendix D of the XML 1.0 specification. The specification is available on the World Wide Web Consortium web site (http://www.w3.org/TR/REC-xml#sec-entexpand ).

General Entities

General entities are declared within the document type definition and then referenced within the document's text and attribute content. When the document is parsed, the entity's replacement text is substituted for the entity reference. The parser then resumes parsing, starting with the text that was just replaced.

General entities are declared within the DTD using a superset of the syntax used to declare parameter entities. Besides the ability to declare internal parsed entities and external parsed entities, you can declare external unparsed entities and associate an XML notation name with them.

Internal entities are used when the replacement text can be efficiently stored inline as a literal string. The replacement text within an internal entity is included completely in the entity declaration itself, obviating the need for an external file to contain the replacement text. This situation closely resembles the string replacement macro facilities found in many popular programming languages and environments:

<!ENTITY name "Replacement text">

There are two types of external entities: parsed and unparsed. When a parsed entity is referenced, the contents of the external entity are included in the document, and the XML parser resumes parsing, starting with the newly included text. When an unparsed entity is referenced, the parser supplies the application with the unparsed entity's URI, but it does not insert that data into the document or parse it. What to do with that URI is up to the application. Any entity declared with an XML notation name associated with it is an external unparsed entity, and any references to it within the document must be made using attribute values of type ENITITY or ENTITIES:

<!ENTITY name SYSTEM
    "system-literal">
<!ENTITY name PUBLIC
    "pubid-literal" "system-literal">
Text Declarations
<?xml[ version="1.0"] encoding="encoding-name"?>
Files that contain external parsed entities must include a text declaration if the entity file uses a character encoding other than UTF-8 or UTF-16. This declaration would be followed by the replacement text of the external parsed entity.

NOTE: External parsed entities may contain only document content or a completely well-formed subset of the DTD. This restriction is significant because it indicates that external parameter entities cannot be used to play token-pasting games by splitting XML syntax constructs into multiple files, then expecting the parser to reassemble them.

Unparsed Entities

It may be necessary at times to include data in your XML document that should not be parsed. For instance, your XML document may need to include pointers to graphics files that will be used by an application. These files are logically part of the document, but should not be parsed. The XML language allows you to declare external unparsed entities that can be included as attribute values within the content of your document:

<!ENTITY name SYSTEM
    "system-literal" NDATA notation_name >
<!ENTITY name PUBLIC "pubid-literal "
    "system-literal" NDATA notation_name >

To include unparsed entities, you must first declare a notation that will be referenced in the actual entity declaration:

<!NOTATION gif SYSTEM "images/gif">

Then declaring the entity itself is possible:

<!ENTITY bookcase_pic SYSTEM "bookcase.gif" NDATA gif>

As an unparsed general entity, it can be referenced only as an attribute value of type ENTITY or ENTITIES:

<picture src="bookcase_pic" type="gif"/>

When an XML parser parses this element, the information contained in the entity and notation declarations can be used to identify the actual type of data stored in the external entity. For example, a program could choose to display the contents of a GIF external entity on the screen, once the actual format is known.

NOTE: XLink and similar mechanisms are commonly used in place of unparsed entities.

External Subset

The document type declaration can include part or all of the document type definition from an external file. This external portion of the DTD is referred to as the external DTD subset and may contain markup declarations, conditional sections, and parameter entity references. It must include a text declaration if the character encoding is not UTF-8 or UTF-16:

<?xml[ version="1.0"] encoding="encoding-name"?>

This declaration (if present) would then be followed by a series of complete DTD markup statements, including ELEMENT, ATTLIST, ENTITY, and NOTATION declarations, as well as conditional sections, and processing instructions. For example:

<!ELEMENT furniture_item (desc, %extra_tags; user_tags?, parts_list,
    assembly+)>

<!ATTLIST furniture_item
    xmlns CDATA #FIXED "http://namespaces.oreilly.com/furniture/"
>
...
Internal DTD Subset

The internal DTD subset is the portion of the document type definition included directly within the document type declaration between the [ and ] characters. The internal DTD subset can contain markup declarations and parameter entity references, but not conditional sections. A single document may have both internal and external DTD subsets, which, when taken together, form the complete document type definition. The following example shows the internal subset, which appears between the [ and ] characters:

<!DOCTYPE furniture_item SYSTEM "furniture.dtd"
[
<!ENTITY % bookcase_ex SYSTEM "Bookcase_ex.ent">

%bookcase_ex;

<!ENTITY bookcase_pic SYSTEM "bookcase.gif" NDATA gif>
<!ENTITY parts_list SYSTEM "parts_list.ent">
]>
Element Type Declaration

Element type declarations provide a template for the actual element instances that appear within an XML document. The declaration determines what type of content, if any, can be contained within elements with the given name. The following sections describe the various element content options available.

NOTE: Since namespaces are not explicitly included in the XML 1.0 recommendation, element and attribute declarations within a DTD must give the complete (qualified) name that will be used in the target document. This means that if namespace prefixes will be used in instance documents, the DTD must declare them just as they will appear, prefixes and all. While parameter entities may allow instance documents to use different prefixes, this still makes complete and seamless integration of namespaces into a DTD-based application very awkward.

Empty Element Type
<!ELEMENT name EMPTY>

Elements that are declared empty cannot contain content or nested elements. Within the document, empty elements may use one of the following two syntax forms:

<name [attribute="value" ...]/>
<name [attribute="value" ...]></name>
Any Element Type
<!ELEMENT name ANY>

This content specifier acts as a wildcard, allowing elements of this type to contain character data or instances of any valid element types that are declared in the DTD.

Mixed Content Element Type
<!ELEMENT name (#PCDATA [ | name]+)*>
<!ELEMENT name (#PCDATA)>
Element declarations that include the #PCDATA token can include text content mixed with other nested elements that are declared in the optional portion of the element declaration. If the #PCDATA token is used, it is not possible to limit the number of times or sequence in which other nested elements are mixed with the parsed character data. If only text content is desired, the asterisk is optional.

Constrained Child Nodes
<!ELEMENT name (child_node_regexp)[? | * | +]>

XML provides a simple regular-expression syntax that can be used to limit the order and number of child elements within a parent element. This language includes the following operators:

Operator

Meaning

Name

Matches an element of the given name

( ... )

Groups expressions for processing as sets of sequences (using the comma as a separator) or choices (using | as a separator)

?

Indicates that the preceding name or expression can occur zero or one times at this point in the document

*

Indicates that the preceding name or expression can occur zero or more times at this point in the document

+

Indicates that the preceding name or expression must occur one or more times at this point in the document

Attribute List Declaration
<!ATTLIST element_name [attribute_name attribute_type default_decl]*>

In a valid XML document it is necessary to declare the attribute names, types, and default values that are used with each element type.

The attribute name must obey the rules for XML identifiers, and no duplicate attribute names may exist within a single declaration.

Attributes are declared as having a specific type. Depending on the declared type, a validating XML parser will constrain the values that appear in instances of those attributes within a document. The following table lists the various attribute types and their meanings:

Attribute type

Meaning

CDATA

Simple character data.

ID

A unique ID value within the current XML document. No two ID attribute values within a document can have the same value, and no element can have two attributes of type ID.

IDREF, IDREFS

A single reference to an element ID (IDREF) or a list of IDs (IDREFS), separated by spaces. Every ID token must refer to a valid ID located somewhere within the document that appears as the ID type attribute's value.

ENTITY, ENTITIES

A single reference to a declared unparsed external entity (ENTITY) or a list of references (ENTITIES), separated by spaces.

NMTOKEN, NMTOKENS

A single name token value (NMTOKEN) or a list of name tokens (NMTOKENS), separated by spaces.

NOTATION Attribute Type

... NOTATION (notation [| notation]*) ...
The NOTATION attribute mechanism lets XML document authors indicate that the character content of some elements obey the rules of some formal language other than XML. The following short sample document shows how notations might be used to specify the type of programming language stored in the code_fragment element:

<?xml version="1.0"?>
<!DOCTYPE code_fragment
[
<!NOTATION java_code PUBLIC "Java source code">
<!NOTATION c_code PUBLIC "C source code">
<!NOTATION perl_code PUBLIC "Perl source code">
<!ELEMENT code_fragment (#PCDATA)>
<!ATTLIST code_fragment
          code_lang NOTATION (java_code | c_code | perl_code) #REQUIRED>

]>
<code_fragment code_lang="c_code">
    main( ) { printf("Hello, world."); }
</code_fragment>
Enumeration Attribute Type

... (name_token [| name_token]*) ...
This syntax limits the possible values of the given attribute to one of the name tokens from the provided list:

<!ELEMENT door EMPTY>
<!ATTLIST door
          state (open | closed | missing) "open">
. . .
<door state="closed"/>
Default Values

If an optional attribute is not present on a given element, a default value may be provided to be passed by the XML parser to the client application. The following table shows various forms of the attribute default value clause and their meanings:

Default value clause

Explanation

#REQUIRED

A value must be provided for this attribute.

#IMPLIED

A value may or may not be provided for this attribute.

[#FIXED ] "default value"

If this attribute has no explicit value, the XML parser substitutes the given default value. If the #FIXED token is provided, this attribute's value must match the given default value. In either case, the parent element always has an attribute with this name.

The #FIXED modifier indicates that the attribute may contain only the value given in the attribute declaration. Although redundant, it is possible to provide an explicit attribute value on an element when the attribute was declared as #FIXED. The only restriction is that the attribute value must exactly match the value given in the #FIXED declaration.

Special Attributes

Some attributes are significant to XML and must be declared and implemented in a particular way:

xml:space
The xml:space attribute tells an XML application whether the whitespace within the specified element is significant:

<!ATTLIST element_name xml:space (default|preserve)
  default_decl>
<!ATTLIST element_name xml:space (default) #FIXED 'default' >
<!ATTLIST element_name xml:space (preserve) #FIXED 'preserve' >
xml:lang
For an element's character content, the xml:lang attribute allows a document author to specify the human language for an element's character content. If used in a valid XML document, the document type definition must include an attribute type declaration with the xml:lang attribute name. See Chapter 5 for an explanation of language support in XML.

Notation Declaration

<!NOTATION notation_name SYSTEM "system-literal">
<!NOTATION notation_name PUBLIC "pubid-literal">
<!NOTATION notation_name PUBLIC "pubid-literal" "system-literal">
Notation declarations are used to provide information to an XML application about the format of the document's unparsed content. Notations are used by unparsed external entities, processing instructions, and some attribute values.

Notation information is not significant to the XML parser, but it is preserved for use by the client application. The public and system identifiers are made available to the client application so that it may correctly interpret non-XML data and processing instructions.

Conditional Sections

The conditional section markup provides support for conditionally including and excluding content at parse time within an XML document's external subset. Conditional sections are not allowed within a document's internal subset. The following example illustrates a likely application of conditional sections:

<!ENTITY % debug 'IGNORE' >
<!ENTITY % release 'INCLUDE' >

<!ELEMENT addend (#PCDATA)>
<!ELEMENT result (#PCDATA)>

<![%debug;[
<!ELEMENT sum (addend+, result)>
]]>
<![%release;[
<!ELEMENT sum (result)>
]]>

20.3.3. Document Body

Elements are an XML document's lifeblood. They provide the structure for character data and attribute values that make up a particular instance of an XML document type definition. The !ELEMENT and !ATTLIST declarations from the DTD restrict the possible contents of an element within a valid XML document. Combining elements and/or attributes that violate these restrictions generates an error in a validating parser.

Start-Tags and End-Tags

<element_name [attribute_name="attribute value"]*> ...</element_name>
Elements that have content (either character data, other elements, or both) must start with a start-tag and end with an element end-tag.

Empty-Element Tags
<element_name [attribute_name="attribute value"]*></empty_element>
<element_name [attribute_name="attribute value"]* />
Empty elements have no content and are written using either the start- and end-tag syntax mentioned previously or the empty-element syntax. The two forms are functionally identical, but the empty-element syntax is more succinct and more frequently used.

Attributes
attribute_name="attribute value"
attribute_name='attribute value'
Elements may include attributes. The order of attributes within an element tag is not significant and is not guaranteed to be preserved by an XML parser. Attribute values must appear within either single or double quotations. Attribute values within a document must conform to the rules explained in Section 20.4.1 of this chapter.

Note that whitespace may appear around the = character.

The value that appears in the quoted string is tested for validity, depending on the attribute type provided in the !ATTLIST declaration for the element type. Attribute values can contain general entity references, but cannot contain references to external parsed entities. See Section 20.4.1 of this chapter for more information about attribute-value restrictions.

20.3.4. Namespaces

Although namespace support was not part of the original XML 1.0 recommendation, Namespaces in XML was approved less than a year later (January 14, 1999). Namespaces are used to identify uniquely the element and attribute names of a given XML application from those of other applications. See Chapter 4 for more detailed information.

The following sections describe how namespaces impact the formation and interpretation of element and attribute names within an XML document.

Unqualified Names
name
An unqualified name is an XML element or attribute name that is not associated with a namespace. This could be because it has no namespace prefix and no default namespace has been declared. All unprefixed attribute names are unqualified because they are never automatically associated with a default namespace. XML parsers that do not implement namespace support (of which there are very few) or parsers that have been configured to ignore namespaces will always return unqualified names to their client applications. Two unqualified names are considered to be the same if they are lexically identical.

Qualified Names
[prefix:]local_part
A qualified name is an element or attribute name that is associated with an XML namespace. There are three possible types of qualified names:

  • Unprefixed element names that are contained within the scope of a default namespace declaration

  • Prefixed element names

  • Prefixed attribute names

Unlike unqualified names, qualified names are considered the same only if their namespace URIs (from their namespace declarations) and their local parts match.

Default Namespace Declaration
xmlns="namespace_URI"
When this attribute is included in an element start-tag, it and any unprefixed elements contained within it are automatically associated with the namespace URI given. If the xmlns attribute is set to the empty string, any effective default namespace is ignored, and unprefixed elements are not associated with any namespace.

NOTE: An important caveat about default namespace declarations is that they do not affect unprefixed attributes. Unprefixed attributes are never explicitly named in any namespace, even if their containing element is.

Namespace Prefix Declaration
xmlns:prefix="namespace_URI"
This declaration associates the namespace URI given with the prefix name given. Once it has been declared, the prefix may qualify the current element name, attribute names, or any other element or attribute name within the scope of the element that declares it. Nested elements may redefine a given prefix, using a different namespace URI if desired.

Previous | Home | Next
20.2. Annotated Sample Documents | Book Index | 20.4. Constraints
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Constraints (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

20.4. Constraints

In addition to defining the basic structures used in documents and DTDs, XML 1.0 defines a list of rules regarding their usage. These constraints put limits on various aspects of XML usage, and documents cannot in fact be considered to be "XML" unless they meet all of the well-formedness constraints. Parsers are required to report violations of these constraints, though only well-formedness constraint violations require that processing of the document halt completely. Namespace constraints are defined in Namespaces in XML, not XML 1.0.

20.4.1. Well-Formedness Constraints

Well-formedness refers to an XML document's physical organization. Certain lexical rules must be obeyed before an XML parser can consider a document well-formed. These rules should not be confused with validity constraints, which determine whether a particular document is valid when parsed using the document structure rules contained in its DTD. The Backus-Naur Form (BNF) grammar rules must also be satisfied. The following sections contain all well-formedness constraints recognized by XML Version 1.0 parsers, including actual text from the 1.0 specification.

PEs in Internal Subset

Text from specification

In the internal DTD subset, parameter entity references can occur only where markup declarations can occur, not within markup declarations. (This does not apply to references that occur in external parameter entities or to the external subset.)

Explanation

It is only legal to use parameter entity references to build markup declarations within the external DTD subset. In other words, within the internal subset, parameter entities may only be used to include complete markup declarations.

External Subset

Text from specification

The external subset, if any, must match production for extSubset.

Explanation

The extSubset production constrains what type of declaration may be contained in the external subset. This constraint generally means that the external subset of the DTD must only include whole declarations or parameter entity references. See the extSubset production in the EBNF grammar at the end of this chapter for specific limitations.

PE Between Declarations

Text from specification

The replacement text of a parameter entity reference in a DeclSep must match the production extSubsetDecl.

Explanation

The replacement text of parameter entities may contain declarations that might not be allowed if the replacement text appeared directly. Parameter entity references in the internal subset cannot appear within declarations, but this rule does not apply to declarations that have been included via parameter entities.

Element Type Match

Text from specification

The Name in an element's end-tag must match the element type in the start-tag.

Explanation

Proper element nesting is strictly enforced, and every open tag must be matched by a corresponding close tag. Of course empty elements do not require and may not have a close tag.

Unique Att Spec

Text from specification

No attribute name may appear more than once in the same start-tag or empty- element tag.

Explanation

Attribute names must be unique within a given element.

No External Entity References

Text from specification

Attribute values cannot contain direct or indirect entity references to external entities.

Explanation

XML parsers report an error when asked to replace references to external parsed entities within attribute values.

No < in Attribute Values

Text from specification

The replacement text of any entity referred to directly or indirectly in an attribute value (other than "&lt;") must not contain a <.

Explanation

This restriction is meant to simplify the task of parsing XML data. Since attribute values can't even appear to contain element data, simple parsers need not track literal strings. Just by recognizing < and > characters, simple parsers can check for proper markup formation and nesting.

Legal Character

Text from specification

Characters referred to using character references must match the production for Char.

Explanation

Any characters that the XML parser generates must be real characters. A few character values in Unicode are not valid standalone characters.

Entity Declared

Text from specification

In a document without any DTD, a document with only an internal DTD subset which contains no parameter entity references, or a document with standalone='yes', for an entity reference that does not occur within the external subset or a parameter entity, the Name given in the entity reference must match that in an entity declaration that does not occur within the external subset or a parameter entity, except that well-formed documents need not declare any of the following entities: amp, lt, gt, apos, quot. The declaration of a parameter entity must precede any reference to it. Similarly, the declaration of a general entity must precede any reference to it which appears in a default value in an attribute-list declaration. Note that if entities are declared in the external subset or in external parameter entities, a non-validating processor is not obligated to read and process their declarations; for such documents, the rule that an entity must be declared is a well-formedness constraint only if standalone='yes'.

Explanation

This long constraint lists the only situations in which an entity reference may appear without a corresponding entity declaration. Since a nonvalidating parser is not obliged to read and parse the external subset, the parser must give the document the benefit of the doubt, if an entity could possibly have been declared.

Parsed Entity

Text from specification

An entity reference must not contain the name of an unparsed entity. Unparsed entities may be referred to only in attribute values declared to be of type ENTITY or ENTITIES.

Explanation

Since unparsed entities can't be parsed, don't try to force the parser to parse them.

No Recursion

Text from specification

A parsed entity must not contain a recursive reference to itself, either directly or indirectly.

Explanation

Be careful how you structure your entities; make sure you don't inadvertently create a circular reference:

<!ENTITY a "&b;">
<!ENTITY b "&c;">
<!ENTITY c "&a;"> <!--wrong!-->
In DTD

Text from specification

Parameter entity references may only appear in the DTD.

Explanation

This constraint is self evident because the % character has no significance outside of the DTD. Therefore, it is perfectly legal to have an element like this in your document:

<ok>%noproblem;</ok>

The text %noproblem; is passed on by the parser without generating an error.

20.4.2. Validity Constraints

The following sections contain all validity constraints that are enforced by a validating parser. Each includes actual text from the XML 1.0 specification and a short explanation of what the constraint actually means.

Root Element Type

Text from specification

The Name in the document type declaration must match the element type of the root element.

Explanation

The name provided in the !DOCTYPE declaration identifies the root element's name and must match the name of the root element in the document.

Proper Declaration/PE Nesting

Text from specification

Parameter entity replacement text must be properly nested with markup declarations. That is to say, if either the first character or the last character of a markup declaration is contained in the replacement text for a parameter entity reference, both must be contained in the same replacement text.

Explanation

This constraint means you can't create a parameter entity that completes one DTD declaration and begins another; the following XML fragment would violate this constraint:

<!ENTITY % finish_it ">">
<!ENTITY % bad "won't work" %finish_it; <!--wrong!-->
Standalone Document Declaration

Text from specification

The standalone document declaration must have the value "no" if any external markup declarations contain declarations of: attributes with default values, if elements to which these attributes apply appear in the document without specifications of values for these attributes, or entities (other than amp, lt, gt, apos, quot), if references to those entities appear in the document, or attributes with values subject to normalization, where the attribute appears in the document with a value which will change as a result of normalization, or element types with element content, if whitespace occurs directly within any instance of those types.

Explanation

This laundry list of potential standalone flag violations can be read to mean, "If you have an external subset in your DTD, ensure that your document doesn't depend on anything in it if you say standalone='yes' in your XML declaration." A more succinct interpretation would be, "If your document has an external DTD subset, just set standalone to no."

Element Valid

Text from specification

An element is valid if there is a declaration matching elementdecl where the Name matches the element type, and one of the following holds: The declaration matches EMPTY and the element has no content. The declaration matches children and the sequence of child elements belongs to the language generated by the regular expression in the content model, with optional whitespace (characters matching the nonterminal S) between the start-tag and the first child element, between child elements, or between the last child element and the end-tag. Note that a CDATA section containing only whitespace does not match the nonterminal S, and hence cannot appear in these positions. The declaration matches Mixed and the content consists of character data and child elements whose types match names in the content model. The declaration matches ANY, and the types of any child elements have been declared.

Explanation

If a document includes a DTD with element declarations, make sure the actual elements in the document match the rules set down in the DTD.

Attribute Value Type

Text from specification

The attribute must have been declared; the value must be of the type declared for it.

Explanation

All attributes used on elements in valid XML documents must have been declared in the DTD, including the xml:space and xml:lang attributes. If you declare an attribute for an element, make sure that every instance of that attribute has a value conforming to the type specified. (For attribute types, see Attribute List Declaration.)

Unique Element Type Declaration

Text from specification

No element type may be declared more than once.

Explanation

Unlike entity and attribute declarations, only one declaration may exist for a particular element type.

Proper Group/PE Nesting

Text from specification

Parameter entity replacement text must be properly nested with parenthesized groups. That is to say, if either of the opening or closing parentheses in a choice, seq, or Mixed construct is contained in the replacement text for a parameter entity, both must be contained in the same replacement text.

For interoperability, if a parameter entity reference appears in a choice, seq, or Mixed construct, its replacement text should contain at least one non-blank character, and neither the first nor last non-blank character of the replacement text should be a connector (| or ,).

Explanation

This constraint restricts the way parameter entities can be used to construct element declarations. It is similar to the Proper Declaration/PE Nesting constraint in that parameter entities may not be used to complete or open new parenthesized expressions. It prevents the XML author from hiding significant syntax elements inside parameter entities.

No Duplicate Types

Text from specification

The same name must not appear more than once in a single mixed-content declaration.

Explanation

Don't list the same element type name more than once in the same mixed-content declaration.

ID

Text from specification

Values of type ID must match the Name production. A name must not appear more than once in an XML document as a value of this type; i.e., ID values must uniquely identify the elements which bear them.

Explanation

No two attribute values for attributes declared as type ID can have the same value. This constraint is not restricted by element type, but it is global across the entire document.

One ID per Element Type

Text from specification

No element type may have more than one ID attribute specified.

Explanation

Each element can have at most one ID type attribute.

ID Attribute Default

Text from specification

An ID attribute must have a declared default of #IMPLIED or #REQUIRED.

Explanation

To avoid potential duplication, you can't declare an ID attribute to be #FIXED or provide a default value for it.

IDREF

Text from specification

Values of type IDREF must match the Name production, and values of type IDREFS must match Names; each Name must match the value of an ID attribute on some element in the XML document; i.e., IDREF values must match the value of some ID attribute.

Explanation

ID references must refer to actual ID attributes that exist within the document.

Entity Name

Text from specification

Values of type ENTITY must match the Name production, and values of type ENTITIES must match Names; each Name must match the name of an unparsed entity declared in the DTD.

Explanation

Attributes declared to contain entity references must contain references to unparsed entities declared in the DTD.

Name Token

Text from specification

Values of type NMTOKEN must match the Nmtoken production; values of type NMTOKENS must match Nmtokens.

Explanation

If an attribute is declared to contain a name or list of names, the values must be legal XML name tokens.

Notation Attributes

Text from specification

Values of this type must match one of the notation names included in the declaration; all notation names in the declaration must be declared.

Explanation

Attributes that must contain notation names must contain names that reference notations declared in the DTD.

One Notation per Element Type

Text from specification

No element type may have more than one NOTATION attribute specified.

Explanation

A given element can have only one attribute declared with the NOTATION attribute type. This constraint is provided for backward compatibility with SGML.

No Notation on Empty Element

Text from specification

For compatibility, an attribute of type NOTATION must not be declared on an element declared EMPTY.

Explanation

Empty elements cannot have NOTATION attributes in order to maintain compatibility with SGML.

Enumeration

Text from specification

Values of this type must match one of the Nmtoken tokens in the declaration.

Explanation

Assigning a value to an enumerated type attribute that isn't listed in the enumeration is illegal in the DTD.

Required Attribute

Text from specification

If the default declaration is the keyword #REQUIRED, then the attribute must be specified for all elements of the type in the attribute-list declaration.

Explanation

Required attributes must appear in the document and have a value assigned to them if they are declared as #REQUIRED in the DTD.

Attribute Default Legal

Text from specification

The declared default value must meet the lexical constraints of the declared attribute type.

Explanation

If you provide a default attribute value, it must obey the same rules that apply to a normal attribute value within the document.

Fixed Attribute Default

Text from specification

If an attribute has a default value declared with the #FIXED keyword, instances of that attribute must match the default value.

Explanation

If you choose to provide an explicit value for a #FIXED attribute in your document, it must match the default value given in the attribute declaration.

Proper Conditional Section/PE Nesting

Text from specification

If any of the "<![", "[", or "]]>" of a conditional section is contained in the replacement text for a parameter entity reference, all of them must be contained in the same replacement text.

Explanation

If you use a parameter entity to contain the beginning of a conditional section, the parameter entity must also contain the end of the section.

Entity Declared

Text from specification

In a document with an external subset or external parameter entities with "standalone='no'", the Name given in the entity reference must match that in an entity declaration. For interoperability, valid documents should declare the entities amp, lt, gt, apos, quot, in the form specified in 4.6 Predefined Entities. The declaration of a parameter entity must precede any reference to it. Similarly, the declaration of a general entity must precede any attribute-list declaration containing a default value with a direct or indirect reference to that general entity.

Explanation

Parameter and general entity declarations must precede any references to these entities. All entity references must refer to previously declared entities. The specification also states that declaring the five predefined general entities (amp, lt, gt, apos, and quot) is a good idea. In reality, declaring the predefined general entities adds unnecessary complexity to most applications.

Notation Declared

Text from specification

The Name must match the declared name of a notation.

Explanation

External unparsed entities must use a notation that is declared in the document.

Unique Notation Name

Text from specification

Only one notation declaration can declare a given Name.

Explanation

Declaring two notations with the same name is illegal.

20.4.3. Namespace Constraints

The following list contains all constraints defined by the namespaces specification. Each includes actual text from the Namespaces in XML specification and a short explanation of what the constraint actually means.

Leading "XML"

Text from specification

Prefixes beginning with the three-letter sequence x, m, l, in any case combination, are reserved for use by XML and XML-related specifications.

Explanation

Just like most other names in XML, namespace prefixes names can't begin with xml unless they've been defined by the W3C.

Prefix Declared

Text from specification

The namespace prefix, unless it is xml or xmlns, must have been declared in a namespace declaration attribute in either the start-tag of the element where the prefix is used or in an ancestor element (i.e., an element in whose content the prefixed markup occurs). The prefix xml is by definition bound to the namespace name http://www.w3.org/XML/1998/namespace. The prefix xmlns is used only for namespace bindings and is not itself bound to any namespace name.

Explanation

You have to declare all namespaces before you can use them. The prefixes have no meaning without the declarations, so using a prefix without a declaration context is an error. The namespace with the prefix xml is permanently defined, so there is no need to redeclare it. The xmlns prefix used by namespace declarations is not considered a namespace prefix itself, and no declaration is needed for it.

Previous | Home | Next
20.3. XML Syntax | Book Index | 20.5. XML Document Grammar
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Document Grammar (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

20.5. XML Document Grammar

The Extended Backus-Naur Form (EBNF) grammar, shown in the following section, was collected from the XML 1.0 Recommendation, Second Edition. It brings all XML language productions together in a single location and describes the syntax that is understood by XML 1.0-compliant parsers. Each production has been numbered and cross-referenced using superscripted numbers.

20.5.1. Extended Backus-Naur Form (EBNF) Grammar

20.5.1.1. Document

[1] document ::= prolog22 element39 Misc27

20.5.1.2. Character range

[2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

20.5.1.3. Whitespace

[3] S ::= (#x20 | #x9 | #xD | #xA)+

20.5.1.4. Names and tokens

[4] NameChar ::= Letter84 | Digit88 | '.' | '-' | '_' | ':' | CombiningChar87 | Extender89
[5] Name ::= (Letter84 | '_' | ':') (NameChar4)*
[6] Names ::= Name5 (#x20 Name5)*
[7] Nmtoken ::= (NameChar4)+
[8] Nmtokens ::= Nmtoken7 (#x20 Nmtoken7)*

20.5.1.5. Literals

[9] EntityValue ::= '"' ([^%&"] | PEReference69 | Reference67)* '"' |  "'" ([^%&'] | PEReference69 | Reference67)* "'"
[10] AttValue ::= '"' ([^<&"] | Reference67)* '"' |  "'" ([^<&'] | Reference67)* "'"
[11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
[12] PubidLiteral ::= '"' PubidChar13* '"' | "'" (PubidChar13 - "'")* "'"
[13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'( )+,./:=?;!*#@$_%]

20.5.1.6. Character data

[14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)

20.5.1.7. Comments

[15] Comment ::= '<!--' ((Char2 - '-') | ('-' (Char2 - '-')))* '-->'

20.5.1.8. Processing instructions

[16] PI ::= '<?' PITarget17 (S3 (Char2* - (Char2* '?>' Char2*)))? '?>'
[17] PITarget ::= Name5 - (('X' | 'x') ('M' | 'm') ('L' | 'l'))

20.5.1.9. CDATA sections

[18] CDSect ::= CDStart19 CData20 CDEnd21
[19] CDStart ::= '<![CDATA['
[20] CData ::= (Char2* - (Char2* ']]>' Char2*))
[21] CDEnd ::= ']]>'

20.5.1.10. Prolog

[22] prolog ::= XMLDecl23? Misc27* (doctypedecl28 Misc27*)?
[23] XMLDecl ::= '<?xml' VersionInfo24 EncodingDecl80? SDDecl32? S3? '?>'
[24] VersionInfo ::= S3 'version' Eq ("'" VersionNum26 "'" | '"' VersionNum26 '"')
[25] Eq ::= S3? '=' S3?
[26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+
[27] Misc ::= Comment15 | PI16 | S3

20.5.1.11. Document type definition

[28] doctypedecl ::= '<!DOCTYPE' S3 Name5 (S3 ExternalID)? S3? ('[' intSubset28b ']' S3?)? '>'
[28a] DeclSep ::= PEReference69 | S3
[28b] intSubset ::= (markupdecl29 | DeclSep28a)*
[29] markupdecl ::= elementdecl45 | AttlistDecl52 | EntityDecl70 | NotationDecl82 | PI16 | Comment15

20.5.1.12. External subset

[30] extSubset ::= TextDecl77? extSubsetDecl31
[31] extSubsetDecl ::= ( markupdecl29 | conditionalSect61 | DeclSep28a)*

20.5.1.13. Standalone document declaration

[32] SDDecl ::= S3 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))

20.5.1.14. Element

[39] element ::= EmptyElemTag44 | STag40 content43 ETag42

20.5.1.15. Start-tag

[40] STag ::= '<' Name5 (S3 Attribute)* S3? '>'
[41] Attribute ::= Name5 Eq AttValue10

20.5.1.16. End-tag

[42] ETag ::= '</' Name5 S3? '>'

20.5.1.17. Content of elements

[43] content ::= CharData14? ((element39 | Reference67 | CDSect18 | PI16 | Comment15) CharData14?)*

20.5.1.18. Tags for empty elements

[44] EmptyElemTag ::= '<' Name5 (S3 Attribute41)* S3? '/>'

20.5.1.19. Element type declaration

[45] elementdecl ::= '<!ELEMENT' S3 Name5 S3 contentspec46 S3? '>'
[46] contentspec ::= 'EMPTY' | 'ANY' | Mixed51 | children47

20.5.1.20. Element-content models

[47] children ::= (choice49 | seq50) ('?' | '*' | '+')?
[48] cp ::= (Name5 | choice49 | seq50) ('?' | '*' | '+')?
[49] choice ::= '(' S3? cp48 ( S3? '|' S3? cp48 )+ S3? ')'
[50] seq ::= '(' S3? cp48 ( S3? ',' S3? cp48 )* S3? ')'

20.5.1.21. Mixed-content declaration

[51] Mixed ::= '(' S3? '#PCDATA' (S3? '|' S3? Name5)* S3? ')*' | '(' S3? '#PCDATA' S3? ')'

20.5.1.22. Attribute-list declaration

[52] AttlistDecl ::= '<!ATTLIST' S3 Name5 AttDef53* S3? '>'
[53] AttDef ::= S3 Name5 S3 AttType54 S3 DefaultDecl60

20.5.1.23. Attribute types

[54] AttType ::= StringType55 | TokenizedType56 | EnumeratedType57
[55] StringType ::= 'CDATA'
[56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' |
'NMTOKEN' | 'NMTOKENS'

20.5.1.24. Enumerated attribute types

[57] EnumeratedType ::= NotationType58 | Enumeration59
[58] NotationType ::= 'NOTATION' S3 '(' S3? Name5 (S3? '|' S3? Name5)* S3? ')'
[59] Enumeration ::= '(' S3? Nmtoken7 (S3? '|' S3? Nmtoken7)* S3? ')'

20.5.1.25. Attribute defaults

[60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S3)? AttValue10

20.5.1.26. Conditional section

[61] conditionalSect ::= includeSect62 | ignoreSect63
[62] includeSect ::= '<![' S3? 'INCLUDE' S3? '[' extSubsetDecl31 ']]>'
[63] ignoreSect ::= '<![' S3? 'IGNORE' S3? '[' ignoreSectContents64* ']]>'
[64] ignoreSectContents ::= Ignore65 ('<![' ignoreSectContents64 ']]>' Ignore65)*
[65] Ignore ::= Char2* - (Char2* ('<![' | ']]>') Char2*)

20.5.1.27. Character reference

[66] CharRef ::= '&#' [0-9]+ ';'
| '&#x' [0-9a-fA-F]+ ';'

20.5.1.28. Entity reference

[67] Reference ::= EntityRef68 | CharRef66
[68] EntityRef ::= '&' Name5 ';'
[69] PEReference ::= '%' Name5 ';'

20.5.1.29. Entity declaration

[70] EntityDecl ::= GEDecl71 | PEDecl72
[71] GEDecl ::= '<!ENTITY' S3 Name5 S3 EntityDef73 S3? '>'
[72] PEDecl ::= '<!ENTITY' S3 '%' S3 Name5 S3 PEDef74 S3? '>'
[73] EntityDef ::= EntityValue9 | (ExternalID75 NDataDecl76?)
[74] PEDef ::= EntityValue9 | ExternalID75

20.5.1.30. External entity declaration

[75] ExternalID ::= 'SYSTEM' S3 SystemLiteral11 | 'PUBLIC' S3 PubidLiteral12 S3 SystemLiteral11
[76] NDataDecl ::= S3 'NDATA' S3 Name5

20.5.1.31. Text declaration

[77] TextDecl ::= '<?xml' VersionInfo24? EncodingDecl80 S3? '?>'

20.5.1.32. Well-formed external parsed entity

[78] extParsedEnt ::= TextDecl77? content43
[79] extPE ::= TextDecl77? extSubsetDecl31

20.5.1.33. Encoding declaration

[80] EncodingDecl ::= S3 'encoding' Eq ('"' EncName81 '"' | "'" EncName81 "'" )
[81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*

20.5.1.34. Notation declarations

[82] NotationDecl ::= '<!NOTATION' S3 Name5 S3 (ExternalID75 | PublicID83) S3? '>'
[83] PublicID ::= 'PUBLIC' S3 PubidLiteral12

20.5.1.35. Characters

[84] Letter ::= BaseChar85 | Ideographic86
[85] BaseChar ::= [#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] |
[#x00D8-#x00F6] | [#x00F8-#x00FF] | [#x0100-#x0131] | [#x0134-#x013E] |
[#x0141-#x0148] | [#x014A-#x017E] | [#x0180-#x01C3] | [#x01CD-#x01F0] |
[#x01F4-#x01F5] | [#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 |
[#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] | [#x03D0-#x03D6] |
#x03DA | #x03DC | #x03DE | #x03E0 | [#x03E2-#x03F3] | [#x0401-#x040C] |
[#x040E-#x044F] | [#x0451-#x045C] | [#x045E-#x0481] | [#x0490-#x04C4] |
[#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] | [#x04EE-#x04F5] |
[#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 | [#x0561-#x0586] | [#x05D0-#x05EA] |
[#x05F0-#x05F2] | [#x0621-#x063A] | [#x0641-#x064A] | [#x0671-#x06B7] |
[#x06BA-#x06BE] | [#x06C0-#x06CE] | [#x06D0-#x06D3] | #x06D5 | [#x06E5-#x06E6] |
[#x0905-#x0939] | #x093D | [#x0958-#x0961] | [#x0985-#x098C] | [#x098F-#x0990] |
[#x0993-#x09A8] | [#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] |
[#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] | [#x0A0F-#x0A10] |
[#x0A13-#x0A28] | [#x0A2A-#x0A30] | [#x0A32-#x0A33] | [#x0A35-#x0A36] |
[#x0A38-#x0A39] | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | [#x0A85-#x0A8B] |
#x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | [#x0AAA-#x0AB0] | [#x0AB2-#x0AB3] |
[#x0AB5-#x0AB9] | #x0ABD | #x0AE0 | [#x0B05-#x0B0C] | [#x0B0F-#x0B10] |
[#x0B13-#x0B28] | [#x0B2A-#x0B30] | [#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D |
[#x0B5C-#x0B5D] | [#x0B5F-#x0B61] | [#x0B85-#x0B8A] | [#x0B8E-#x0B90] |
[#x0B92-#x0B95] | [#x0B99-#x0B9A] | #x0B9C | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] |
[#x0BA8-#x0BAA] | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] |
[#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] | [#x0C35-#x0C39] |
[#x0C60-#x0C61] | [#x0C85-#x0C8C] | [#x0C8E-#x0C90] | [#x0C92-#x0CA8] |
[#x0CAA-#x0CB3] | [#x0CB5-#x0CB9] | #x0CDE | [#x0CE0-#x0CE1] | [#x0D05-#x0D0C] |
[#x0D0E-#x0D10] | [#x0D12-#x0D28] | [#x0D2A-#x0D39] | [#x0D60-#x0D61] |
[#x0E01-#x0E2E] | #x0E30 | [#x0E32-#x0E33] | [#x0E40-#x0E45] | [#x0E81-#x0E82] |
#x0E84 | [#x0E87-#x0E88] | #x0E8A | #x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] |
[#x0EA1-#x0EA3] | #x0EA5 | #x0EA7 | [#x0EAA-#x0EAB] | [#x0EAD-#x0EAE] | #x0EB0 |
[#x0EB2-#x0EB3] | #x0EBD | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | [#x0F49-#x0F69] |
[#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | [#x1102-#x1103] | [#x1105-#x1107] |
#x1109 | [#x110B-#x110C] | [#x110E-#x1112] | #x113C | #x113E | #x1140 | #x114C |
#x114E | #x1150 | [#x1154-#x1155] | #x1159 | [#x115F-#x1161] | #x1163 | #x1165 |
#x1167 | #x1169 | [#x116D-#x116E] | [#x1172-#x1173] | #x1175 | #x119E | #x11A8 |
#x11AB | [#x11AE-#x11AF] | [#x11B7-#x11B8] | #x11BA | [#x11BC-#x11C2] | #x11EB |
#x11F0 | #x11F9 | [#x1E00-#x1E9B] | [#x1EA0-#x1EF9] | [#x1F00-#x1F15] |
[#x1F18-#x1F1D] | [#x1F20-#x1F45] | [#x1F48-#x1F4D] | [#x1F50-#x1F57] | #x1F59 |
#x1F5B | #x1F5D | [#x1F5F-#x1F7D] | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE |
[#x1FC2-#x1FC4] | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] |
[#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 | [#x212A-#x212B] |
#x212E | [#x2180-#x2182] | [#x3041-#x3094] | [#x30A1-#x30FA] | [#x3105-#x312C] |
[#xAC00-#xD7A3]
[86] Ideographic ::= [#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029]
[87] CombiningChar ::= [#x0300-#x0345] | [#x0360-#x0361] | [#x0483-#x0486] |
[#x0591-#x05A1] | [#x05A3-#x05B9] | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] |
#x05C4 | [#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] |
[#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] | [#x0901-#x0903] | #x093C |
[#x093E-#x094C] | #x094D | [#x0951-#x0954] | [#x0962-#x0963] | [#x0981-#x0983] |
#x09BC | #x09BE | #x09BF | [#x09C0-#x09C4] | [#x09C7-#x09C8] | [#x09CB-#x09CD] |
#x09D7 | [#x09E2-#x09E3] | #x0A02 | #x0A3C | #x0A3E | #x0A3F | [#x0A40-#x0A42] |
[#x0A47-#x0A48] | [#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC |
[#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] | [#x0B01-#x0B03] | #x0B3C |
[#x0B3E-#x0B43] | [#x0B47-#x0B48] | [#x0B4B-#x0B4D] | [#x0B56-#x0B57] |
[#x0B82-#x0B83] | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | [#x0BCA-#x0BCD] | #x0BD7 |
[#x0C01-#x0C03] | [#x0C3E-#x0C44] | [#x0C46-#x0C48] | [#x0C4A-#x0C4D] |
[#x0C55-#x0C56] | [#x0C82-#x0C83] | [#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] |
[#x0CCA-#x0CCD] | [#x0CD5-#x0CD6] | [#x0D02-#x0D03] | [#x0D3E-#x0D43] |
[#x0D46-#x0D48] | [#x0D4A-#x0D4D] | #x0D57 | #x0E31 | [#x0E34-#x0E3A] |
[#x0E47-#x0E4E] | #x0EB1 | [#x0EB4-#x0EB9] | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] |
[#x0F18-#x0F19] | #x0F35 | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] |
[#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] | [#x0FB1-#x0FB7] |
#x0FB9 | [#x20D0-#x20DC] | #x20E1 | [#x302A-#x302F] | #x3099 | #x309A
[88] Digit ::= [#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] |
[#x0966-#x096F] | [#x09E6-#x09EF] | [#x0A66-#x0A6F] | [#x0AE6-#x0AEF] |
[#x0B66-#x0B6F] | [#x0BE7-#x0BEF] | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] |
[#x0D66-#x0D6F] | [#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29]
[89] Extender ::= #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 |
#x3005 | [#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE]

Previous | Home | Next
20.4. Constraints | Book Index | 21. Schemas Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schemas Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 21. Schemas Reference

Contents:

The Schema Namespaces
Schema Elements
Primitive Types
Instance Document Attributes

The W3C XML Schema Language (schemas) is a declarative language used to describe the allowed contents of XML documents by assigning types to elements and attributes. The schema language includes several dozen standard types and allows you to define your own custom types. The combination of the information in an XML document instance and the types in that document as defined by the schema is sometimes called the Post Schema Validation Infoset (PSVI).

A schema processor reads both an input XML document and a schema (which is itself an XML document because the W3C XML Schema Language is an XML application) and returns a Boolean result specifying whether the document adheres to the constraints in the schema. A document that satisfies all the schema's constraints and in which all the document's elements and attributes are declared is said to be schema-valid, though in this chapter we will mostly just call such documents valid. A document that does not satisfy all of the constraints is said to be invalid.

21.1. The Schema Namespaces

All standard schema elements are in the http://www.w3.org/2001/XMLSchema namespace. In this chapter, we assume that this URI is mapped to the xs prefix using an appropriate xmlns:xs declaration. This declaration is almost always placed on the root element start-tag:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

In addition, several attributes are used in instance documents to associate schema information with them, including schemaLocation and type. These attributes are in the http://www.w3.org/2001/XMLSchema-instance namespace. In this chapter, we assume that this URI is mapped to the xsi prefix with an appropriate xmlns:xsi declaration on either the element where this attribute appears or one of its ancestors.

In a few cases, schema elements may contain elements from other, arbitrary namespaces or no namespace at all. This occurs primarily inside xs:appinfo and xs:documentation elements, which provide supplementary information about the schema itself or the documents the schema describes to systems that are not schema validators or to people reading the schema.

Finally, most schema elements can have arbitrary attributes from other namespaces. For instance, this allows you to make an xs:attribute element a simple XLink by giving it xlink:type and xlink:href attributes or to identify the language of an xs:notation using an xml:lang attribute. However, this capability is not much used in practice.

Previous | Home | Next
20.5. XML Document Grammar | Book Index | 21.2. Schema Elements
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Schema Elements (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

21.2. Schema Elements

The W3C XML Schema Language defines 42 elements, which naturally divide into several categories:

One root element
xs:schema

Three declaration elements
xs:element, xs:attribute, and xs:notation

Eight elements for defining types
xs:complexContent, xs:complexType, xs:extension, xs:list, xs:restriction, xs:simpleContent, xs:simpleType, and xs:union

Seven elements for defining content models
xs:all, xs:any, xs:anyAttribute, xs:attributeGroup, xs:choice, xs:group, and xs:sequence

Five elements for specifying identity constraints
xs:field, xs:key, xs:keyref, xs:selector, and xs:unique

Three elements for assembling schemas out of component parts
xs:import, xs:include, and xs:redefine

12 facet elements for constraining simple types
xs:enumeration, xs:fractionDigits, xs:length, xs:maxExclusive, xs:maxInclusive, xs:maxLength, xs:minExclusive, xs:minInclusive, xs:minLength, xs:pattern, xs:totalDigits, and xs:whiteSpace

Three elements for documenting schemas
xs:appinfo, xs:annotation, and xs:documentation

Elements in this section are arranged alphabetically from xs:any to xs:whiteSpace. Each element begins with a sample implementation in the following form:

<xs:elementName
   attribute1 = "allowed attribute values"
   attribute2 = "allowed attribute values"
>
  <!-- Content model -->
</xs:elementName>

Most attribute values can be expressed as one of the 44 XML Schema built-in simple types, such as xs:string, xs:ID, or xs:integer. Some attribute values are specified as an enumeration of the legal values in the form ( value1 | value2 | value3 | etc. ). In this case, the default value, if there is one, is given in boldface.

Element content models are given in a comment in the form they might appear in an ELEMENT declaration in a DTD. For example, an xs:all element may contain a single optional xs:annotation child element followed by zero or more xs:element elements. Thus its content model is written like this:

<!-- ( xs:annotation?, xs:element* ) -->
xs:all
<xs:all
  id = "ID"
  maxOccurs = "1"
  minOccurs = "(0 | 1)">
  <!-- ( xs:annotation?, xs:element* ) -->
</xs:all>

The xs:all element indicates that every element represented by one of its child xs:element elements must appear. However, the order of the child elements in the instance element does not matter. For example, an xs:all element can require that each FullName element have exactly one FirstName child and exactly one LastName child, but that the order of the two child elements does not matter; the first name can come first or the last name can come first.

The xs:all element must be the top group in its content model (i.e., an xs:group, xs:choice, or xs:sequence cannot contain an xs:all element). The complete group represented by the xs:all element can occur either zero or one time as indicated by its minOccurs and maxOccurs attributes. By default it must occur exactly once. Furthermore, the minOccurs attribute of each of the individual xs:element elements inside the xs:all element must also be set to either 0 or 1, and the maxOccurs attribute of each of these elements must be set to 1. xs:all cannot indicate, for example, that a FullName element must contain between zero and five FirstName s and between one and three LastName s in any order.

xs:annotation
<xs:annotation
  id = "ID">
  <!-- ( xs:appinfo | xs:documentation )* -->
</xs:annotation>

The xs:annotation element is ignored by schema validators. Its purpose is to provide metainformation about the schema or schema element in which it appears. Information intended for human readers is placed in xs:documentation child elements. Information intended for software programs is placed in xs:appinfo child elements.

xs:any
<xs:any
  id = "ID"
  maxOccurs = "nonNegativeInteger | unbounded"
  minOccurs = "nonNegativeInteger"
  namespace = " ##any | ##other | anyURI* ##targetNamespace? ##local? "
  processContents = " lax | skip | strict ">
  <!-- xs:annotation? -->
</xs:any>

The wildcard element xs:any is useful when writing schemas for languages such as XSLT that routinely include markup from multiple vocabularies that are unknown when the schema is written. It indicates that between minOccurs and maxOccurs, elements from one or more namespaces identified by the namespace attribute may appear at that position in a content model. As well as literal namespace URIs, the special value ##targetNamespace can be included in the list to indicate that any element from the schema's target namespace can be used. The special value ##local can be included in the list to indicate that elements not in any namespace can be used. Instead of the list of namespace URIs, you can use the special value ##any to indicate that all elements from any namespace or no namespace are allowed, or the special value ##other to indicate that elements from namespaces other than the schema's target namespace can be used.

The processContents attribute indicates whether the elements represented by xs:any have to be declared or whether they can be completely unfamiliar to the schema. It has one of these three values:

strict
Elements represented by this xs:any element must be declared or have an xsi:type attribute. Furthermore, the element must be valid according to its declaration or type.

skip
Elements represented by this xs:any element need not be declared in the schema and need not be valid even if they are declared.

lax
Elements represented by this xs:any element must be validated if they are declared or if they have an xsi:type attribute, but must not be validated if they are neither declared nor have an xsi:type attribute.

The default value is strict.

xs:anyAttribute
<xs:anyAttribute
  id = "ID"
  namespace = "##any | ##other | anyURI* ##targetNamespace? ##local?"
  processContents = "(lax | skip | strict)" >
  <!-- (xs:annotation?) -->
</xs:anyAttribute>

The xs:anyAttribute element appears inside xs:complexType elements, where it indicates that elements of that type can have any attribute from one or more namespaces. It can also appear inside xs:attributeGroup elements, where it adds attributes from one or more namespaces as potential members of the group. The namespace attribute contains a whitespace-separated list of the namespace URIs that are allowed for this element's attributes. As well as literal namespace URIs, the special value ##targetNamespace can be included in the list to indicate that any attribute from the schema's target namespace can be used. The special value ##local can be included in the list, indicating that attributes not in any namespace (unprefixed attributes) may be used. Instead of the list of namespace URIs, you can use the special value ##any to indicate that all attributes from any namespace are allowed or the special value ##other to indicate that attributes from namespaces other than the schema's target namespace can be used.

The processContents attribute indicates whether the attributes themselves have to be declared, generally as top-level attributes. It has one of these three values:

strict
Attributes represented by this xs:anyAttribute element must be declared, and the attribute must be valid according to its declaration. This is the default.

lax
Attributes represented by this xs:anyAttribute element must be validated if they are declared, but must not be validated if they are not declared.

skip
Attributes represented by this xs:anyAttribute element need not be declared in the schema and need not be valid even if they are declared.

xs:appinfo
<xs:appinfo
  source = "anyURI">
  <!-- any well-formed XML markup -->
</xs:appinfo>

The xs:appinfo element appears exclusively inside xs:annotation elements, where it provides machine-readable information about the schema or schema element it's documenting. It has no effect on schema validation. It can contain absolutely any XML markup: an XSLT stylesheet for the schema, a schema for the schema, a schema in a different schema language such as Schematron, or anything else you can imagine. The only restriction is that the contents must be well-formed. Alternately, instead of or in addition to including this information directly, the source attribute can point to it using a URI.

xs:attribute
<xs:attribute
  default = "string"
  fixed = "string"
  form = "( qualified | unqualified )
  id = "ID"
  name = "NCName"
  ref = "QName"
  type = "QName"
  use = "( optional | prohibited | required )">
  <!-- ( xs:annotation?, xs:simpleType? ) -->
</xs:attribute>

The xs:attribute element declares an attribute. Inside an xs:complexType element it indicates that elements of that type can have an attribute with the specified name and type.

Attributes

id, optional
An XML name unique among all of the ID-type attributes in this schema document.

default, optional
The default value of the attribute reported for those elements in the instance document that do not contain an explicit specification of this attribute.

fixed, optional
A default value for this attribute that may not be overridden in the instance document. An xs:attribute element cannot have both fixed and default attributes.

form, optional
If this has the value qualified, then the attribute must be in the schema's target namespace. If this has the value unqualified, then the attribute must not be in any namespace. The default value for this is set by the attributeFormDefault attribute on the root xs:schema element.

name, optional
The local name of the attribute.

ref, optional
The qualified name of the attribute declared by a top-level xs:attribute element elsewhere in the schema. Either the name or ref attribute should be provided, but not both.

type, optional
The qualified name of the type of the attribute, either a built-in simple type such as xs:integer or a user-defined simple type.

use, optional
One of the three keywords, optional, prohibited, or required, which have the following meanings:

optional
Authors of instance documents may or may not include this attribute as they choose. This is the default.

prohibited
Authors of instance documents must not include this attribute.

required
Authors of instance documents must include this attribute on all elements of the requisite type.

Contents

The xs:attribute element may contain a single xs:annotation element to describe itself. This has no effect on the attribute type.

In place of a type attribute, the xs:attribute element may contain a single xs:simpleType element that provides an anonymous type for the attribute derived from a base simple type.

xs:attributeGroup
<xs:attributeGroup
  id = "ID"
  name = "NCName"
  ref = "QName">
  <!--
    ( xs:annotation?, (xs:attribute | xs:attributeGroup)*,
      xs:anyAttribute? )
   -->
</xs:attributeGroup>

The xs:attributeGroup element is used in two ways. At the top level of the schema, it has a name attribute and defines a new attribute group. The attributes in the group are indicated by the child elements of the xs:attributeGroup element. Inside an xs:complexType element or another xs:attributeGroup, it has a ref attribute but no name and adds the attributes in the referenced group to the type or group's list of attributes.

xs:choice
<xs:choice
  id = "ID"
  maxOccurs = "( nonNegativeInteger | unbounded )"
  minOccurs = "nonNegativeInteger">
  <!--
    ( xs:annotation?, (xs:element | xs:group | xs:choice
    | xs:sequence | xs:any)*)
  -->
</xs:choice>

The xs:choice element indicates that any element or group represented by one of its child elements may appear at that position in the instance document. At least minOccurs elements from the choice must appear. At most maxOccurs elements from the choice must appear. The default for both minOccurs and maxOccurs is 1.

xs:complexContent
<xs:complexContent
  id = "ID"
  mixed = "( true | false )">
  <!-- ( xs:annotation?, (xs:restriction | xs:extension) ) -->
</xs:complexContent>

The xs:complexContent element is used inside xs:complexType elements to derive a new complex type from an existing complex type by restriction or extension. When deriving by extension, the mixed attribute must have the same value as the base type's mixed attribute. When deriving by restriction, the mixed attribute can have the value false to disallow mixed content that would be allowed in the base type. It can have the value true only if the base type allows mixed content. In other words, a derived type can disallow mixed content that's allowed in the base type, but cannot allow it if the base type doesn't already allow it.

xs:complexType
<xs:complexType
  abstract = "( true | false )"
  block = "( #all | extension | restriction )"
  final = "( #all | extension | restriction )"
  id = "ID"
  mixed = "( true | false )"
  name = "NCName"
  >
  <!-- ( xs:annotation?, (xs:simpleContent | xs:complexContent
       | ((xs:group | xs:all | xs:choice | xs:sequence)?,
         ((xs:attribute | xs:attributeGroup)*, xs:anyAttribute?)))) -->
</xs:complexType>

The xs:complexType element defines a new complex type, that is, an element type that can potentially contain either child elements, attributes, or both. The valid child elements and attributes for elements of this type are specified by the contents of the xs:complexType element. The mixed attribute specifies whether the complex type is allowed to contain text interspersed with its child elements. If the xs:complexType element is a top-level element, then it has a name attribute and defines a named type. Otherwise, if the xs:complexType element appears inside an xs:element element, then it does not have a name attribute and defines an anonymous type for that element alone.

If the abstract attribute has the value true, then no elements of this type can be included in instance documents--only elements of subtypes derived from this type, which are marked as elements of this type by an xsi:type attribute. If the final attribute has the value restriction, then this type cannot be subtyped by restriction. If the final attribute has the value extension, then this type cannot be subtyped by extension. If the final attribute has the value #all, then this type cannot be subtyped by either restriction or extension. The default value of the final attribute is set by the finalDefault attribute on the root xs:schema element. If the block attribute has the value extension or restriction, then instances of this type cannot be replaced in instance documents by instances of subtypes derived from this type by extension or restriction, respectively, though such subtypes may still be defined and used for other elements. If the block attribute has the value #all, then this type cannot be replaced in instance documents by instances of any subtype. The default value of the block attribute is set by the blockDefault attribute on the root xs:schema element.

xs:documentation
<xs:documentation
  source = "anyURI"
  xml:lang = "language">
  <!-- any well-formed XML markup -->
</xs:documentation>

The xs:documentation element appears exclusively inside xs:annotation elements where it provides human-readable information about the schema or schema element it's annotating. It has no effect on schema validation. It can contain absolutely any XML markup: XHTML, DocBook, or just plain text. The only restriction is that the contents must be well-formed. Alternately, instead of or in addition to including this information directly, the source attribute can point to it using a URI. The xml:lang attribute can be used to indicate the language in which the description is written. You could even include multiple xs:documentation elements in different languages.

xs:element
<xs:element
  abstract = "( true | false )"
  block = "( #all | extension | restriction | substitution )"
  default = "string"
  final = "( #all | extension | restriction )"
  fixed = "string"
  form = "( qualified | unqualified )"
  id = "ID"
  maxOccurs = "( nonNegativeInteger | unbounded )"
  minOccurs = "nonNegativeInteger"
  name = "NCName"
  nillable = "( true | false )"
  ref = "QName"
  substitutionGroup = "QName"
  type = "QName">
  <!-- ( xs:annotation?,
       ((xs:simpleType | xs:complexType)?,
       (xs:unique | xs:key | xs:keyref)*) ) -->
</xs:element>

The xs:element element declares an element, including its name and type. Used at the top level of the schema, it indicates a potential root element. Used inside an xs:complexType element, it indicates a potential child element of another element. Alternately, instead of specifying a name and a type, it can have a ref attribute that points to an element declaration elsewhere in the schema.

Attributes

id, optional
id is an XML name unique within ID-type attributes in this schema document.

abstract, optional
If the abstract attribute has the value true, then only elements from this element's substitution group are allowed in instance documents, not elements of this type itself.

default, optional
default is the default value of the element reported for empty elements of this type in the instance document.

block, optional
If the block attribute contains the value extension, restriction, or substitution, then this element cannot be replaced in instance documents and substitution groups by instances of subtypes derived from this element's type by extension, restriction, or substitution, respectively. If the block attribute has the value #all, then this element cannot be replaced in instance documents by instances of any subtype of the element's type.

final, optional
The final attribute controls which elements can refer to this element as the head of their substitution group. If the value contains the keyword restriction, then restrictions of this element's type cannot do so. If the value contains the keyword extension, then extensions of this element's type cannot do so. If the value is #all, then neither extensions nor restrictions of this type can do so.

form, optional
If the form attribute has the value qualified, then the element is in the schema's target namespace. If it has the value unqualified, then the element is not in any namespace. The default value is set by the elementFormDefault attribute on the root xs:schema element. This attribute can only be used on locally declared elements. All globally declared elements are always in the schema's target namespace.

maxOccurs, optional
This signifies the maximum number of times this element may be repeated in valid instance documents.

minOccurs, optional
This signifies the minimum number of times this element must be repeated in valid instance documents.

name, optional
This represents the name of the element.

nillable, optional
If nillable has the value true, then this element can be specified as being "nil" using an xsi:nil="true" attribute in the instance document.

ref, optional
This represents the qualified name of the element declared by a top-level xs:element element elsewhere in the schema.

type, optional
This is the qualified name of the type of the element, either a built-in simple type such as xs:integer or a user-defined type.

substitutionGroup, optional
This is the qualified name of a globally declared element for which this element may substitute in instance documents.

Contents

The xs:element element may contain an optional xs:annotation. If and only if the xs:element element does not have a type attribute, then it must have either an xs:simpleType child element or an xs:complexType child element that provides an anonymous type for this element. Finally, it may have any number of xs:key, xs:keyref, and xs:unique elements to set uniqueness and identity constraints.

xs:enumeration
<xs:enumeration
  id = "ID"
  value = "anySimpleType">
  <!-- (xs:annotation?) -->
</xs:enumeration>

The xs:enumeration facet element is used inside xs:restriction elements to derive new simple types by listing all valid values. The value attribute contains a single valid value of the type specified by the parent xs:restriction's base attribute. This xs:restriction element contains one xs:enumeration child element for each valid value.

xs:extension
<xs:extension
  base = "QName"
  id = "ID">
  <!-- (xs:annotation?,
       ((xs:group | xs:all | xs:choice | xs:sequence)?,
       ((xs:attribute | xs:attributeGroup)*, xs:anyAttribute?))) -->
</xs:extension>

The xs:extension element is used inside xs:simpleContent and xs:complexContent elements to derive a new complex type that adds attributes and/or child elements not present in the base type. The base type being extended is given by the value of the base attribute. The child elements and attributes added to the base type's content model are specified by the content of the xs:extension element. An instance of such an extended type must have all the child elements required by the base type followed by all the child elements required in the xs:extension.

xs:field
<xs:field
  id = "ID"
  xpath = "XPath expression">
  <!-- (xs:annotation?) -->
</xs:field>

One or more xs:field elements are placed inside each xs:unique, xs:key, and xs:keyref element to define a value calculated by the XPath expression in the xpath attribute. The context node for this expression is set in turn to each element in the node set selected by the xs:selector element.

Not all XPath expressions are allowed here. In particular, the XPath expression must limit itself to the child axis, except for the last step, which may use the attribute axis. The only node tests used are name tests (element and attribute names, the * wildcard, and the prefix:* wildcard). Abbreviated syntax must be used, and predicates are not allowed. Thus, person/name/first_name/@id is a legal XPath expression for this attribute, but person//name/@id is not. Several instances of this restricted form of XPath expression can be combined with the vertical bar so that person/name/first_name/@id | person/name/last_name/@id is also an acceptable XPath expression. Finally, the XPath expression may begin with .// so that .//name/@id is legal. However, this is the only place the descendant-or-self axis can be used. No other forms of XPath expression are allowed here.

xs:fractionDigits
<xs:fractionDigits
  fixed = "( true | false )"
  id = "ID"
  value = "nonNegativeInteger" >
  <!-- (xs:annotation?) -->
</xs:fractionDigits>

The xs:fractionDigits facet element is used when deriving from xs:decimal (and its subtypes) by restriction. It limits the number of digits allowed after the decimal point to at most the number specified by the value attribute. This sets only the maximum number of digits after the decimal point. If you want to set the minimum number of digits required, you'll have to use the xs:pattern element instead. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of fractionDigits given here.

xs:group
<xs:group
  name = "NCName"
  ref  = "NCName"
  minOccurs = "nonNegativeInteger"
  maxOccurs = "nonNegativeInteger | unbounded">
  <!-- ( xs:annotation?, (xs:all | xs:choice | xs:sequence) ) -->
</xs:group>

The xs:group element can be used in two ways. As a top-level element with a name attribute, it defines a model group that can be referenced from complex types elsewhere in the schema. The content model of the group is established by a child xs:all, xs:choice, or xs:sequence element.

The second use is inside a xs:complexType element. Here the xs:group element indicates that the contents of the group should appear at this point in the instance document at least as many times as indicated by the minOccurs attribute and at most as many times as indicated by the maxOccurs attribute. The default for both of these is 1. The group to be included is indicated by the ref attribute that contains the name of a top-level xs:group element found elsewhere in the schema.

xs:import
<xs:import
  id = "ID"
  namespace = "anyURI"
  schemaLocation = "anyURI" >
  <!-- ( xs:annotation? ) -->
</xs:import>

Since each schema document has exactly one target namespace, the top-level xs:import element is needed to create schemas for documents that involve multiple namespaces. The namespace attribute contains the namespace URI for the application that the imported schema describes. If the imported schema describes elements and types in no namespace, then the namespace attribute is omitted. The optional schemaLocation attribute contains a relative or absolute URL pointing to the actual location of the schema document to import.

There is no limit to import depth. Schema A can import schema B, which itself imports schema C and schema D. In such a case, schema A can use definitions and declarations from all four schemas. Even recursion (schema A imports schema B, which imports schema A) is not prohibited. Since the imported schema must describe a different target namespace than the importing schema, conflicts between definitions in the multiple schemas are normally not a problem. However, if conflicts do arise, then the schema is in error and cannot be used. There are no precedence rules for choosing between multiple conflicting definitions or declarations.

xs:include
<xs:include
  id = "ID"
  schemaLocation = "anyURI">
  <!-- (annotation?) -->
</xs:include>

The top-level xs:include element is used to divide a schema into multiple separate documents. The schemaLocation attribute contains a relative or absolute URI pointing to the schema document to include. It differs from xs:import in that all included files describe the same target namespace.

There is no limit to inclusion depth. Schema A can include schema B, which itself includes schema C and schema D. In such a case, schema A can use definitions and declarations from all four documents. Even recursion (schema A includes schema B, which includes schema A) is not prohibited, though it is strongly discouraged. Instance documents would refer only to the top-level schema A in their xsi:schemaLocation or xsi:noNamespaceSchemaLocation attribute.

Validation is performed after all includes are resolved. If there are any conflicts between the including schema and an included schema--for instance, one schema declares that the FullName element has a simple type, and another declares that the FullName element has a complex type--then the schema is in error and cannot be used. Most of the time schemas should be carefully managed so that each element and type is defined in exactly one schema document.

xs:key
<xs:key
  id = "ID"
  name = "NCName" >
  <!-- (xs:annotation?, (xs:selector, xs:field+) ) -->
</xs:key>

Keys establish uniqueness and co-occurrence constraints among various nodes in the document. For example, you can define a key for an Employee element based on its EmployeeNumber child element and then require that each Assignment element have a team attribute whose contents are a list of employee keys.

The xs:key element defines a new key. It appears only as a child of an xs:element element following the element's type. The name of the key is specified by the name attribute. The elements that have a value for this key are identified by the xs:selector child element. The value of the key for each of these nodes is given by the xs:field child element and must be unique within that set. If there is more than one xs:field child element, then the key has multiple values.

xs:keyref
<xs:keyref
  id = "ID"
  name = "NCName"
  refer = "QName" >
  <!-- (xs:annotation?, (xs:selector, xs:field+) ) -->
</xs:keyref>

The xs:keyref element is placed inside xs:element elements to indicate that a certain part of those elements must match the key with the name given by the refer attribute. The value that is matched against the specified key is determined by the XPath expressions used in the xs:selector child and the xs:field child elements.

xs:length
<xs:length
  fixed = "( true | false )"
  id = "ID"
  value = "nonNegativeInteger" >
  <!-- (xs:annotation?) -->
</xs:length>

The xs:length facet element specifies the exact number of characters in a type derived from xs:string, xs:QName, xs:language, xs:anyURI, or xs:NOTATION. When applied to a list type such as xs:ENTITIES, this facet specifies the number of items in the list. Finally, when applied to xs:hexBinary and xs:base64Binary, it specifies the number of bytes in the decoded data, rather than the number of characters in the encoded data. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of length given here.

xs:list
<xs:list
  id = "ID"
  itemType = "QName" >
  <!-- (xs:annotation?, (xs:simpleType?) ) -->
</xs:list>

The xs:list element is placed inside an xs:simpleType element to derive a new list simple type from a base atomic type identified by the itemType attribute. Alternately, instead of referencing an existing simple type with itemType, a new anonymous atomic type for the list can be created by an xs:simpleType child element. In either case, the newly defined simple type is a whitespace-separated list of atomic-type instances.

xs:maxExclusive
<xs:maxExclusive
  fixed = "( true | false )"
  id = "ID"
  value = "anySimpleType" >
  <!-- (xs:annotation?) -->
</xs:maxExclusive>

The xs:maxExclusive facet element applies to all ordered types, including xs:decimal, xs:float, xs:double, xs:date, xs:duration, xs:dateTime, xs:time, xs:gDay, xs:gMonthYear, xs:gMonth, xs:gYear, and their subtypes. The value attribute contains the maximum value in a form appropriate for the type. For example, the maximum for a type derived from xs:integer might be 75; the maximum for a type derived from xs:double might be 1.61803; and the maximum for a type derived from xs:date might be 2001-09-26. All instances of this type must be strictly less than the maximum value. They may not be equal to the maximum. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of maxExclusive given here.

xs:maxInclusive
<xs:maxInclusive
  fixed = "( true | false )"
  id = "ID"
  value = "anySimpleType" >
  <!-- (xs:annotation?) -->
</xs:maxInclusive>

The xs:maxInclusive facet element applies to all ordered types, including xs:decimal, xs:float, xs:double, xs:date, xs:duration, xs:dateTime, xs:time, xs:gDay, xs:gMonthYear, xs:gMonth, xs:gYear, and their subtypes. The value attribute contains the maximum value in a form appropriate for the type. For example, the maximum for a type derived from xs:integer might be 75; the maximum for a type derived from xs:double might be 1.61803; and the maximum for a type derived from xs:date might be 2001-09-26. All instances of this type must be less than or equal to the maximum value. They may be equal to the maximum. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of maxInclusive given here.

xs:maxLength
<xs:maxLength
  fixed = "( true | false )"
  id = "ID"
  value = "nonNegativeInteger" >
  <!-- (xs:annotation?) -->
</xs:maxLength>

The xs:maxLength facet element specifies the maximum number of characters in a type derived from xs:string, xs:QName, xs:language, xs:anyURI, or xs:NOTATION. It can also be used to restrict xs:hexBinary and xs:base64Binary. However, in this case, it refers to the maximum number of bytes in the decoded data rather than the maximum number of characters in the encoded data. Finally, when applied to a list type such as xs:IDREFS, it describes the maximum number of items in the list. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of maxLength given here.

xs:minExclusive
<xs:minExclusive
  fixed = "( true | false )"
  id = "ID"
  value = "anySimpleType" >
  <!-- (xs:annotation?) -->
</xs:minExclusive>

The xs:minExclusive facet element applies to all ordered types, including xs:decimal, xs:float, xs:double, xs:date, xs:duration, xs:dateTime, xs:time, xs:gDay, xs:gMonthYear, xs:gMonth, xs:gYear, and their subtypes. The value attribute contains the minimum value in a form appropriate for the type. For example, the minimum for a type derived from xs:integer might be 75; the minimum for a type derived from xs:double might be 1.61803; and the minimum for a type derived from xs:date might be 2001-09-26. All instances of this type must be strictly greater than the minimum value. They may not be equal to the minimum. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of minExclusive given here.

xs:minInclusive
<xs:minInclusive
  fixed = "( true | false )"
  id = "ID"
  value = "anySimpleType" >
  <!-- (xs:annotation?) -->
</xs:minInclusive>

The xs:minInclusive facet element applies to all ordered types, including xs:decimal, xs:float, xs:double, xs:date, xs:duration, xs:dateTime, xs:time, xs:gDay, xs:gMonthYear, xs:gMonth, xs:gYear, and their subtypes. The value attribute contains the minimum value in a form appropriate for the type. For example, the minimum for a type derived from xs:integer might be 75; the minimum for a type derived from xs:double might be 1.61803; and the minimum for a type derived from xs:date might be 2001-09-26. All instances of this type must be greater than or equal to the minimum value. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of minInclusive given here.

xs:minLength
<xs:minLength
  fixed = "( true | false )"
  id = "ID"
  value = "nonNegativeInteger" >
  <!-- (xs:annotation?) -->
</xs:minLength>

The xs:minLength facet element specifies the minimum number of characters in a type derived from xs:string, xs:QName, xs:language, xs:anyURI, or xs:NOTATION. It can also be used to restrict xs:hexBinary and xs:base64Binary. However, in this case, it refers to the minimum number of bytes in the decoded data rather than the minimum number of characters in the encoded data. Finally, when applied to a list type such as xs:IDREFS, it describes the minimum number of items in the list. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of maxLength given here.

xs:notation
<xs:notation
  id = "ID"
  name = "NCName"
  public = "PUBLIC identifier"
  system = "anyURI" >
  <!-- (xs:annotation?) -->
</xs:notation>

The top-level xs:notation element defines a notation. It's the schema equivalent of the DTD's <!NOTATION> declaration. Each notation has a name, a public ID, and a system ID identified by the relevant attribute on this element.

xs:pattern
<xs:pattern
  id = "ID"
  value = "regular expression" >
  <!-- (xs:annotation?) -->
</xs:pattern>

The xs:pattern facet element is used to derive new simple types by specifying a regular expression against which values of the type are compared. It applies to all simple types. The schema regular-expression grammar is quite similar to that used in Perl 5.6 and later. (The big change from earlier versions of Perl is support for Unicode character class-based regular expressions.) Most strings and characters match themselves, but a few characters have special meanings, as summarized in Table 21-1. In this table, A and B are subexpressions; n and m are non-negative integers; a, b, c, and d are all single Unicode characters; and X is a name.

Table 21-1. XML Schema regular-expression syntax

Pattern

Matches

(A)

A string that matches A

A | B

A string that matches A or a string that matches B

AB

A string that matches A followed by a string that matches B

A?

Zero or one repetitions of a string that matches A

A*

Zero or more repetitions of a string that matches A

A+

One or more repetitions of a string that matches A

A{n,m}

A sequence of between n and m strings, each of which matches A

A{n}

A sequence of exactly n strings, each of which matches A

A{n,}

A sequence of at least n strings, each of which matches A

[abcd]

Exactly one of the characters listed inside the square brackets

[^abcd]

Exactly one character not listed inside the square brackets

[a-z]

Exactly one character with a Unicode value between a and z, inclusive

[a-z-[d-h]]

Exactly one character included in the outer range but not in the inner range

\n

The newline; &#x0A;

\r

The carriage return; &#x0D;

\t

The tab; &#x09;

\\

The backslash, \

\|

The vertical bar, |

\.

The period, .

\-

The hyphen, -

\^

The caret, ^

\?

The question mark, ?

\*

The asterisk, *

\+

The plus sign, +

\{

The left curly brace, {

\}

The right curly brace, }

\(

The left parenthesis, (

\)

The right parenthesis, )

\[

The left square bracket, [

\]

The right square bracket, ]

.

Any single character except the carriage return or line feed

\s

A space, tab, carriage return, or line feed

\S

Any single character except a space, tab, carriage return, or line feed

\i

An XML name-start character

\c

An XML name character

\d

A decimal digit

\D

Any single character except a decimal digit

\w

A "word character," that is, any single character that is not a punctuation mark, a separator, or "other" (as defined by Unicode)

\W

Any single character that is a punctuation mark, a separator, or "other" (as defined by Unicode)

\p{X}

Any single character from the Unicode character class X; character class names are listed in Table 21-2

\P{X}

Any single character not in the Unicode character class X

\p{IsX}

Any single character from the Unicode character block X. Block names include BasicLatin, Latin-1Supplement, LatinExtended-A, LatinExtended-B, IPAExtensions, SpacingModifierLetters, CombiningDiacriticalMarks, Greek, Cyrillic, Armenian, Hebrew, Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar, Georgian, HangulJamo, Ethiopic, Cherokee, UnifiedCanadianAboriginalSyllabics, Ogham, Runic, Khmer, Mongolian, LatinExtendedAdditional, GreekExtended, GeneralPunctuation, SuperscriptsandSubscripts, CurrencySymbols, CombiningMarksforSymbols, LetterlikeSymbols, NumberForms, Arrows, MathematicalOperators, MiscellaneousTechnical, ControlPictures, OpticalCharacterRecognition, EnclosedAlphanumerics, BoxDrawing, BlockElements, GeometricShapes, MiscellaneousSymbols, Dingbats, BraillePatterns, CJKRadicalsSupplement, KangxiRadicals, IdeographicDescriptionCharacters, CJKSymbolsandPunctuation, Hiragana, Katakana, Bopomofo, HangulCompatibilityJamo, Kanbun, BopomofoExtended, EnclosedCJKLettersandMonths, CJKCompatibility, CJKUnifiedIdeographsExtensionA, CJKUnifiedIdeographs, YiSyllables, YiRadicals, HangulSyllables, HighSurrogates, HighPrivateUseSurrogates, LowSurrogates, PrivateUse, CJKCompatibilityIdeographs, AlphabeticPresentationForms, ArabicPresentationForms-A, CombiningHalfMarks, CJKCompatibilityForms, SmallFormVariants, ArabicPresentationForms-B, Specials, HalfwidthandFullwidthForms, Specials, OldItalic, Gothic, Deseret, ByzantineMusicalSymbols, MusicalSymbols, MathematicalAlphanumericSymbols, CJKUnifiedIdeographsExtensionB, CJKCompatibilityIdeographsSupplement, Tags, and PrivateUse. The characters from many of these blocks are shown in Chapter 26.

\P{IsX}

Any single character not in the Unicode character block X

Table 21-2. Unicode character classes

Unicode character class | Includes
L | Letters
Lu | Uppercase letters
Ll | Lowercase letters
Lt | Titlecase letters
Lm | Modifier letters
Lo | Other letters
M | All marks
Mn | Nonspacing marks
Mc | Spacing combining marks
Me | Enclosing marks
N | Numbers
Nd | Decimal digits
Nl | Number letters
No | Other numbers
P | Punctuation
Pc | Connector punctuation
Pd | Dashes
Ps | Opening punctuation
Pe | Closing punctuation
Pi | Initial quotes
Pf | Final quotes
Po | Other punctuation
Z | Separators
Zs | Spaces
Zl | Line breaks
Zp | Paragraph breaks
S | Symbols
Sm | Mathematical symbols
Sc | Currency symbols
Sk | Modifier symbols
So | Other symbols
C | Other characters (nonletters, nonsymbols, non-numbers, nonseparators)
Cc | Control characters
Cf | Format characters
Co | Private use characters
Cn | Unassigned code points

xs:redefine
<xs:redefine
  id = "ID"
  schemaLocation = "anyURI" >
  <!-- (annotation | (simpleType | complexType | group | attributeGroup))*
    -->
</xs:redefine>

The xs:redefine element is used much like xs:include. That is, it inserts definitions and declarations for the same target namespace from a schema document found at a URL specified by the schemaLocation attribute. However, unlike xs:include, xs:redefine can override type, model group, and attribute group definitions from the included schema. The new type and group definitions are children of the xs:redefine element. They must extend or restrict the original definition of the redefined type or group. Note, however, that xs:redefine cannot override element and attribute declarations made in the included schema.

xs:restriction
<xs:restriction
  base = "QName"
  id = "ID">
  <!-- ( xs:annotation?, (
        (xs:simpleType?,
        ( xs:minExclusive | xs:minInclusive | xs:maxExclusive
        | xs:maxInclusive | xs:totalDigits | xs:fractionDigits
        | xs:length | xs:minLength | xs:maxLength | xs:enumeration
        | xs:whiteSpace | xs:pattern)*)
        | ( (xs:group | xs:all | xs:choice | xs:sequence)?,
          ((xs:attribute | xs:attributeGroup)*, xs:anyAttribute?) )
       ) -->
</xs:restriction>

The xs:restriction element derives a new type from an existing base type identified by either a base attribute or an xs:simpleType child element. When deriving by restriction, all valid values of the derived type must also be legal values of the base type. However, the reverse is not true. The valid values of the derived type are a subset (almost always a proper subset) of the valid values of the base type. For derived simple types, the allowed values are identified by the various facet child elements of the xs:restriction element. For derived complex types, the allowed values are identified by the same elements you'd find inside an xs:complexType element--that is, zero or one group elements such as xs:all, xs:choice, or xs:sequence followed by attribute representation elements such as xs:attribute, xs:attributeGroup, and xs:anyAttribute.

xs:schema
<xs:schema
  attributeFormDefault = "( qualified | unqualified )"
  elementFormDefault   = "( qualified | unqualified )"
  blockDefault = "( #all | extension | restriction | substitution )
  finalDefault = "( #all | extension | restriction )
  id = "ID"
  targetNamespace = "anyURI"
  version = "token"
  xml:lang = "language" >
  <!-- (
         (xs:include | xs:import | xs:redefine | xs:annotation)*,
         (((xs:simpleType | xs:complexType | xs:group
          | xs:attributeGroup) | xs:element | xs:attribute
          | xs:notation), xs:annotation*)*
   ) -->
</xs:schema>

xs:schema is the root element of all schema documents. It contains all the top-level elements described elsewhere in this chapter. First come all the elements that somehow reference other schema documents, including xs:include, xs:import, and xs:redefine. These are followed by the various elements that define types and groups and declare elements and attributes. As usual, xs:annotation elements can be placed anywhere that is convenient.

Attributes

id, optional
id is an XML name unique within ID-type attributes in this schema document.

targetNamespace, optional
The namespace URI for the XML application described by this schema. If not present, then this schema describes elements in no namespace. If the XML application uses multiple namespaces, then there must be a separate schema document for each different namespace. These schemas can be connected with xs:import elements.

version, optional
You can use this attribute to specify the version of the schema, e.g., 1.0, 1.0.1, 1.1, 1.2, 1.3b1, 2.0, etc. This refers to the version of the specific schema, not the version of the W3C XML Schema Language used in this document.

blockDefault, optional
The blockDefault attribute establishes the default value for the block attributes of xs:element and xs:complexType elements in this schema.

finalDefault, optional
The finalDefault attribute establishes the default value for the final attributes of xs:element and xs:complexType elements in this schema.

xml:lang, optional
This is the human language in which this schema is primarily written, such as en or fr-CA.

attributeFormDefault, optional
This sets the default value for the form attribute of xs:attribute elements. This specifies whether or not locally declared attributes are namespace qualified by the target namespace. If this attribute is not used, locally declared attributes are unqualified unless the form attribute of the xs:attribute element has the value qualified.

elementFormDefault, optional
This sets the default for the form attribute of xs:element elements. This specifies whether locally declared elements are namespace-qualified by the target namespace. By default, locally declared elements are unqualified unless the form attribute of the xs:element element has the value qualified.

WARNING: elementFormDefault is part of a misguided effort to make child elements and attributes equivalent. If you're using namespaces at all, just put all elements in the target namespace of the schema and set elementFormDefault to qualified.

xs:selector
<xs:selector
  id = "ID"
  xpath = "XPath expression" >
  <!-- (xs:annotation?) -->
</xs:selector>

A single xs:selector element is placed inside each xs:unique, xs:key, and xs:keyref element to specify the element nodes for which the key on key reference is defined. The node set is selected by an XPath expression contained in the value of the xpath attribute. The context node for this XPath expression is the element matched by the xs:element declaration in which the xs:unique, xs:key, or xs:keyref element appears.

Not all XPath expressions are allowed here. In particular, the XPath expression must be an abbreviated location path that limits itself to the child axis. The only node tests used are element name, the * wildcard, and the prefix:* wildcard. Abbreviated syntax must be used; predicates are not allowed. Thus, person/name/first_name is a legal XPath expression for this attribute, but person//name and name/first_name/@id are not. Several instances of this restricted form of XPath expression can be combined with the vertical bar so that person/name/first_name | person/name/last_name is also an acceptable XPath expression. Finally, the XPath expression may begin with .// so that .//name is valid. However, this is the only place the descendant-or-self axis can be used. No other forms of XPath expression are allowed here.

xs:sequence
<xs:sequence
  id = "ID"
  maxOccurs = "( nonNegativeInteger | unbounded)"
  minOccurs = "nonNegativeInteger" >
  <!-- (  xs:annotation?,
        ( xs:element | xs:group | xs:choice | xs:sequence | xs:any )*
       )
  -->
</xs:sequence>

The xs:sequence element indicates that the elements represented by its child elements should appear at that position in the instance document in the order they're listed here. The sequence must repeat at least minOccurs times and at most maxOccurs times. The default for both minOccurs and maxOccurs is 1. The maxOccurs attribute can be set to unbounded to indicate that the sequence may repeat indefinitely.

xs:simpleContent
<xs:simpleContent
  id = "ID" >
  <!-- (xs:annotation?, (xs:restriction | xs:extension)) -->
</xs:simpleContent>

The xs:simpleContent element is used inside xs:complexType elements whose content is a simple type, such as xs:string or xs:integer, rather than child elements or mixed content. This is customarily done when the only reason an element has a complex type instead of a simple type is for the purpose of attributes.

xs:simpleType
<xs:simpleType
  final = "( #all | list | union | restriction )"
  id = "ID"
  name = "NCName" >
  <!-- (xs:annotation?, (xs:restriction | xs:list | xs:union)) -->
</xs:simpleType>

The xs:simpleType element defines a new simple type for elements and attributes. A simple type is composed purely of text but no child elements--#PCDATA, in DTD parlance. A top-level xs:simpleType element has a name given in the name attribute by which it can be referred to from the type attribute of xs:element and xs:attribute elements. Alternately, an xs:element or xs:attribute element can have an xs:simpleType child without a name attribute that defines an anonymous type for that element or attribute.

New types are derived from existing types in one of three ways: by restricting the range of a base type using an xs:restriction child element, by combining multiple base types with an xs:union child element, or by allowing multiple values of a base type separated by whitespace with an xs:list child element.

The final attribute can be used to prevent a simple type from being subtyped. If final contains the value list, the type cannot be extended by listing. If final contains the value restriction, the type cannot be extended by restriction. If final contains the value union, the type cannot become a member of a union. These three values can be combined in a whitespace-separated list. For instance, final="list union" prevents derivation by list and union but not by restriction. If final has the value #all, the type cannot be used as a base type in any way.

xs:totalDigits
<xs:totalDigits
  fixed = "( true | false )"
  id = "ID"
  value = "positiveInteger" >
  <!-- (xs:annotation?) -->
</xs:totalDigits>

The xs:totalDigits facet element is used when deriving from xs:decimal elements and its descendants (xs:integer, xs:long, xs:nonNegativeInteger, xs:unsignedLong, etc.) by restriction. It specifies the maximum number of digits allowed in the number, including both the integer and fractional parts, but not counting the decimal point or the sign. This only sets the maximum number of digits. If you want to specify a minimum number of digits, use the xs:pattern element instead. If the fixed attribute has the value true, then types derived from this type are not allowed to override the value of fractionDigits given here.

xs:union
<xs:union
  id = "ID"
  memberTypes = "List of QName" >
  <!-- (xs:annotation?, (xs:simpleType*)) -->
</xs:union>

The xs:union element is placed inside an xs:simpleType to indicate that an element or attribute can contain any one of multiple types. For example, it can say that an element can contain either an xs:integer or an xs:token. The names of the types that participate in the union are listed in the memberTypes attribute separated by whitespace. Furthermore, the types defined in the xs:simpleType children of the xs:union are also members of the union.

xs:unique
<xs:unique
  id = "ID"
  name = "NCName" >
  <!-- (xs:annotation?, xs:selector, xs:field+ ) -->
</xs:unique>

The xs:unique element requires that a specified subset of elements and/or attributes in the instance document have unique values calculated from each of those elements/attributes. This is similar to the constraint imposed by declaring an attribute to have type xs:ID, but much more flexible. The xs:selector child element uses XPath to specify the subset of nodes from the instance document over which uniqueness is calculated. The xs:field children use XPath expressions to specify what properties of those nodes must be unique within the subset.

xs:whiteSpace
<xs:whiteSpace
  fixed = "( true | false )"
  id = "ID"
  value = "(collapse | preserve | replace)" >
  <!-- (xs:annotation?) -->
</xs:whiteSpace>

The xs:whiteSpace facet element is unusual in that it does not constrain values. Instead, it tells the validator how it should normalize whitespace before validating the value against other facets. The value attribute has one of three values:

preserve
All whitespace is significant; this is conceptually similar to the pre element in HTML.

collapse
Before the value is validated, tabs, carriage returns, and line feeds are replaced by spaces; leading and trailing whitespace is deleted; and runs of more than one consecutive space are condensed to a single space.

replace
Tabs, carriage returns, and line feeds are replaced by spaces before the value is validated.

Previous | Home | Next
21. Schemas Reference | Book Index | 21.3. Primitive Types
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Primitive Types (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

21.3. Primitive Types

The W3C XML Schema Language provides 44 built-in simple types for text strings. Each type has a value space and a lexical space. The value space is the set of unique meanings for the type, which may or may not be text. In some sense, the value space is composed of Platonic forms. The lexical space is the set of text strings that correspond to particular points in the value space. For example, the xs:boolean type has the value space true and false. However, its lexical space contains four strings: true, false, 0, and 1. true and 1 both map to the same value true, while false and 0 map to the single value false. In cases like this where multiple strings in the lexical space map to a single value, then one of those strings is selected as the canonical lexical representation. For instance, the canonical lexical representations of true and false are the strings true and false.

The primitive types are organized in a hierarchy. All simple types descend from an abstract ur-type called xs:anySimpleType, which is itself a descendant of an abstract ur-type called xs:anyType that includes both simple and complex types. Simple types are derived from other simple types by union, restriction, or listing. For example, the xs:nonNegativeInteger type is derived from the xs:integer type by setting its minInclusive facet to 0. The xs:integer type is derived from the xs:decimal type by setting its fractionDigits facet to 0. Figure 21-1 diagrams the complete hierarchy of built-in types. The xs:simpleType element allows you to apply facets to these types to create your own derived types that extend this hierarchy.

Figure 21-1. The simple type hierarchy

The types are organized in the following section alphabetically. For each type the value and lexical spaces are described, and some examples of permissible instances are provided.

xs:anyURI

The xs:anyURI type indicates a Uniform Resource Identifier. This includes not only Uniform Resource Locators (URLs), but also Uniform Resource Names (URNs). Both relative and absolute URLs are allowed. Legal xs:anyURI values include the following:

  • http://www.cafeaulait.org/

  • http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/

  • http://www.w3.org/TR/xmlschema-2/#anyURI

  • #xpointer(/book/chapter[20]/sect1[2]/sect2[1])

  • gopher://spinaltap.micro.umn.edu/00/Weather/

  • mailto:elharo@metalab.unc.edu

  • chapters/ch03.html

  • http://ibiblio.org/nywc/compositions.phtml?category=Concertos

More specifically, elements of this type must be composed exclusively of the ASCII letters A-Z and a-z and digits 0-9, as well as the ASCII punctuation marks -, _, ., !, ~, *, ', (, and ). In addition, the ASCII punctuation marks ;, /, ?, :, @, &, =, +, $, %, and , may be used for their intended purposes in URLs, e.g., the forward slash can be used as the path separator but not as part of a filename. All other characters must be escaped by encoding each byte of their UTF-8 representation as a percent sign followed by the hexadecimal value of the character. Although there are other restrictions on what does and does not make a legal URI, in practice the only conditions that schema processors check are the limitations on the characters that may appear.

Constraining facets that apply to xs:anyURI are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:base64Binary

The xs:base64Binary type represents an arbitrary sequence of bytes that has been encoded in ASCII characters using the Base-64 algorithm defined in RFC 2045, Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies. The data is divided into lines of 76 characters or less. The letters A-Z and a-z, the digits 0-9, and the punctuation marks + and / are used to encode data. The equals sign is use to pad data at the end if necessary. Other characters are allowed, but are ignored by the Base-64 decoder.

The constraining facets that apply to xs:base64Binary are length, minLength, maxLength, pattern, enumeration, and whiteSpace. Unlike string types, the values specified by the length, minLength, and maxLength facets refer to the number of bytes in the decoded data, not to the number of characters in the encoded data.

xs:boolean

The xs:boolean type represents a logical Boolean whose value is either true or false. There are exactly four legal values for elements and attributes whose type is Boolean:

  • true

  • false

  • 0

  • 1

0is the same as false, and 1 is the same as true. Only two constraining facets apply to xs:boolean: pattern and whiteSpace.

xs:byte

The xs:byte type represents an integer with a value between -128 and 127. It is a subtype of the xs:short type. Legal values include any sequence of digits whose value is less than or equal to 127 and greater than or equal to -128. An optional leading plus or minus sign is allowed. For example, these are legal bytes:

  • 127

  • -128

  • 0

  • 52

  • +52

  • 0000052

Constraining facets that apply to xs:byte are length, minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:date

The xs:date type represents a specific day in history such as September 26, 2001. Dates are written in the form CCYY-MM-DD. For example, September 26, 2001 is written as 2001-09-26. Dates in the far future and distant past can be written with more than four digits in the year, but at least four digits are required. Dates before year 1 are written with a preceding minus sign. (There was no year 0.) An optional time zone indicator in the form ±hh:mm may be suffixed to provide a time zone as an offset from Coordinated Universal Time (Greenwich Mean Time, UTC). For example, 2001-09-26-05:00 is September 26, 2001 in the U.S. Eastern time zone. A Z can be used instead to indicate UTC. These are all valid values of type xs:date:

  • 2001-01-01

  • 1999-12-31Z

  • 0482-11-24

  • -0052-10-23

  • 2002-12-23+12:00

  • 87500-01-01

Constraining facets that apply to xs:date are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. Note, however, that when the time zone is not specified, it's not always possible to determine unambiguously whether one date begins after another.

xs:dateTime

The xs:dateTime type represents a specific moment in history, such as 3:32 P.M., September 26, 2001. Dates are written in the form CCYY-MM-DDThh:mm:ss. For example, 3:32 P.M., September 26, 2001 is written as 2001-09-26T15:32:00. Decimal fractions of a second can be indicated by appending a period and any number of digits after the seconds. Dates in the far future and distant past can be written with more than four digits in the year, but at least four digits are required. Dates before year 1 are written with a preceding minus sign. (There was no year 0.) An optional time zone indicator in the form ±hh:mm may be suffixed to provide a time zone as an offset from Coordinated Universal Time (Greenwich Mean Time, UTC). For example, 2001-09-26T15:32:00-05:00 is 3:32 P.M., September 26, 2001 in the U.S. Eastern time zone. A Z can be used instead to indicate UTC. These are all valid values of type xs:dateTime:

  • 2001-01-01T03:32:00-05:00

  • 1999-12-31T00:00:00Z

  • 2002-12-23T17:08:30.121893632178

Constraining facets that apply to xs:dateTime are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. Note, however, that when the time zone is not specified, it's not always possible to determine unambiguously whether one time falls after another.

xs:decimal

xs:decimal is the base type for all numeric built-in schema types, except xs:float and xs:double. It represents a base 10 number with any finite number of the digits 0-9 before and after the decimal point. It may be prefixed with either a plus sign or a minus sign. These are all valid values of type xs:decimal:

  • 3.1415292

  • 03.1415292

  • 127

  • +127

  • -128

  • 0.0

  • 0.

  • .0

This type is not conducive to localization. Only European digits can be used, and only a period can be used as a decimal point. Exponential and scientific notation are not supported.

Constraining facets that apply to xs:decimal are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, fractionDigits, and totalDigits.

xs:double

The xs:double type is designed to represent eight-byte, binary floating-point numbers in IEEE 754 format, such as is used by the double type in Java and many C compilers. This includes the special values INF for infinity and NaN for not a number, used for the results of unconventional operations like dividing by zero and taking the square root of a negative number. Because not all binary numbers can be precisely represented by decimal numbers, it is possible that two different decimal representations in the lexical space map to the same value (and vice versa). In this case, the closest approximation IEEE-754 value is chosen. These are all legal values of type xs:double:

  • 3.1415292

  • -03.1415292

  • 6.022E23

  • 127E-13

  • +2.998E+10

  • -128e12

  • 0.0

  • INF

  • NaN

  • -INF

Constraining facets that apply to xs:double are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace.

xs:duration

The xs:duration type represents a length of time such as 15 minutes; 2 hours; or 3 years, 7 months, 2 days, 8 hours, 32 minutes, and 12 seconds. It does not have a specific beginning or end, just a length. Durations are represented using the ISO-8601 standard format P n YnMnDTnHnMnS. n Y gives the number of years, n M the number of months, n D the number of days, n H the number of hours, n M the number of minutes, and n S the number of seconds. The number of years, months, days, hours, minutes, and seconds are all given as non-negative integers. The number of seconds is a decimal number with as many places after the decimal point as necessary. For example, in this format, 3 years, 7 months, 2 days, 8 hours, 32 minutes, and 12 seconds is written as P3Y7M2DT8H32M12S. Any values that are zero can be omitted. Thus, a duration of 2 years and 2 minutes can be written as P2YT2M. If there are no hours, minutes, or seconds, then the T is omitted. Thus, a duration of two years is written as P2Y. A leading minus sign before the P indicates a negative duration.

Constraining facets that apply to xs:duration are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. However, because the number of days in a month varies from 28 to 31 and the number of days in a year varies from 365 to 366, durations are not always perfectly ordered. For instance, whether P1M is greater than, equal to, or less than P30D depends on which month it is.

xs:ENTITIES

The xs:ENTITIES type indicates that the value is a whitespace-separated list of unparsed entity names declared in the instance document's DTD. This is the same as the DTD ENTITIES attribute type.

Constraining facets that apply to xs:ENTITIES are length, minLength, maxLength, enumeration, pattern, and whiteSpace. The length, minLength, and maxLength facets all refer to the number of entity names in the list.

xs:ENTITY

The xs:ENTITY type is a subtype of xs:NCNAME with the additional restriction that the value be declared as an unparsed entity in the document's DTD. The legal lexical values of type xs:ENTITY are exactly the same as for xs:NCNAME. Constraining facets that apply to xs:ENTITY are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

TIP: A schema cannot declare either parsed or unparsed entities. An XML document that uses any entities other than the five predefined ones must have a DOCTYPE declaration and a DTD.

xs:float

The xs:float type represents four-byte, binary floating-point numbers in IEEE-754 format, such as is the float type in Java and many C compilers. This includes the special values INF for infinity and NaN for not a number, used for the results of unconventional operations like dividing by zero and taking the square root of a negative number. Because not all binary numbers can be precisely represented by decimal numbers, it is possible that two different decimal representations in the lexical space map to the same value (and vice versa). In this case, the closest approximation of the IEEE-754 value is chosen. These are all legal values of type xs:float:

  • 3.1415292

  • -03.1415292

  • 6.022E23

  • 127E-13

  • +2.998E+10

  • -128e12

  • 0.0

  • INF

  • NaN

  • -INF

Constraining facets that apply to xs:float are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace.

xs:gDay

The xs:gDay type represents a certain day of the month such as the 14th or the 23rd in no particular month. The format used is -- -DD plus an optional time zone suffix in the form ±hh:mm or Z to indicate Coordinated Universal Time (UTC). These are all valid xs:gDay values:

  • ---01

  • ---28

  • ---29Z

  • ---31+02:00

  • ---15-11:00

The g indicates that the day is given in the Gregorian calendar. Schema date types are not localizable to non-Gregorian calendars. If you need a different calendar, you'll need to derive from xs:string using the pattern facet.

Constraining facets that apply to xs:gDay are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. However, if the time zones are not specified it may not be possible to conclusively determine whether one day is greater than or less than another. If time zones are specified, days are compared by when they start. Thus ---29-05:00 is greater than ---29Z, which is greater than ---29+02:00.

xs:gMonth

The xs:gMonth type represents a certain month of the year in the Gregorian calendar as an integer between --01-- and --12--. An optional time zone suffix in the form ±hh:mm or Z to indicate Coordinated Universal Time (UTC) can be added as well. These are all valid xs:gMonth values:

  • --01--

  • --12--

  • --12--Z

  • --09--+02:00

  • --03---11:00

TIP: XML Schema Part 2: Datatypes has a mistake here. The date time data types are supposed to be in the format specified by the ISO-8601 standard, according to which the proper format for a month is an integer between --01 and --12 with no trailing hyphens. Exactly how this will be fixed remains to be determined, though at the least months of the form --01 and --10 will be allowed in the future.

The g indicates that the month is given using the Gregorian calendar. Schema date types are not localizable to non-Gregorian calendars. If you need a different calendar, you'll need to derive from xs:string using the pattern facet.

Constraining facets that apply to xs:gMonth are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. However, if the time zones are not specified, it may not be possible to determine conclusively whether one month starts before another. If time zones are specified, months are compared by their first moment. Thus, --12---05:00 is greater than --12--Z, which is greater than --12--+02:00.

xs:gMonthDay

The xs:gMonthDay type represents a certain day of a certain month in no particular year. It is written in the format -- MM-DD plus an optional time zone suffix in the form ±hh:mm or Z to indicate Coordinated Universal Time (UTC). These are all valid xs:gMonthDay values:

  • --10-31

  • --12-25Z

  • --01-01+05:00

  • --07-04-02:00

The g indicates that the month and day are specified in the Gregorian calendar. Schema date types are not localizable to non-Gregorian calendars. For a different calendar, you'll have to derive from xs:string using the pattern facet.

Constraining facets that apply to xs:gMonthDay are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. However, if the time zones are not specified, it is not always possible to determine conclusively whether one day starts before another. If time zones are specified, days are compared by their first moment in the same year.

xs:gYear

The xs:gYear type represents a year in the Gregorian calendar. It is written in the format CCYY plus an optional time zone suffix in the form ±hh:mm or Z to indicate Coordinated Universal Time (UTC). Dates before year 1 can be indicated by a minus sign. At least four digits are used, but additional digits can be added to indicate years after 9999 or before 9999 BCE. These are all valid xs:gYear values in their order of occurrence:

  • -15000000000

  • 0004

  • 0600

  • 1492

  • 2002+10:00

  • 2002Z

  • 2002-04:30

  • 100000

  • 800000000

Constraining facets that apply to xs:gYear are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. However, if the time zones are not specified, it may not be possible to determine conclusively whether one year starts before another. If time zones are specified, years are compared by their first moment.

xs:gYearMonth

The xs:gYearMonth type represents a month and year in the Gregorian calendar, such as March, 2002. It is written in the format CCYY-MM plus an optional time zone suffix in the form ±hh:mm or Z to indicate Coordinated Universal Time (UTC). Dates before year 1 can be indicated by a minus sign. At least four digits are used, but additional digits can be added to indicate years after 9999 or before 9999 BCE. These are all valid xs:gYearMonth values in their order of occurrence:

  • -15000000000-05

  • 0004-04

  • 0600-10

  • 1492-11

  • 2002-03+10:00

  • 2002-03Z

  • 2002-03-04:30

  • 100000-07

  • 100000-08

Constraining facets that apply to xs:gYearMonth are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. However, if the time zones are not specified it may not be possible to determine conclusively whether one month and year starts before another.

xs:hexBinary

The xs:hexBinary type represents an arbitrary sequence of bytes that has been encoded by replacing each byte of data with two hexadecimal digits from 0 through F (A is 10, B is 11, C is 12, etc.). Either upper- or lowercase letters may be used in whatever character set the document is written. In UTF-8 or ASCII, this has the effect of exactly doubling the space used for the data.

The constraining facets that apply to xs:hexBinary are length, minLength, maxLength, pattern, enumeration, and whiteSpace. Unlike string types, the values specified by the length, minLength, and maxLength facets refer to the number of bytes in the decoded data, not to the number of characters in the encoded data.

xs:ID

xs:ID is a subtype of xs:NCName with the additional restriction that the value is unique among other items of type xs:ID within the same document. The legal lexical values of type xs:ID are exactly the same as for xs:NCName. Constraining facets that apply to xs:ID are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:IDREF

xs:IDREF is a subtype of xs:NCName with the additional restriction that the value is used elsewhere in the instance document on an item with type xs:ID. The legal lexical values of type xs:ID are exactly the same as for xs:NCName. Constraining facets that apply to xs:IDREF are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:IDREFS

The xs:IDREFS type indicates that the value is a whitespace-separated list of xs:ID type values used elsewhere in the instance document. This is the same as the DTD IDREFS attribute type.

Constraining facets that apply to xs:IDREFS are length, minLength, maxLength, enumeration, pattern, and whiteSpace. The length, minLength, and maxLength facets all refer to the number of IDREFs in the list.

xs:int

The xs:int type represents a signed integer small enough to be represented as a four-byte, two's complement number such as Java's int primitive data type. It is derived from xs:long by setting the maxInclusive facet to 2147483647 and the minInclusive facet to -2147483648. These are all legal values of type xs:int:

  • 200

  • 200000

  • -200000

  • +2147483647

  • -2147483648

  • 0

Constraining facets that apply to xs:int are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:integer

The xs:integer type represents a mathematical integer of arbitrary size. The type is derived from xs:double by fixing the fractionDigits facet at 0. It may be prefixed with either a plus sign or a minus sign. If no sign is present, a plus is assumed. These are all legal values of type xs:integer:

  • 3

  • 3000

  • 349847329847983264983264987326487326487324678346374

  • +127

  • -128

  • 0

  • +0

  • -0

Constraining facets that apply to xs:integer are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:language

Elements and attributes with type xs:language contain a language code as defined in RFC 1766, Tags for the Identification of Languages. These are essentially the acceptable values for the xml:lang attribute described in Chapter 5. If possible, this should be one of the two-letter language codes defined in ISO 639, possibly followed by a country code. For languages that aren't listed in ISO 639, you can use one of the i-codes registered with IANA. If the language you need isn't present in either of these sets, you can make up your own language tag beginning with the prefix "x-" or "X-". Thus, these are acceptable language values:

  • en

  • en-US

  • en-GB

  • fr-CA

  • i-klingon

  • x-quenya

  • X-PigLatin

Constraining facets that apply to xs:language are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:long

The xs:long type represents a signed integer that can be represented as an eight-byte, two's complement number such as Java's long primitive data type. It is derived from xs:integer by setting the maxInclusive facet to 9223372036854775807 and the minInclusive facet to -9223372036854775808. These are all legal values of type xs:long:

  • 2

  • 200

  • +9223372036854775807

  • -9223372036854775808

  • 5000000000

  • 0

Constraining facets that apply to xs:long are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:Name

xs:Name is a subtype of xs:token that is restricted to legal XML 1.0 names. In other words, the value must consist exclusively of letters, digits, ideographs, and the underscore, hyphen, period, and colon. Digits, the hyphen, and the period may not be used to start a name, though they may be used inside the name. These are all legal values of type xs:Name:

  • G127

  • _128

  • Limit

  • xml-stylesheet

  • svg:rect

Constraining facets that apply to xs:Name are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:NCName

An xs:NCName is a noncolonized name as defined in Namespaces in XML. This is a legal XML name that does not contain a colon. The value must consist exclusively of letters, digits, ideographs, and the underscore, hyphen, and period. Digits, the hyphen, and the period may not be used to start a name, though they may be used inside the name. These are all legal values of type xs:NCName:

  • I-10

  • _128

  • Limit

  • xml-stylesheet

Constraining facets that apply to xs:NCName are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:negativeInteger

The xs:negativeInteger type represents a mathematical integer that is strictly less than zero. It is derived from xs:integer by setting the maxInclusive facet to -1. These are all legal values of type xs:negativeInteger:

  • -2

  • -200

  • -9223372036854775809

  • -9223372036854775808922337203685477580892233720368

  • -34

Constraining facets that apply to xs:negativeInteger are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:NMTOKEN

An xs:NMTOKEN is the schema equivalent of the DTD NMTOKEN attribute type. It is a subtype of xs:token that is restricted to legal XML 1.0 name tokens. These are the same as XML names except that there are no restrictions on what characters may be used to start the name token. In other words, the value must consist of one or more letters, digits, ideographs, and the underscore, hyphen, period, and colon. These are all legal values of type xs:NMTOKEN:

  • 127

  • -128

  • Limit

  • integration

  • svg:rect

Constraining facets that apply to xs:NMTOKEN are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:NMTOKENS

The xs:NMTOKENS type is the schema equivalent of the DTD NMTOKENS attribute type. xs:NMTOKENS is derived from xs:NMTOKEN by list. Thus, a value of type xs:NMTOKENS contains one or more whitespace-separated name tokens. These are all legal values of type xs:NMTOKENS:

  • 127 126 125 124 123 122 121 120 119 118

  • -128

  • Limit Integral Sum Sup Liminf Limsup

  • Jan Feb Mar Apr May June July Sept Nov Dec

  • svg:rect

Constraining facets that apply to xs:NMTOKENS are length, minLength, maxLength, enumeration, pattern, and whiteSpace. The length, minLength, and maxLength facets all refer to the number of name tokens in the list.

xs:nonNegativeInteger

The xs:nonNegativeInteger type represents a mathematical integer that is greater than or equal to zero. It is derived from xs:integer by setting the minInclusive facet to 0. These are all legal values of type xs:nonNegativeInteger:

  • 2

  • +200

  • 9223372036854775809

  • 9223372036854775808922337203685477580892233720368

  • 0

Constraining facets that apply to xs:nonNegativeInteger are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:nonPositiveInteger

The xs:nonPositiveInteger type represents a mathematical integer that is less than or equal to zero. It is derived from xs:integer by setting the maxInclusive facet to 0. These are all legal values of type xs:nonPositiveInteger:

  • -2

  • -200

  • -9223372036854775809

  • -9223372036854775808922337203685477580892233720368

  • 0

Constraining facets that apply to xs:nonPositiveInteger are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:normalizedString

xs:normalizedString is derived from xs:string by setting the whiteSpace facet to replace so that the carriage return (#xD) and tab (#x9) characters are replaced by spaces in the normalized value. A normalized string can contain any characters that are allowed in XML, though depending on context, special characters such as <, &, and " may have to be escaped with character or entity references in the usual way. All legal strings are also legal lexical representations of type xs:normalizedString. However, a schema-aware parser that presents the normalized value of an element instead of the literal characters in the document will replace all carriage returns and tabs with spaces before passing the string to the client application.

Constraining facets that apply to xs:normalizedString are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:NOTATION

The xs:NOTATION type restricts a value to those qualified names declared as notations using an xs:notation element in the schema. This is an abstract type. In other words, you cannot directly declare that an element or attribute has type xs:NOTATION. Instead, you must first derive a new type from xs:NOTATION, most commonly by enumeration, and then declare that your element or attribute possesses the subtype. Constraining facets that apply to xs:NOTATION are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:positiveInteger

The xs:positiveInteger type represents a mathematical integer that is strictly greater than zero. It is derived from xs:integer by setting the minInclusive facet to 1. These are all legal values of type xs:positiveInteger:

  • 1

  • +2

  • 9223372036854775809

  • 9223372036854775808922337203685477580892233720368

  • 34

Constraining facets that apply to xs:positiveInteger are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:QName

An xs:QName is a base type that is restricted to namespace-qualified names. The logical value of a qualified name is a namespace URI, local part pair. Lexically, qualified names are the same as XML names except that they may not contain more than one colon and that colon may not be the first character in the name. A qualified name may or may not be prefixed. If it is prefixed, then the prefix must be properly mapped to a namespace URI. If it is not prefixed, then the name must occur in the scope of a default namespace. These are all legal values of type xs:QName provided that this condition is met in context:

  • xsl:apply-templates

  • svg:rect

  • limit

  • xml:lang

  • body

  • xlink:href

Constraining facets that apply to xs:QName are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:short

The xs:short type indicates a signed integer small enough to be represented as a two-byte, two's complement number such as Java's short primitive data type. It is derived from xs:int by setting the maxInclusive facet to 32767 and the minInclusive facet to -32768. These are all legal values of type xs:int:

  • 2000

  • +2000

  • -2000

  • 32767

  • -32768

  • 0

Constraining facets that apply to xs:short are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:string

This is the most general simple type. Elements and attributes with type xs:string can contain any sequence of characters allowed in XML, though depending on context certain characters such as <, &, and " may have to be escaped with character or entity references in the usual way.

Constraining facets that apply to xs:string are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:time

The xs:time type represents a specific time of day on no particular day, such as 3:32 P.M. Times are written in the form ±hh:mm:ss.xxx using a 24-hour clock and as many fractions of a second as necessary. For example, 3:41 P.M. is written as 15:41:00. 3:41 A.M. and a half-second is written as 03:41:00.5. The Z suffix indicates Coordinated Universal Time (Greenwich Mean Time, UTC). Otherwise, the time zone can be indicated as an offset in hours and minutes from UTC. For example, 15:41:00-05:00 is 3:41 P.M., in the U.S. Eastern time zone. The time zone may be omitted, in which case the actual time is somewhat nondeterministic. These are all valid values of type xs:time:

  • 03:32:00-05:00

  • 00:00:00Z

  • 08:30:34.121893632178

  • 23:59:59

Constraining facets that apply to xs:time are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, and whiteSpace. Note, however, that when the time zone is not specified, it's not always possible to determine unambiguously whether one time falls after another.

xs:token

xs:token is a subtype of xs:normalizedString whose normalized value does not contain any line feed (#xA) or tab (#x9) characters, does not have any leading or trailing whitespace, and has no sequence of two or more spaces. All legal strings are also legal lexical representations of type xs:normalizedString. However, a schema-aware parser that presents the normalized value of an element instead of the literal characters in the document will trim leading and trailing whitespace and compress all others runs of whitespace characters with a single space before passing the string to the client application.

Constraining facets that apply to xs:token are length, minLength, maxLength, pattern, enumeration, and whiteSpace.

xs:unsignedByte

The xs:unsignedByte type represents a non-negative integer that can be stored in one byte, such as the unsigned char type used by some (but not all) C compilers. It is derived from xs:unsignedShort by setting the maxInclusive facet to 255 (28-1). These are all legal values of type xs:unsignedByte:

  • 3

  • 200

  • +255

  • 50

  • 0

Constraining facets that apply to xs:unsignedByte are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:unsignedInt

The xs:unsignedInt type represents a non-negative integer that can be stored in four bytes, such as the unsigned int type used by some C compilers. It is derived from xs:unsignedLong by setting the maxInclusive facet to 4294967295 (2321-1). These are all legal values of type xs:unsignedInt:

  • 2

  • 200

  • +4294967295

  • 100000

  • 0

Constraining facets that apply to xs:unsignedInt are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:unsignedLong

The xs:unsignedLong type represents a non-negative integer that can be stored in eight bytes, such as the unsigned long type used by some C compilers. It is derived from xs:nonNegativeInteger by setting the maxInclusive facet to 18446744073709551615 (264-1). These are all legal values of type xs:unsignedLong:

  • 2

  • 200

  • +9223372036854775807

  • 18446744073709551615

  • 5000000000

  • 0

Constraining facets that apply to xs:unsignedLong are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

xs:unsignedShort

The xs:unsignedShort type represents a non-negative integer that can be stored in four bytes, such as the unsigned short type used by some C compilers. It is derived from xs:unsignedInt by setting the maxInclusive facet to 65535 (216-1). These are all legal values of type xs:unsignedShort:

  • 3

  • 300

  • +65535

  • 50000

  • 0

Constraining facets that apply to xs:unsignedShort are minInclusive, maxInclusive, minExclusive, maxExclusive, pattern, enumeration, whiteSpace, and totalDigits.

Previous | Home | Next
21.2. Schema Elements | Book Index | 21.4. Instance Document Attributes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Instance Document Attributes (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

21.4. Instance Document Attributes

The W3C XML Schema Language defines four attributes in the http://www.w3.org/2001/XMLSchema-instance namespace (here mapped to the xsi prefix), which are attached to elements in the instance document rather than elements in the schema. These are as follows: xsi:nil, xsi:type, xsi:schemaLocation, and xsi:noNamespaceSchemaLocation. All four of these attributes are special in that the schemas do not need to declare them.

xsi:nil

The xsi:nil attribute indicates that a certain element does not have a value or that the value is unknown. This is not the same as having a value that is zero or the empty string. Semantically, it is equivalent to SQL's null. For example, in this full_name element, the last_name child has a nil value:

<full_name xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <first_name>Cher</first_name>
  <last_name xsi:nil="true"/>
</full_name>

It is not relevant whether an empty-element tag or a start-tag/end-tag pair is used to represent the nil element. However, a nil element may not have any content.

In order for this document to be valid, the element declaration for the name element must explicitly specify that nil values are allowed by setting the nillable attribute to true. For example:

<xs:element name="last_name" type="xs:string" nillable="true"/>
xsi:noNamespaceSchemaLocation

The xsi:noNamespaceSchemaLocation attribute locates the schema for elements that are not in any namespace. (Attributes that are not in any namespace are assumed to be declared in the same schema as their parent element.) Its value is a relative or absolute URL where the schema document can be found. It is most commonly attached to the root element but can appear further down the tree. For example, this person element claims that it should be validated against the schema found at the URL http://www.elharo.com/person.xs:

<person xsi:noNamespaceSchemaLocation="http://www.elharo.com/person.xs">
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>computer scientist</profession>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

These are only suggestions. Schema processors may use other means of locating the relevant schemas and to ignore the hints provided by xsi:noNamespaceSchemaLocation.

xsi:schemaLocation

The xsi:schemaLocation attribute locates schemas for elements and attributes that are in a specified namespace. Its value is a namespace URI followed by a relative or absolute URL where the schema for that namespace can be found. It is most commonly attached to the root element but can appear further down the tree. For example, this person element in the http://www.cafeconleche.org/namespaces/person namespace claims that it should be validated against the schema found at the URL http://www.elharo.com/person.xs:

<person xmlns="http://www.cafeconleche.org/namespaces/person"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.cafeconleche.org/namespaces/person
                      http://www.elharo.com/person.xs">
  <name>
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>computer scientist</profession>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

If more than one namespace is used in a document, then each namespace must have its own schema. The namespace URIs and schema URLs can be listed in sequence in the same xsi:schemaLocation attribute. For example, the xsi:schemaLocation attribute on this person element says that items from the http://www.cafeconleche.org/namespaces/person namespace should be validated against the schema found at the URL http://www.elharo.com/person.xs, while items from the http://www.cafeconleche.org/namespaces/names namespace should be validated against the schema found at the relative URL names.xs.

<person xmlns="http://www.cafeconleche.org/namespaces/person"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.cafeconleche.org/namespaces/person
                      http://www.elharo.com/person.xs
                      http://www.cafeconleche.org/namespaces/names
                      names.xs">
  <name xmlns="http://www.cafeconleche.org/namespaces/names">
    <first_name>Alan</first_name>
    <last_name>Turing</last_name>
  </name>
  <profession>computer scientist</profession>
  <profession>mathematician</profession>
  <profession>cryptographer</profession>
</person>

These are only suggestions. Schema processors are allowed to use other means of locating the relevant schemas and to ignore the hints provided by xsi:schemaLocation.

xsi:type

The xsi:type attribute may be used in instance documents to indicate the type of an element, even when a full schema is not available. For example, this length element has type xs:decimal:

<length xsi:type="xs:decimal">23.5</length>

More importantly, the xsi:type attribute enables a limited form of polymorphism. That is, it allows you to place an instance of a derived type where an instance of the base type would normally be expected. The instance of the derived type must carry an xsi:type attribute identifying it as an instance of the base type. For example, suppose a schema says that a transport element must contain exactly one vehicle element. If bus, train, and airplane are all subtypes of vehicle, then these are valid transport elements:

<transport>
  <airplane xsi:type="airplane">Boeing 767</airplane>
</transport>
<transport>
  <bus xsi:type="bus">Greyhound</bus>
</transport>

However, when the xsi:type attributes are removed, these are no longer valid transport elements:

<transport>
  <airplane>Boeing 767</airplane>
</transport>
<transport>
  <bus>Greyhound</bus>
</transport>
Previous | Home | Next
21.3. Primitive Types | Book Index | 22. XPath Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 22. XPath Reference

Contents:

The XPath Data Model
Data Types
Location Paths
Predicates
XPath Functions

XPath is a non-XML syntax for expressions that identify particular nodes and groups of nodes in an XML document. It is used by both XPointer and XSLT, as well as by some native XML databases and query languages.

22.1. The XPath Data Model

XPath views each XML document as a tree of nodes. Each node has one of seven types:

Root
Each document has exactly one root node, which is the root of the tree. This node contains one comment node child for each comment outside the document element, one processing-instruction node child for each processing instruction outside the document element, and exactly one element node child for the document element. It does not contain any representation of the XML declaration, the document type declaration, or any whitespace that occurs before or after the root element. The root node has no parent node. The root node's value is the value of the document element.

Element
An element node represents an element. It has a name, a namespace URI, a parent node, and a list of child nodes, which may include other element nodes, comment nodes, processing-instruction nodes, and text nodes. An element node also has a list of attributes and a list of in-scope namespaces, none of which are considered to be children of the element. The value of an element node is the complete, parsed text between the element's start- and end-tags that remains after all tags, comments, and processing instructions are removed and all entity and character references are resolved.

Attribute
An attribute node represents an attribute. It has a name, a namespace URI, a value, and a parent element. However, although elements are parents of attributes, attributes are not children of their parent elements. The biological metaphor breaks down here. xmlns and xmlns:prefix attributes are not represented as attribute nodes. An attribute node's value is the normalized attribute value.

Text
Each text node represents the maximum possible contiguous run of text between tags, processing instructions, and comments. A text node has a parent node but does not have children. A text node's value is the text of the node.

Namespace
A namespace node represents a namespace in scope on an element. In general, each namespace declaration by an xmlns or xmlns:prefix attribute produces multiple namespace nodes in the document tree. Like attribute nodes, each namespace node has a parent element but is not the child of that parent. The name of a namespace node is the prefix. The value of a namespace node is the namespace URI.

Processing instruction
A processing-instruction node represents a processing instruction. It has a target, data, a parent node, and no children. The name of a processing-instruction node is its target. The value of a processing-instruction node is the data of the processing instruction, not including any initial whitespace.

Comment
A comment node represents a comment. It has a parent node and no children. The value of a comment is the string content of the comment, not including the <!-- and -->.

The XML declaration and the document type declaration are not included in XPath's view of an XML document. All entity references, character references, and CDATA sections are resolved before the XPath tree is built. The references themselves are not included as a separate part of the tree.

Previous | Home | Next
21.4. Instance Document Attributes | Book Index | 22.2. Data Types
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Data Types (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

22.2. Data Types

Each XPath expression evaluates to one of four types:

Boolean
A binary value that is either true or false. In XPath, Booleans are most commonly produced by using the comparison operators =, !=, <, >, <=, and >=. Multiple conditions can be combined using the and and or operators, which have their usual meaning in logic (e.g., 3>2 or 2>1 is true.) XPath does not offer Boolean literals. However, the true( ) and false( ) functions fill that need.

Number
All numbers in XPath, which are IEEE 754-compliant, 64-bit floating point numbers. This is the same as the double type in Java. Numbers range from 4.94065645841246544e-324d to 1.79769313486231570e+308d, and are either positive or negative. Numbers also include the special values Inf (positive infinity), -Inf (negative infinity), and NaN (not a number), which is used for the results of illegal operations, such as dividing by zero. XPath provides all the customary operators for working with numbers, including:

+
Addition

-
Subtraction; however, this operator should always be surrounded by whitespace to avoid accidental misinterpretation as part of an XML name

*
Multiplication

div
Division

mod
Taking the remainder

String
Sequence of zero or more Unicode characters. String literals are enclosed in either single or double quotes, as convenient. Unlike Java, XPath does not allow strings to be concatenated with the plus sign. However, the concat( ) function serves this purpose.

Node-set
Collection of zero or more nodes from an XML document. Location paths produce most node-sets. Generally, a single node-set can contain every kind of node: root, element, attribute, namespace, comment, processing instruction, and text. However, most node-sets contain a single type of node.

Some standards that use XPath also define additional data types. For instance, XSLT defines a result tree fragment type that represents the result of processing an XSLT instruction or instantiating a template. XPointer defines a location set type that extends node-sets to include points and ranges.

Previous | Home | Next
22. XPath Reference | Book Index | 22.3. Location Paths
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Location Paths (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

22.3. Location Paths

Node-sets are returned by location-path expressions. Location paths consist of location steps. Each location step contains an axis and a node test separated by a double colon. That is, a location step looks like this:

axis::node test

The axis specifies in which direction from the context node the processor searches for nodes. The node test specifies which nodes along that axis are selected. These are some location steps with different axes and node tests:

child::set
descendant-or-self::node( )
ancestor-or-self::*
attribute::xlink:href

Each location step may be suffixed with one or more predicates enclosed in square brackets that further winnow the node-set. For example:

child::set[position( )=2]
descendant-or-self::node( )[.='Eunice']
ancestor-or-self::*[position( )=2][.="Celeste"]
attribute::xlink:href[starts-with('http')]

Each individual location step is itself a relative location path. The context node against which the relative location path is evaluated is established by some means external to XPath--for example, by the current matched node in an XSLT template.

Location steps can be combined by separating them with forward slashes. Each step in the resulting location path sets the context node (or nodes) for the next path in the step. For example:

ancestor-or-self::*/child::*[position( )=1]
child::document/child::set[position( )=2]/following-sibling::*
descendant::node( )[.='Eunice']/attribute::ID

An absolute location path is formed by prefixing a forward slash to a relative location path. This sets the context node for the first step in the location path to the root of the document. For example, these are all absolute location paths:

/descendant::ship/ancestor-or-self::*/child::*[position( )=1]
/child::document/child::set[position( )=2]/following-sibling::*
/descendant::node( )[.='Eunice']/attribute::ID

Multiple location paths can be combined with the union operator (|) to form an expression that selects a node-set containing all the nodes identified by any of the location paths. For example, this expression selects a node-set containing all the set children of the context node, all the vector descendants of the context node, all ancestor elements of the context node and the root node, and all attributes of the context node named href:

child::set | descendant::vector | ancestor::* | attribute::href

22.3.1. Abbreviated Syntax

An abbreviated syntax is available for particularly common location steps. This is the syntax allowed in XSLT match patterns. In this syntax, five axes may use this shorthand:

.
The context node

..
The parent node

name
The child element or elements with the specified name

//
All descendants of the context node, and the context node itself

@name
The attribute of the context node with the specified name

Using the abbreviated syntax, the previous examples can be rewritten in the following manner:

set
descendant-or-self::node( )
ancestor-or-self::*
@xlink:href
set[position( )=2]
descendant-or-self::node( )[.='Eunice']
ancestor-or-self::*[position( )=2][.="Celeste"]
@xlink:href[starts-with('http')]
ancestor-or-self::*/*[position( )=1]
document/set[position( )=2]/following-sibling::*
descendant::node( )[.='Eunice']/@ID
//ship/ancestor-or-self::*/*[position( )=1]
/document/set[position( )=2]/following-sibling::*
/descendant::node( )[.='Eunice']/@ID
set | .//vector | ancestor::* | @href

Not all location steps can be rewritten using the abbreviated syntax. In particular, only the child, self, attribute, descendant-or-self, and parent axes can be abbreviated. The remaining axes must be spelled out in full.

22.3.2. Axes

Each XPath location step moves along an axis from a context node. The context node is a particular root, element, attribute, comment, processing-instruction, namespace, or text node in the XML document. (In practice, it's almost always an element node or the root node.) The context node may be a node selected by the previous location step in the location path, or it may be determined by extra-XPath rules, such as which node is matched by an xsl:template element.

However the context node is determined, it has some relationship to every other node in the document. The various axes divide the document into different, overlapping sets, depending on their relationship to the context node. There are exactly 13 axes you can use in an XPath location step:

child
All children of the context node. Only root and element nodes have children. Attribute and namespace nodes are not children of any node, though they do have parent nodes.

descendant
All nodes contained inside the context node, that is, a child node, a child of a child node, a child of a child of a child node, and so on. Only root and element nodes have descendants. Like the child axis, the descendant axis never contains attribute or namespace nodes.

descendant-or-self
Any descendant of the context node or the context node itself. // is an abbreviation for /descendant-or-self::node( )/.

parent
The element or root node that immediately contains the context node. Only the root node does not have a parent node. .. is an abbreviation for parent::node( ).

ancestor
The root node and all element nodes that contain the context node. The root node itself has no ancestors.

ancestor-or-self
All ancestors of the context node, as well as the node itself.

following-sibling
All nodes that follow the end of the context node and have the same parent node. Attribute and namespace nodes do not have siblings.

preceding-sibling
All nodes that precede the start of the context node and have the same parent node. Attribute and namespace nodes do not have siblings.

following
All nodes that begin after the context node ends, except for attribute nodes and namespace nodes; that is, all nodes after the context node except descendants.

preceding
All nodes that end before the context node begins, except for attribute nodes and namespace nodes; that is, all nodes before the context node except ancestors.

attribute
All attributes of the context node; the axis is empty if the context node is not an element node. This axis does not contain xmlns and xmlns:prefix attributes. @name is an abbreviation for attribute::name.

namespace
All namespaces in scope (not merely declared) on the context node; this axis is empty if the context node is not an element node.

self
The context node itself. A single period (.) is an abbreviation for self::node.

22.3.3. Node Tests

Each location step has at least an axis and a node test. The node test further refines the nodes selected by the location step. In an unabbreviated location step, a double colon (::) separates the axis from the node test. There are seven kinds of node tests:

name
An XML name matches all elements with the same name. However, along the attribute axis it matches all attributes with the same name instead; along the namespace axis it matches all namespaces with that prefix. As usual, if the element or attribute name is prefixed, only the URI to which the prefix is mapped matters, not the prefix itself.

prefix:*
Along most axes, this node test matches all element nodes whose namespace URIs are the same as the namespace URI to which this prefix is mapped, regardless of name. However, along the attribute axis, this node test matches all attribute nodes whose namespace URIs are the same as the namespace URI to which this prefix is mapped.

comment( )
This includes all comment nodes.

text( )
This includes all text nodes. Each text node is a maximum contiguous run of text between other types of nodes.

processing-instruction( )
processing-instruction('target')
With no arguments, this node test selects all processing instructions. With a single string argument, it selects all processing instructions that have the specified target.

node( )
This node test selects all nodes, regardless of type: attribute, namespace, element, text, comment, processing instruction, and root.

*
This test normally selects all element nodes, regardless of name. However, if the axis is the attribute axis, then it selects all attribute nodes. If the axis is the namespace axis, then it selects all namespace nodes.

Previous | Home | Next
22.2. Data Types | Book Index | 22.4. Predicates
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Predicates (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

22.4. Predicates

Each location step may have zero or more predicates. A predicate is an XPath expression enclosed in square brackets that follows the node test in the location step. This expression most commonly, but not necessarily, returns a Boolean value. In the following location path:

/person[position()=1]/profession[.="physicist"][position( )<3]

[position( )=1], [.="physicist"], and [position( )<3] are predicates. An XPath processor works from left to right in an expression. After it has evaluated everything that precedes the predicate, it's left with a context node list that may contain no nodes, one node, or more than one node. For most axes, including child, following-sibling, following, and descendant, this list is in document order. For the ancestor, preceding, and preceding-sibling axes, this list is in reverse document order.

The predicate is evaluated against each node in the context node list. If the expression returns true, then that node is retained in the list. If the expression returns false, then the node is removed from the list. If the expression returns a number, then the node being evaluated is left in the list if and only if the number is the same as the position of that node in the context node list. If the expression returns a non-Boolean, non-number type, then that return value is converted to a Boolean using the boolean( ) function, described later, to determine whether it retains the node in the set.

Previous | Home | Next
22.3. Location Paths | Book Index | 22.5. XPath Functions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Functions (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

22.5. XPath Functions

XPath 1.0 defines 27 built-in functions for use in XPath expressions. Various technologies that use XPath, such as XSLT and XPointer, also extend this list with functions they need. XSLT even allows user-defined extension functions.

Every function is evaluated in the context of a particular node, called the context node. The higher-level specification in which XPath is used, such as XSLT or XPointer, decides exactly how this context node is determined. In some cases the function operates on the context node. In other cases it operates on the argument, if present, and the context node, if no argument exists. The context node is ignored in other cases.

In the following sections, each function is described with at least one signature in this form:

return-type function-name(type argument, type argument, ...)

Compared to languages like Java, XPath argument lists are quite loose. Some XPath functions take a variable number of arguments and fill in the arguments that are omitted with default values or the context node.

Furthermore, XPath is weakly typed. If you pass an argument of the wrong type to an XPath function, it generally converts that argument to the appropriate type using the boolean( ), string( ), or number( ) functions, described later. The exceptions to the weak-typing rule are the functions that take a node-set as an argument. Standard XPath 1.0 provides no means of converting anything that isn't a node-set into a node-set. In some cases a function can operate equally well on multiple argument types. In this case, its type is given simply as object.

boolean( )
boolean boolean(object o)
The boolean( ) function converts its argument to a Boolean according to these rules:

  • Zero and NaN are false. All other numbers are true.

  • Empty node-sets are false. Nonempty node-sets are true.

  • Empty strings are false. Nonempty strings are true.

ceiling( )
number ceiling(number x)
The ceiling( ) function returns the smallest integer greater than or equal to x. For example, ceiling(3.141592) is 4. ceiling(-3.141592) is -3. Before the ceiling is calculated, non-number types are converted to numbers as if by the number( ) function.

concat( )
string concat(string s1, string s2)
string concat(string s1, string s2, string s3)
string concat(string s1, string s2, string s3, string s4)
...
This function concatenates its arguments in order from left to right and returns the combined string. It may take two or more arguments. Nonstrings may be passed to this function as well, in which case they're converted to strings automatically as if by the string( ) function.

contains( )
boolean contains(string s1, string s2)
This function returns true if s2 is a substring of s1--that is, if s1 contains s2--false otherwise. For example, contains("A very Charming cat", "Charm") is true, but contains("A very Charming cat", "Marjorie") is false. The test is case-sensitive. For example, contains("A very charming cat", "Charm") is false. Nonstrings may also be passed to this function, in which case they're automatically converted to strings as if by the string( ) function.

count( )
number count(node-set set)
The count( ) function returns the number of nodes in the argument node-set, that is, the length of the set.

false( )
boolean false( )
The false( ) function always returns false. It makes up for the lack of Boolean literals in XPath.

floor(3e )
number floor(number x)
The floor( ) function returns the greatest integer less than or equal to x. For example, floor(3.141592) is 3. floor(-3.141592) is -4. Before their floor is calculated, non-number types are converted to numbers as if by the number( ) function.

id( )
node-set id(string IDs)
node-set id(node-set IDs)
The id( ) function returns a node-set containing all elements in the document with any of the specified IDs. If the argument is a string, then this string is interpreted as a whitespace-separated list of IDs, and the function returns a node-set containing any elements that have an ID matching one of these IDs. If the argument is a node-set, then each node in the set is converted to a string, which is in turn treated as a white-space-separated list of IDs. The returned node-set contains all the elements whose ID matches any ID in the string value of any of these nodes. Finally, if the argument is any other type, then it's converted to a string, as by the string( ) function, and it returns the same result as passing that string value to id( ) directly.

lang( )
boolean lang(string languageCode)
The lang( ) function returns true if the context node is written in the language specified by the languageCode argument; false otherwise. The nearest xml:lang attribute on the context node or one of its ancestors determines the language of any given node. If no such xml:lang attribute exists, then lang( ) returns false.

The lang( ) function takes into account country and other subcodes before making its determination. For example, lang('fr') returns true for elements whose language code is fr-FR, fr-CA, or fr. However, lang('fr-FR') is not true for elements whose language code is fr-CA or fr.

last( )
number last( )
The last( ) function returns the size of (i.e., the number of nodes in) the context node-set.

local-name( )
string local-name( )
string local-name(node-set nodes)
With no arguments, this function returns the context node's local name, that is, the part of the name after the colon, or the entire name if it isn't prefixed. For a node-set argument, it returns the local name of the first node in the node-set. If the node-set is empty or the first node in the set does not have a name (e.g., it's a comment or root node), then it returns the empty string.

name( )
string name( )
string name(node-set nodes)
With no arguments, this function returns the qualified (prefixed) name of the context node or the empty string if the context node does not have a name (e.g., it's a comment or root node). With a node-set as an argument, it returns the qualified name of the first node in the node-set. If the node-set is empty or if the set's first node does not have a name, then it returns the empty string.

namespace-uri( )
string namespace-uri( )
string namespace-uri(node-set nodes)
With no arguments, this function returns the namespace URI of the context node. With a node-set as an argument, it returns the namespace URI of the first node in the node-set. If this node does not have a namespace URI (i.e., it's not an element or an attribute node; it is an element or attribute node, but is not in any namespace; or the node-set is empty), then it returns the empty string.

normalize-space( )
string normalize-space( )
string normalize-space(string s)
The normalize-space( ) function strips all leading and trailing whitespace from its argument and replaces each run of whitespace with a single space character. Among other effects, this removes all line breaks. If the argument is omitted, it normalizes the string value of the context node. A nonstring may be passed to this function, in which case it's automatically converted to a string, as if by the string( ) function, and that string is normalized and returned.

not( )
boolean not(boolean b)
The not( ) function inverts its argument; that is, false becomes true and true becomes false. For example, not(3 > 2) is false, and not(2+2=5) is true. Non-Booleans are converted as by the boolean( ) function before being processed.

number( )
number number( )
number number(object o)
The number( ) function converts its argument to a number according to these rules:

  • A string is converted by first stripping leading and trailing whitespace and then picking the IEEE 754 value that is closest (according to the IEEE 754 round-to-nearest rule) to the mathematical value represented by the string. If the string does not seem to represent a number, it is converted to NaN. Exponential notation (e.g., 75.2E-12) is not recognized.

  • True Booleans are converted to 1; false Booleans are converted to 0.

  • Node-sets are first converted to a string as if by the string( ) function. The resulting string is then converted to a number.

If the argument is omitted, then it converts the context node.

position( )
number position( )
The position( ) function returns a number equal to the position of the current node in the context node-set. For most axes it counts forward from the context node. However, if the axis in use is ancestor, ancestor-or-self, preceding, or preceding-sibling, then it counts backward from the context node instead.

round( )
number round(number x)
The round( ) function returns the integer closest to x. For example, round(3.141592) returns 3. round(-3.141592) returns -3. If two integers are equally close to x, then the one that is closer to positive infinity is returned. For example, round(3.5) returns 4, and round(-3.5) returns -3. Non-number types are converted to numbers as if by the number( ) function, before rounding.

starts-with( )
boolean starts-with(string s1, string s2)
The starts-with( ) function returns true if s1 starts with s2; false otherwise. For example, starts-with("Charming cat", "Charm") is true, but starts-with ("Charming cat", "Marjorie") is false. The test is case-sensitive. For example, starts-with("Charming cat", "charm") is false. Nonstrings may be passed to this function as well, in which case they're automatically converted to strings as if by the string( ) function, before the test is made.

string( )
string string( )
string string(object o)
The string( ) function converts an object to a string according to these rules:

  • A node-set is converted to the string value of the first node in the node-set. If the node-set is empty, it's converted to the empty string.

  • A number is converted to a string as follows:

    • NaN is converted to the string NaN.

    • Positive Inf is converted to the string Infinity.

    • Negative Inf is converted to the string -Infinity.

    • Integers are converted to their customary English form with no decimal point and no leading zeros. A minus sign is used if the number is negative, but no plus sign is used for positive numbers.

    • Nonintegers (numbers with nonzero fractional parts) are converted to their customary English form with a decimal point, with at least one digit before the decimal point and at least one digit after the decimal point. A minus sign is used if the number is negative, but no plus sign is used for positive numbers.

  • A Boolean with the value true is converted to the English word "true." A Boolean with the value false is converted to the English word "false." Lowercase is always used.

The object to be converted is normally passed as an argument, but if omitted, the context node is converted instead.

WARNING: The XPath specification specifically notes that the "string function is not intended for converting numbers into strings for presentation to users." The primary problem is that it's not localizable and not attractive for large numbers. If you intend to show a string to an end user, use the format-number( ) function and/or xsl:number element in XSLT instead.

string-length( )
number string-length(string s)
number string-length( )
The string-length( ) function returns the number of characters in its argument. For example, string-length("Charm") returns 5. If the argument is omitted, it returns the number of characters in the string value of the context node. A nonstring may be passed to this function, in which case it's automatically converted to a string, as if by the string( ) function, and that string's length is returned.

substring( )
string substring(string s, number index, number length)
string substring(string s, number index)
The substring( ) function returns the substring of s starting at index and continuing for length characters. The first character in the string is at position 1 (not 0, as in Java and JavaScript). For example, substring('Charming cat', 1, 5) returns "Charm". If length is omitted, then the substring to the end of the string is returned. For example, substring('Charming cat', 10) returns "cat". As usual, any type of object may be passed to this function in place of the normal argument, in which case it is automatically converted to the correct type.

substring-after( )
string substring-after(string s1, string s2)
The substring-after( ) function returns the substring of s1 that follows the first occurrence of s2 in s1, or it returns the empty string, if s1 does not contain s2. For example, substring-after('Charming cat', 'harm') returns "ing cat". The test is case-sensitive. As usual, nonstring objects may be passed to this function, in which case they're automatically converted to strings as if by the string( ) function.

substring-before( )
string substring-before(string s1, string s2)
The substring-before( ) function returns the substring of s1 that precedes the first occurrence of the s2 in s1, or it returns the empty string if s1 does not contain s2. For example, substring-before('Charming cat', 'ing') returns "Charm". The test is case-sensitive. Nonstring objects may be passed to this function, in which case they're automatically converted to strings as if by the string( ) function.

sum( )
number sum(node-set nodes)
The sum( ) function converts each node in the node-set to a number, as if by the number( ) function, then it adds up those numbers and returns the sum.

translate( )
string translate(string s1, string s2, string s3)
The translate( ) function looks in s1 for any characters found in s2. It replaces each character with the corresponding character from s3. For example, translate("XML in a Nutshell", " ", "_") replaces the spaces with underscores and returns "XML_in_a_Nutshell". translate("XML in a Nutshell", "XMLN", "xmln") replaces the uppercase letters with lowercase letters and returns "xml in a nutshell". If s3 is shorter than s2, then characters in s1 and s2 with no corresponding character in s3 are simply deleted. For example, translate("XML in a Nutshell", " ", "") deletes the spaces and returns "XMLinaNutshell". Once again, nonstring objects may be passed to this function, in which case they're automatically converted to strings, as if by the string( ) function.

true( )
boolean true( )
The true( ) function simply returns true. It makes up for the lack of Boolean literals in XPath.

Previous | Home | Next
22.4. Predicates | Book Index | 23. XSLT Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 23. XSLT Reference

Contents:

The XSLT Namespace
XSLT Elements
XSLT Functions
TrAX

Extensible Stylesheet Language Transformations (XSLT) is a functional programming language used to specify how an input XML document is converted into another text document--possibly, though not necessarily, another XML document. An XSLT processor reads both an input XML document and an XSLT stylesheet (which is itself an XML document because XSLT is an XML application) and produces a result tree as output. This result tree may then be serialized into a file or written onto a stream. Documents can be transformed using a standalone program or as part of a larger program that communicates with the XSLT processor through its API.

23.1. The XSLT Namespace

All standard XSLT elements are in the http://www.w3.org/1999/XSL/Transform namespace. In this chapter, we assume that this URI is mapped to the xsl prefix using an appropriate xmlns:xsl declaration somewhere in the stylesheet. This mapping is normally declared on the root element like this:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!-- XSLT top-level elements go here -->
</xsl:stylesheet>
Previous | Home | Next
22.5. XPath Functions | Book Index | 23.2. XSLT Elements
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Elements (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

23.2. XSLT Elements

XSLT defines 37 elements, which break down into 3 overlapping categories:

  • Two root elements:

    xsl:stylesheet
xsl:transform

  • 12 top-level elements. These elements may appear as immediate children of the root and are the following:

    xsl:attribute-set          xsl:decimal-format
xsl:import                 xsl:include
xsl:key                    xsl:namespace-alias
xsl:output                 xsl:param
xsl:preserve-space         xsl:strip-space
xsl:template               xsl:variable

  • 23 instruction elements. These elements appear in the content of elements that contain templates. Here we don't mean the xsl:template element. We mean the content of that and several other elements, such as xsl:for-each and xsl:message, which are composed of literal result elements, character data, and XSLT instructions that are processed to produce part of the result tree. These elements are as follows:

    xsl:apply-imports          xsl:apply-templates
xsl:attribute              xsl:call-template
xsl:choose                 xsl:comment
xsl:copy                   xsl:copy-of
xsl:element                xsl:fallback
xsl:for-each               xsl:if
xsl:message                xsl:number
xsl:otherwise              xsl:param
xsl:processing-instruction xsl:sort
xsl:text                   xsl:value-of
xsl:variable               xsl:with-param
xsl:when

Most XSLT processors also provide various nonstandard extension elements and allow you to write your own extension elements in languages such as Java and JavaScript.

Elements in this section are arranged alphabetically from xsl:apply-imports to xsl:with-param. Each element begins with a synopsis in the following form:

<xsl:elementName
   attribute1 = "allowed attribute values"
   attribute2 = "allowed attribute values"
>
  <!-- Content model -->
</xsl:elementName>

Most attribute values are one of the following types:

expression
An XPath expression. In cases where the expression is expected to return a value of a particular type, such as node-set or number, it is prefixed with the type and a hyphen, for example, node-set-expression or number-expression. However, XPath is weakly typed, and in most cases, any supplied type will be converted to the requested type. For instance, an attribute that should contain a string might in fact contain a number or a node-set. The processor automatically converts this number or set to a string, according to the rules given in the last chapter for XPath's string( ) function. The only exception to this rule is node-set-expression. XSLT does not convert other types to node-sets automatically. If an attribute requires a node-set-expression, then it is an error to set its value to another type of expression such as a boolean or string.

QualifiedName
An XML name, such as set or mathml:set. If the name is in a nondefault namespace, then it has a prefix.

PrefixedName
An XML name that must have a prefix such as mathml:set but not set.

pattern
An XSLT pattern; that is, a group of one or more XPath location-path expressions separated by |. in which each location step uses only the child or attribute axis. The initial step may be an id( ) or key( ) function call with a literal argument.

langcode
An RFC 1766 language code, such as en or fr-CA.

string
A literal string of text.

char
A single Unicode character.

enumerated type
One value in a finite list of values. The values shown here are separated by vertical bars, as in an enumerated content model in an ATTLIST declaration.

URI
A relative or absolute URI reference. In practice, these are normally URLs. Relative URIs are relative to the location of the stylesheet itself.

Some attributes that contain strings--whether those strings are literals, expressions, names, or something else--can be given as attribute value templates. This is an XPath expression enclosed in curly braces, which is evaluated to provide the final value of the attribute. When this is the case, it is indicated in the description of each attribute.

Potentially nonempty elements have content models given in a comment in the form they might appear in an ELEMENT declaration. If an element can contain a template, we use the word "template" to stand in for all the possible elements that may appear.

xsl:apply-imports
<xsl:apply-imports />
The xsl:apply-imports instruction processes the current node using only templates that were imported into the stylesheet with xsl:import. A template rule that overrides a template rule in an imported stylesheet can invoke the overridden template rule with xsl:apply-imports.

xsl:apply-templates
<xsl:apply-templates
  select = "node-set-expression"
  mode   = "QualifiedName">
  <! --  (xsl:sort | xsl:with-param)*  -- >
</xsl:apply-templates>
The xsl:apply-templates instruction tells the processor to search for and apply the highest-priority template in the stylesheet that matches each node identified by the select attribute.

Attributes

select, optional
This is an XPath expression that returns a node-set. Each node in this set will be processed further. If the select attribute is omitted, then all child nodes of the context node should be processed.

mode, optional
If the mode attribute is present, then only templates that have a matching mode attribute will be applied. If the mode attribute is absent, then only templates without a mode attribute will be applied.

Contents

The xsl:apply-templates element may have xsl:sort child elements to specify the order in which the selected nodes will be processed. Without any xsl:sort children, the default is to process nodes in document order.

The xsl:apply-templates element may have xsl:with-param child elements to pass parameter values to the matched templates.

xsl:attribute
<xsl:attribute
  name      = "QualifiedName"
  namespace = "URI">
  <! --  template for the attribute value  -- >
</xsl:attribute>
The xsl:attribute instruction adds an attribute to an element in the result tree. This element can be a child of an xsl:attribute-set element, an xsl:element instruction, or a literal result element. In each case, all xsl:attribute elements must precede all literal result elements and other instructions that insert content into the output element.

Attributes

name, required, attribute value template
The name of the attribute this instruction creates.

namespace, optional, attribute value template
The namespace URI of the attribute. If a nonempty namespace URI is specified, then the processor will pick an appropriate prefix for the attribute, probably but not necessarily the one used in the name attribute.

Contents

The contents of this element are a template whose instantiation only produces text nodes. The value of the attribute added to the result tree is determined by instantiating the template.

xsl:attribute-set
<xsl:attribute-set
  name = "QualifiedName"
  use-attribute-sets = "QualifiedNames">
  <! --  xsl:attribute*  -- >
</xsl:attribute-set>
The xsl:attribute-set top-level element defines a collection of attributes that can be applied to elements elsewhere in the stylesheet. For instance, you could define an attribute set that includes the necessary attributes to create a simple XLink, and then you could attach the set to each simple XLink element.

Attributes

name, required
The name attribute gives a name for the set, by which xsl:element and other xsl:attribute-set elements can load this attribute set.

use-attribute-sets, optional
The use-attribute-sets attribute adds attributes from a different attribute set into this attribute set. More than one attribute set can be loaded by separating multiple names with whitespace. The attributes defined in all loaded sets and all attributes defined by child xsl:attribute elements are merged so that no attribute appears in the set more than once. It is an error if an attribute set uses itself directly or indirectly.

Contents

This element contains zero or more xsl:attribute elements. Each such element adds one attribute to the set.

xsl:call-template
<xsl:call-template
   name = "QualifiedName">
   <! --  xsl:with-param*  -- >
</xsl:call-template>
The xsl:call-template instruction invokes a template by name. The current node and context node list are the same for the called template as for the calling template. Templates may be called recursively; an xsl:template element may contain an xsl:call-template element that calls that very xsl:template element. This technique is useful for doing things you'd accomplish with loops in a traditional procedural programming language.

Attributes

name, required
The name of the xsl:template element to call.

Contents

This element contains zero or more xsl:with-param elements that pass parameters to the named template.

xsl:choose
<xsl:choose>
  <! --  (xsl:when+, xsl:otherwise?)  -- >
</xsl:choose>
The xsl:choose element selects zero or one of a sequence of alternatives.

Contents

This element contains one or more xsl:when elements, each of which has a test condition. The contents are output for the first xsl:when child whose test condition is true.

The xsl:choose element may have an optional xsl:otherwise element whose contents are output only if none of the test conditions in any of the xsl:when elements is true.

If no xsl:otherwise element exists and none of the test conditions in any of the xsl:when child elements is true, then this element will not produce output.

xsl:comment
<xsl:comment>
  <! --  template  -- >
</xsl:comment>
The xsl:comment instruction inserts a comment into the result tree.

Contents

The content of xsl:comment is a template that will be instantiated to form the text of the comment inserted into the result tree. The result of instantiating this template must only be text nodes that do not contain the double hyphen (--) (since comments cannot contain the double hyphen).

xsl:copy
<xsl:copy
  use-attribute-sets = "QualifiedName1 QualifiedName2...">
  <! --  template  -- >
</xsl:copy>
The xsl:copy element copies the current node from the source document into the output document. It copies the node itself and any namespace nodes the node possesses. However, it does not copy the node's children or attributes.

Attributes

use-attribute-sets, optional
A whitespace-separated list of xsl:attribute-set names. These attribute sets are merged, and all attributes in the merged set are added to the copied element. The use-attribute-sets attribute can be used only when the copied node is an element node.

Contents

If the current node is an element node, attributes can be added via xsl:attribute children. If the current node is the root node or an element node (a node that can have children), then xsl:copy may contain a template that specifies the content of the element inserted into the result tree. All xsl:attribute elements must precede the output template.

xsl:copy-of
<xsl:copy-of
   select = "expression" />
The xsl:copy-of instruction inserts whatever is identified by the select attribute into the output document. This instruction copies the specific nodes identified by the expression, as well as all those nodes' children, attributes, namespaces, and descendants. This is how it differs from xsl:copy: if the expression selects something other than a node-set or a result-tree fragment, such as a number, then the expression is converted to its string value and the string is output.

Attributes

select, required
An XPath expression identifying the object to copy into the result tree.

xsl:decimal-format
<xsl:decimal-format
   name               = "QualifiedName">
   decimal-separator  = "char"
   grouping-separator = "char"
   infinity           = "string"
   minus-sign         = "char"
   NaN                = "string"
   percent            = "char"
   per-mille          = "char"
   zero-digit         = "char"
   digit              = "char"
   pattern-separator  = "char" />
The xsl:decimal-format top-level element defines a pattern by which the format-number( ) function can convert floating point numbers into text strings. The defaults work well for English, but details may change for other languages and locales, such as French or Chinese.

Attributes

name, optional
The string by which the format-number( ) function identifies the xsl:decimal-format element to use. If this attribute is omitted, then this element establishes the default decimal format used by the format-number( ) function.

decimal-separator, optional
The character that separates the integer part from the fractional point in a floating point number. This character is a period (decimal point) in English and a comma in French. It may be something else in other languages.

grouping-separator, optional
The character that separates groups of digits (e.g., the comma that separates every three digits in English).

infinity, optional
The string that represents IEEE 754 infinity; Infinity by default.

minus-sign, optional
The character prefixed to negative numbers; a hyphen by default.

NaN, optional
The string that represents IEEE 754 Not a Number; NaN by default.

percent, optional
The character that represents a percent; % by default.

per-mille, optional
The character that represents a per mille; Figure by default.

zero-digit, optional
The character that represents zero; 0 by default. Digits 1 through 9 will be represented by the nine subsequent Unicode values after this one. For instance, setting zero-digit to A would set 1 to B, 2 to C, 3 to D, and so on. This is also the character used to represent 0 in format patterns.

digit, optional
The character that represents a digit in a format pattern; # by default.

pattern-separator, optional
The character that separates positive and negative subpatterns in a format pattern; ; by default.

xsl:element
<xsl:element
  name               = "QualifiedName"
  namespace          = "URI"
  use-attribute-sets = "QualifiedName1 QualifiedName2...">
  <! --  template  -- >
</xsl:element>
The xsl:element instruction inserts an element into the result tree. The element's name is given by the name attribute. The element's namespace URI, if any, is given by the optional namespace attribute. Attributes can be added via xsl:attribute children or by referencing an xsl:attribute-set declared elsewhere in the stylesheet from the use-attribute-sets attribute. Finally, the element's contents are determined by instantiating the template contained in the xsl:element element's content.

Attributes

name, required, attribute value template
The name of the element this instruction creates.

namespace, optional, attribute value template
The namespace URI of the element this instruction creates. If this attribute is omitted, then the namespace is determined by matching the name's prefix (or lack thereof) to the namespace declarations in scope at this point in the stylesheet.

use-attribute-sets, optional
A whitespace-separated list of names of xsl:attribute-set elements declared as top-level elements elsewhere in the stylesheet. These attribute sets are merged, and all attributes in the merged set are added to the element.

Contents

The contents of this element are a template. Once instantiated, this template forms the content of the element inserted into the result tree.

xsl:fallback
<xsl:fallback>
  <! --  template  -- >
</xsl:fallback>
The xsl:fallback instruction normally appears as a child of an extension element. If the processor does not recognize the extension element, then it instantiates the contents of all the element's xsl:fallback children in order. If the processor does recognize the element in which the xsl:fallback element appears, then the contents of the xsl:fallback element will not be output.

Contents

The contents of this element are a template that is instantiated and output if and only if the XSLT processor does not recognize the xsl:fallback element's parent element.

xsl:for-each
<xsl:for-each
   select = "node-set-expression">
   <! --  (xsl:sort*, template)  -- >
</xsl:for-each>
The xsl:for-each instruction iterates over the nodes identified by its select attribute and applies templates to each one.

Attributes

select, required
An XPath node-set expression identifying which nodes to iterate over.

Contents

Normally, the selected nodes are processed in the order in which they appear in the document. However, nodes can be sorted using xsl:sort child elements. The first such element is the primary sort key; the second is the secondary sort key; and so on.

The xsl:for-each element must also contain a template that is instantiated once for each member of the node-set returned by the node-set expression in the select attribute.

xsl:if
<xsl:if
  test = "boolean-expression">
  <! --  template  -- >
</xsl:if>
The xsl:if instruction contains a template that is instantiated if and only if the XPath expression contained in its test attribute is true. There is no xsl:else or xsl:else-if element. For these purposes, use xsl:choose instead.

Attributes

test, required
An XPath expression returning a Boolean. If this expression is true, the contents of the xsl:if element are instantiated. If it's false, they're not.

Contents

A template is instantiated if the test attribute evaluates to true.

xsl:import
<xsl:import
  href = "URI" />
The xsl:import top-level element imports the XSLT stylesheet found at the URI given by the href attribute. Source documents are processed using the combination of templates in the imported and importing stylesheets. In the event of a conflict between templates in the two stylesheets, the ones in the importing stylesheet take precedence. In the event of a conflict between imported stylesheets, the last one imported takes precedence.

All xsl:import elements must be immediate children of the root xsl:stylesheet element. Furthermore, they must appear before all other top-level elements.

An imported stylesheet may itself import another stylesheet. A stylesheet may not import a stylesheet that was already imported, directly or indirectly. That is, it's an error if A imports B, which imports A, thus creating a circular reference.

Attributes

href, required
The relative or absolute URI of the stylesheet to import. Relative URIs are resolved relative to the base URI of the importing stylesheet.

xsl:include
<xsl:include
   href = "URI" />
The xsl:include top-level element copies the contents of the xsl:stylesheet or xsl:transform element found at the URI given by the href attribute. Unlike xsl:import, whether a template or other rule comes from the including or the included stylesheet has absolutely no effect on the precedence of the various rules.

An included stylesheet may include another stylesheet. A stylesheet may not include a stylesheet that was already included, directly or indirectly; it is an error if A includes B, which includes A.

Attributes

href, required
The relative or absolute URI of the stylesheet to include. Relative URIs are resolved relative to the including stylesheet's base URI.

xsl:key
<xsl:key
   name  = "QualifiedName"
   match = "pattern"
   use   = "expression" />
The xsl:key top-level element defines one or more keys that can be referenced from elsewhere in the stylesheet using the key( ) function. Each key has a name, a string value, and a node.

Attributes

name, required
The key's name.

match, required
An XSLT match pattern, like that used by xsl:template, specifying which nodes have this key. If this pattern matches more than one node in the source document, then a single xsl:key element may define many keys, all with the same name and possibly the same value, but with different nodes.

use, required
An XPath expression that is converted to a string to give the value of keys defined by this element. The expression is evaluated with respect to each key's node. If match identifies multiple nodes, then use may produce different values for each key.

xsl:message
<xsl:message
  terminate = "yes" | "no">
  <! --  template  -- >
</xsl:message>
The xsl:message instruction sends a message to the XSLT processor. Which messages the processor understands and what it does with messages it does understand is processor dependent. Printing debugging information on stderr or stdout is one common use of xsl:message.

Attributes

terminate, optional
If the attribute is present and has the value yes, then the XSLT processor should halt after the message is delivered and acted on.

Contents

An xsl:message element's content is a template instantiated to create an XML fragment. The result is then delivered to the XSLT processor as the message.

WARNING: The XSLT specification does not define XML fragment, and various XSLT processors interpret it differently. It may be a result tree fragment or an XML fragment, as defined by the now moribund XML Fragment Interchange working draft. It may be something else. Clarification from the W3C is necessary.

xsl:namespace-alias
<xsl:namespace-alias
   stylesheet-prefix = "prefix"
   result-prefix     = "prefix" />
The top-level xsl:namespace-alias element declares that one namespace URI in the stylesheet should be replaced by a different namespace URI in the result tree. Aliasing is particularly useful when you're transforming XSLT into XSLT using XSLT; consequently, which names belong to the input, which belong to the output, and which belong to the stylesheet is not obvious.

Attributes

stylesheet-prefix, required
The prefix used inside the stylesheet itself. May be set to #default to indicate that the nonprefixed default namespace should be used.

result-prefix, required
The prefix used in the result tree. May be set to #default to indicate that the nonprefixed default namespace should be used.

xsl:number
<xsl:number
   value  = "number-expression"
   count  = "pattern"
   from   = "pattern"
   level  = "single" | "multiple" | "any"
   format = "letter or digit"
   lang   = "langcode"
   letter-value = "alphabetic" | "traditional"
   grouping-separator = "char"
   grouping-size = "number" />
The xsl:number instruction inserts a formatted integer into the result tree.

Attributes

value, optional
This XPath expression returns the number to be formatted. If necessary, the result of the expression is rounded to the nearest integer. The value attribute is often omitted, in which case the number is calculated from the position of the current node in the source document. The position is calculated as specified by the level, count, and from attributes.

level, optional
This attribute specifies which levels of the source tree should be considered in determining the position of the current node. It can be set to single to count the preceding siblings of the current node's ancestor that match the count pattern. It can be set to any to count all nodes in the document that match the count pattern and precede the current node. It can be set to multiple to produce hierarchical sequences of numbers such as 2.7.3, where each number in the sequence is calculated from the preceding sibling's ancestor node that matches the count pattern. The default is single.

count, optional
This attribute contains a pattern that specifies which nodes should be counted at those levels. The default is to count all nodes with the same node type (element, text, attribute, etc.) and name as the current node.

from, optional
This attribute contains a pattern identifying the node from which counting starts; that is, it identifies a node that serves as a cutoff point. Any nodes that precede this node are not counted, even if they match the count pattern.

format, optional, attribute value template
This attribute determines how the list is numbered. Format tokens and sequences they produce include the following:

1
1, 2, 3, 4, 5, 6, . . .

01
01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, . . .

A
A, B, C, D, . . . ,Z, AA, AB, AC, . . .

a
a, b, c, d, . . . ,z, aa, ab, ac, . . .

i
i, ii, iii, iv, v, vi, vii, viii, ix, x, xi, . . .

I
I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, . . .

You can also change the starting point; for instance, setting the format token to 5 would create the sequence 5, 6, 7, 8, 9, . . .

lang, optional, attribute value template
This is the RFC 1766 language code describing the language in which the number should be formatted (e.g., en or fr).

letter-value, optional, attribute value template
The default is traditional. However, you can set this attribute to alphabetic to indicate that a format of I should start the sequence I, J, K, L, M, N, . . . rather than I, II, III, IV, V, VI, . . .

grouping-separator, optional, attribute value template
This is the character that separates groups of digits. For instance, in English the comma customarily separates every three digits, as in 2,987,667,342. In French a space is used instead, so this number would be formatted as 2 987 667 342.

grouping-size, optional, attribute value template
This is the number of digits in each group. In most languages, including English, digits are divided into groups of three. However, a few languages use groups of four.

xsl:otherwise
<xsl:otherwise>
  <! --  template  -- >
</xsl:otherwise>
The xsl:otherwise element only appears as the last child element of an xsl:choose element. It serves as the default result if no xsl:when element in the same xsl:choose element is instantiated.

Contents

The contents are a template that is instantiated if and only if none of the xsl:choose element's xsl:when sibling elements is true.

xsl:output
<xsl:output
  method                 = "xml" | "html" | "text" | "PrefixedName"
  version                = "NMTOKEN"
  encoding               = "encoding_name"
  omit-xml-declaration   = "yes" | "no"
  standalone             = "yes" | "no"
  doctype-public         = "PUBLIC_ID"
  doctype-system         = "SYSTEM_ID"  cdata-section-elements = "element_name_1 element_name_2..."
  indent                 = "yes" | "no"
  media-type             = "string" />
The top-level xsl:output element helps determine the exact formatting of the XML document produced when the result tree is stored in a file, written onto a stream, or otherwise serialized into a sequence of bytes. It has no effect on the production of the result tree itself.

Attributes

method, optional
The default method is xml, which simply means that the serialized output document will be a well-formed external parsed entity or XML document. If method is set to html or if the method attribute is not present and the root element of the output tree is html, in any combination of case, then the processor attempts to generate HTML that is more compatible with existing browsers. For example, empty-element tags like <br/> are converted to <br>. The text method outputs only the contents of the text nodes in the output tree. It strips all markup. XSLT processors may also recognize and support other values that are indicated by prefixed names such as saxon:xhtml and jd:canonical-xml.

version , optional
This is a name token that identifies the output method's version. In practice, this has no effect on the output.

encoding, optional
This is the name of the encoding the outputter should use, such as ISO-8859-1 or UTF-16.

omit-xml-declaration, optional
If this attribute has the value yes, then no XML declaration is included. If it has the value no or is not present, then an XML declaration is included.

standalone, optional
This attribute sets the standalone attribute's value in the XML declaration. Like that attribute, it must have the value yes or no.

doctype-public, optional
This attribute sets the public identifier used in the document type declaration.

doctype-system, optional
This attribute sets the system identifier used in the document type declaration.

cdata-section-elements, optional
This is a whitespace-separated list of qualified element names in the result tree whose contents should be emitted using CDATA sections rather than character references.

indent, optional
If this attribute has the value yes, then the processor is allowed (but not required) to insert extra whitespace to attempt to "pretty-print" the output tree. The default is no.

media-type, optional
This is the output's MIME media type, such as text/html or text/xml.

xsl:param
<xsl:param
  name   = "QualifiedName"
  select = "expression">
  <! --  template  -- >
</xsl:param>
Inside an xsl:template element, an xsl:param element receives a named argument passed to the template by xsl:with-param. It also provides a default value that's used when the caller does not provide a value for the parameter. A top-level xsl:param element provides a default value for multiple templates. If an xsl:apply-templates or xsl:call-template passes in a parameter value using xsl:with-param when the template is invoked, then this value overrides any default value the xsl:param element may have. The parameter can be dereferenced using the form $name in expressions.

Attributes

name, required
The parameter's name.

select, optional
An XPath expression that is evaluated to produce the parameter's value. If xsl:param has a select attribute, then it must be an empty element. If a nonempty xsl:param element does not have a select attribute, then the value is taken from the element's contents. If an empty xsl:param element does not have a select attribute, then the value is the empty string.

Contents

An xsl:param element's content is a template that is instantiated to produce a result-tree fragment. This result-tree fragment then becomes the parameter's value. A nonempty xsl:param element must not have a select attribute.

xsl:preserve-space
<xsl:preserve-space
   elements="QualifiedName_1 QualifiedName_2..." />
The top-level xsl:preserve-space element specifies which elements in the source document will not have whitespace stripped from them before they are transformed. Whitespace stripping removes text nodes that contain only whitespace (the space character, the tab character, the carriage return, and the linefeed). By default, whitespace is preserved in an element unless its name is listed in the elements attribute of an xsl:strip-space element. This element allows you to override the list given in xsl:strip-space; if an element is listed in both xsl:strip-space and xsl:preserve-space, then its whitespace is preserved.

Attributes

elements, required
A whitespace-separated list of elements in which space should be preserved. Besides element names, the elements attribute can contain an asterisk to indicate that whitespace should be preserved in all elements or contain a namespace prefix followed by a colon and an asterisk to indicate that whitespace should be preserved in all elements in the given namespace.

xsl:processing-instruction
<xsl:processing-instruction
  name = "target">
  <! --  template  -- >
</xsl:processing-instruction>
The xsl:processing-instruction element inserts a processing instruction into the result tree.

Attributes

name, required, attribute value template
The processing instruction's target.

Contents

The xsl:processing-instruction element's contents are a template that is instantiated to produce the processing-instruction data. This template may include XSLT instructions, provided that the result of instantiating this template is text that does not contain the two-character string ?>.

xsl:sort
<xsl:sort
   select     = "string-expression"
   data-type  = "text" | "number" | "PrefixedName"
   lang       = "langcode"
   order      = "ascending" | "descending"
   case-order = "upper-first" | "lower-first" />
The xsl:sort instruction appears as a child of either xsl:apply-templates or xsl:for-each. It changes the order in which templates are applied to the context node list from document order to another order, such as alphabetic. You can perform multiple key sorts (e.g., sort first by last name, then by first name, then by middle name) using multiple xsl:sort elements in descending order of the keys' importance.

Attributes

select, optional
This is the key to sort by. If select is omitted, then the sort key is set to the value of the current node.

data-type, optional, attribute value template
By default, sorting is purely alphabetic. However, alphabetic sorting leads to strange results with numbers. For instance, 10, 100, and 1000 all sort before 2, 3, and 4. You can specify numeric sorting by setting the data-type attribute to number.

lang, optional, attribute value template
Sorting is language dependent. Setting the lang attribute to an RFC 1766 language code changes the language. The default language is system dependent.

order, optional, attribute value template
This is the order by which strings are sorted. This order can be either descending or ascending. The default is ascending order.

case-order, optional, attribute value template
The case-order attribute can be set to upper-first or lower-first to specify whether uppercase letters sort before lowercase letters, or vice versa. The default depends on the language.

xsl:strip-space
<xsl:strip-space
   elements="QualifiedName_1 QualifiedName_2..." />
The top-level xsl:strip-space element specifies which elements in the source document have whitespace stripped from them before they are transformed. Whitespace stripping removes all text nodes that contain only whitespace (the space character, the tab character, the carriage return, and the linefeed). By default whitespace is not stripped from an element unless its name is listed in the elements attribute of an xsl:strip-space element.

This element does not trim leading or trailing whitespace or otherwise normalize whitespace in elements that contain even a single nonwhitespace character.

Attributes

elements, required
A whitespace-separated list of elements in which space should be stripped. Besides element names, the elements attribute can contain an asterisk to indicate that whitespace should be stripped in all elements or contain a namespace prefix followed by a colon and asterisk to indicate that whitespace should be stripped in all elements in the given namespace.

xsl:stylesheet

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  id = "ID"
  extension-element-prefixes = "prefix1 prefix2..."
  exclude-result-prefixes = "prefix1 prefix2..."
  version = "1.0">
  <! --  (xsl:import*, top-level-elements)  -- >
</xsl:stylesheet>
The xsl:stylesheet element is the root element for XSLT documents.

Attributes

xmlns:xsl, technically optional but de facto required
A standard namespace declaration that maps the prefix xsl to the namespace URI http://www.w3.org/1999/XSL/Transform. The prefix can be changed if necessary.

version, required
Currently, always the value 1.0. However, XSLT 2.0 may be released in 2003 with a concurrent updating of this number.

id, optional
Any XML name that's unique within this document's ID type attributes.

extension-element-prefixes, optional
A whitespace-separated list of namespace prefixes used by this document's extension elements.

exclude-result-prefixes, optional
A whitespace-separated list of namespace prefixes whose declarations should not be copied into the output document.

Contents

Any xsl:import elements, followed by any other top-level elements in any order.

xsl:template
<xsl:template
  match    = "pattern"
  priority = "number"
  name     = "QualifiedName"
  mode     = "QualifiedName">
  <! --  (xsl:param*, template)  -- >
</xsl:template>
The xsl:template top-level element is the key to all of XSLT. A little confusingly, the xsl:template element itself is not a template. Rather, it contains a template. The entire xsl:template element is called a template rule. The match attribute contains a pattern against which nodes are compared as they're processed. If the pattern matches a node, then the template (i.e., the contents of the template rule) is instantiated and inserted into the output tree.

Attributes

match, optional
A pattern against which nodes can be compared. This pattern is a location path using only the child, attribute, and descendant-or-self axes.

name, optional
A name by which this template rule can be invoked from an xsl:call-template element, rather than by node matching.

priority, optional
A number. If more than one template rule with the same import precedence matches a given node, the one with the highest priority is chosen. If this attribute is not present, then the template rule's priority is calculated in the following way:

  • Template rules with match patterns composed of just an element or attribute name (e.g., person or @profession) have priority 0.

  • Template rules with match patterns composed of just a processing-instruction('target') node test have priority 0.

  • Template rules with match patterns in the form prefix:* have priority -0.25.

  • Template rules with match patterns that just have a wildcard node test (*, @*, comment( ), node( ), text(), and processing-instruction( )) have priority -0.5. (This means that built-in template rules have priority -0.5. However, they are also imported before all other template rules, and thus never override any explicit template rule, regardless of priority.)

  • Template rules with any other patterns (person[name='Feynman'], people/person/@profession, person/text( ), etc.) have priority 0.5.

  • It is an error if two or more template rules match a node and have the same priority. However, in this case most XSLT processors choose the last template rule occurring in the stylesheet rather than signaling the error.

mode, optional
If the xsl:template element has a mode, then this template rule is matched only when the calling instruction's mode attribute matches this mode attribute's value.

Contents

The template that should be instantiated when this element is matched or called by name.

xsl:text
<xsl:text
   disable-output-escaping = "yes" | "no">
   <! --  #PCDATA  -- >
</xsl:text>
The xsl:text instruction is used inside templates to indicate that its contents should be output as text. Its contents are pure text, not elements. If the contents are composed exclusively of whitespace, then that whitespace is copied literally into the output document, rather than being stripped as it would be by default in most other elements.

Attributes

disable-output-escaping, optional
Setting the disable-output-escaping attribute to yes indicates that characters such as < and &, which are normally replaced by character or entity references such as &lt; or &#x3C;, should instead be output as the literal characters themselves. Note that the xsl:text element's content in the stylesheet must still be well-formed, and any < or & characters must be written as &lt; or &amp; or the equivalent character references. However, when the output document is serialized, these references are replaced by the actual represented characters rather than references that represent them.

xsl:transform
<xsl:transform
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  id = "ID"
  extension-element-prefixes = "prefix1 prefix2..."
  exclude-result-prefixes = "prefix1 prefix2..."
  version = "1.0">
  <! --  (xsl:import*, top-level-elements)  -- >
</xsl:transform>
The xsl:transform element is a seldom-used synonym for the xsl:stylesheet root element. It has the same attributes and contents as xsl:stylesheet and is used in exactly the same way as xsl:stylesheet. See the description of the xsl:stylesheet " element for the discussion of its attributes and content.

xsl:value-of
<xsl:value-of
   select = "expression"
   disable-output-escaping = "yes" | "no" />
The xsl:value-of element computes the string value of an XPath expression and inserts it into the result tree. The values of the seven different kinds of nodes are as follows:

element
The text content of the element after all entity references are resolved and all tags, comments, and processing instructions are stripped

text
The text of the node

attribute
The normalized value of the attribute

root
The value of the root element

processing instruction
The processing instruction data (<?, ?>, and the target are not included)

comment
The text of the comment (<!-- and --> are not included)

namespace
The namespace URI

You can also take values of things that aren't nodes. The value of a node-set is the value of the first node in the set. The value of a string expression is the string. The value of a number expression is the string form of the number. The value of a Boolean expression is the string true if the Boolean is true or the string false if the Boolean is false.

Attributes

select, required
This is the XPath expression whose value is inserted into the result tree.

disable-output-escaping, optional
If this attribute has the value yes, then when the output document is serialized, characters such as < and & in the value are not replaced with entity or character references. This may result in a malformed document.

xsl:variable
<xsl:variable
  name   = "QualifiedName"
  select = "expression">
  <! --  template  -- >
</xsl:variable>
The xsl:variable element binds a name to a value of any type (string, number, node-set, etc.). This variable can then be dereferenced elsewhere using the form $name in an expression.

TIP: The word variable is a little misleading. Once the value of an xsl:variable is set, it cannot be changed. An xsl:variable is more like a named constant than a traditional variable.

name, required
The variable's name.

select, optional
An XPath expression that sets the value of the variable. If xsl:variable has a select attribute, then it must be an empty element.

Contents

A template that is instantiated to produce the variable's value as a result-tree fragment. If an xsl:variable is not an empty element, it must not have a select attribute. If xsl:variable is empty and does not have a select attribute, then its value is the empty string.

xsl:when
<xsl:when
  test = "boolean-expression">
  <! --  template  -- >
</xsl:when>
The xsl:when element only appears as a child of an xsl:choose element.

Attributes

test, required
An XPath expression that evaluates to either true or false. The xsl:when contents are inserted into the result tree if and only if this is the first xsl:when element in the xsl:choose element whose test attribute evaluates to true.

Contents

The template to be instantiated and inserted into the result tree if the test attribute is true.

xsl:with-param
<xsl:with-param
  name   = "QualifiedName"
  select = "expression">
  <! --  template  -- >
</xsl:with-param>
The xsl:with-param element passes a named parameter to a template that expects it. This can either be a child of xsl:apply-templates or xsl:call-template. An xsl:template element receives the parameter via an xsl:param element with the same name. If a template expects to receive a particular parameter and doesn't get it, then it can take the default from the xsl:param element instead.

Attributes

name, required
The name of the parameter.

select, optional
An XPath expression evaluated to form the value of the parameter. If xsl:with-param has a select attribute, then it must be an empty element. If xsl:with-param does not have a select attribute, then the value is taken from the element's contents.

Contents

A template that is instantiated and passed as the parameter's value. If xsl:with-param is not an empty element, it must not have a select attribute. If xsl:with-param is empty and does not have a select attribute, then its value is the empty string.

Previous | Home | Next
23. XSLT Reference | Book Index | 23.3. XSLT Functions
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Functions (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

23.3. XSLT Functions

XSLT supports all functions defined in XPath. In addition, it defines 10 extra functions. Most XSLT processors also make several extension functions available and allow you to write your own extension functions in Java or other languages. The extension API is nonstandard and processor dependent.

XPath and XSLT functions are weakly typed. Although one type or another is occasionally preferred, the processor normally converts any type you pass in to the type the function expects. Functions that only take node-sets as arguments are an exception to the weak-typing rule. Other data types including strings, numbers, and booleans cannot be converted to node-sets automatically.

XPath and XSLT functions also use optional arguments, which are filled in with defaults if omitted. In the following sections, we list the most common and useful variations of each function.

current( )
node-set current( )
The current( ) function returns a node-set containing a single node, the current node. Outside of an XPath predicate, the current node and the context node (represented by a period in the abbreviated XPath syntax) are identical. However, in a predicate, the current node may change based on other contents in the predicate, while the context node stays the same.

document( )
node-set document(string uri)
node-set document(node-set uris)
node-set document(string uri, node-set base)
node-set document(node-set uris, node-set base)
The document( ) function loads the XML document at the URI specified by the first argument and returns a node-set containing that document's root node. The URI is normally given as a string, but may be given as another type that is converted to a string. If the URI is given as a node-set, then each node in the set is converted to a string, and the returned node-set includes root nodes of all documents referenced by the URI argument.

If the URI contains a fragment identifier, then the node-set returned may indicate something other than the root node and thus contain more than one node. If an error occurs while retrieving a document, most XSLT processors stop processing the stylesheet.

The document( ) function may also take a node-set as an optional second argument, in which case the first node (in document order) in this set is used as the base URI with which to resolve relative URIs given in the first argument. If the second argument is omitted, then base URIs are resolved relative to the stylesheet's location.

element-available( )
boolean element-available(string qualifiedElementName)
element-available( ) returns true if and only if the argument identifies an XSLT element the processor recognizes. If the qualified name maps to a non-null namespace URI, then it refers to an extension element. Otherwise, it refers to a standard XSLT element. Assuming use of a fully conformant processor, you don't need to use this function to test for standard elements; just use it for extension elements.

format-number( )
string format-number(number x, string pattern)
string format-number(number x, string pattern, string decimalFormat)
The format-number( ) function converts the number x to a string using the pattern specified by the second argument, as well as the xsl:decimal-format element named by the third argument (or the default decimal format, if the third argument is omitted).

This function's behavior is modeled after the java.text.DecimalFormat class in Java 1.1 (not 1.2 or later). See http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html for full documentation of the pattern passed as the second argument.

The pattern specifies whether leading and trailing zeros should be printed, whether the number's fractional part is printed, the number of digits in a group, and the leading and trailing suffixes for negative and positive numbers. The patterns are described using an almost Backus-Naur Form grammar, given here:

pattern     ->  subpattern{;subpattern}
subpattern  ->  {prefix}integer{.fraction}{suffix}
prefix      ->  '\\u0000'..'\\uFFFD' - specialCharacters
suffix      ->  '\\u0000'..'\\uFFFD' - specialCharacters
integer     ->  '#'* '0'* '0'
fraction    ->  '0'* '#'*

The first line is not pure BNF. The first subpattern is used for positive numbers. The second subpattern, which may not be present, is used for negative numbers. If it's not present, negative numbers use the positive format, but are prefixed with a minus sign. Table 23-1 defines the symbols used in the grammar.

Table 23-1. Symbols used in the pattern grammar

Symbol | Meaning
0 | A digit, including leading or trailing zeros; may be set to a different character using the zero-digit attribute of xsl:decimal-format.
# | A digit, except for leading or trailing zero; may be set to a different character using the digit attribute of xsl:decimal-format.
. | A decimal separator; may be set to a different character using the decimal-separator attribute of xsl:decimal-format.
, | A grouping separator; may be set to a different character using xsl:decimal-format's grouping-separator attribute.
; | Separates the positive and negative format patterns in a format string; may be set to a different character using the pattern-separator attribute of xsl:decimal-format.
- | A default negative prefix; may be set to a different character using xsl:decimal-format's minus-sign attribute.
% | Multiplies by 100 and shows as percentage; may be set to a different character using xsl:decimal-format's percent attribute.
Figure | Multiplies by 1,000 and shows as per mille; may be set to a different character using xsl:decimal-format's permille attribute.
X | Indicates that any other character can be used in the prefix or suffix.
' | Used to quote special characters in a prefix or suffix.
For instance, #,##0.### is a common decimal-format pattern. The # mark indicates any digit character except a leading or trailing zero. The comma is the grouping separator. The period is the decimal separator. The 0 is a digit that is printed even if it's a nonsignificant zero. This pattern is interpreted as follows:

  1. The integer part contains as many digits as necessary.

  2. The grouping separator separates every three digits.

  3. If the integer part only contains zeros, a single zero is placed before the decimal separator.

  4. Up to three digits are printed after the decimal point. However, any trailing zeros are not printed.

  5. No separate pattern is included for negative numbers. Thus, negative numbers are printed the same as positive numbers, but prefixed with a minus sign.

function-available( )
boolean function-available(string qualifiedName)
function-available( ) returns true if the argument identifies a function in the processor's function library; false otherwise. If the qualified name maps to a non-null namespace URI, then it refers to an extension function. Otherwise, it refers to a built-in function from XPath or XSLT. Assuming you're using a fully conformant processor, however, you don't need to test for standard functions, only for extension functions.

generate-id( )
string generate-id(node-set node)
string generate-id( )
The generate-id( ) function returns a string that can be used as the value of an ID type attribute. This function always produces the same string for the same node and a different string for a different node. If the node-set contains more than one node, then only the first node in the set is considered. If the argument is omitted, then the node-set is set to the context node. If the node-set is empty, then the empty string is returned.

key( )
node-set key(string keyName, string value)
node-set key(string keyName, node-set values)
The key( ) function returns a node-set containing all nodes in the source document that have a key with the name given by the first argument and the value given by the second argument. If the second argument is a node-set, then the node-set returned contains all nodes that have a key with the specified name and a value that matches that of any node in the second argument. Otherwise, the returned node-set contains all nodes that have a key with the specified name and a value that matches the second argument's string value. Key names and values are assigned to nodes using the xsl:key element.

system-property( )
object system-property(string qualifiedPropertyName)
The system-property( ) function returns the value of the named property. The type of the returned object is processor- and property-dependent. If the processor does not recognize the property name, then it returns an empty string.

XSLT processors are required to recognize and return values for these three properties:

xsl:version
A number specifying the version of XSLT implemented by the processor; this is normally 1.0, but may become 2.0 during this book's life span.

xsl:vendor
A string identifying the XSLT processor's vendor; for instance, Apache Software Foundation for Xalan or SAXON 6.4.4 from Michael Kay for SAXON.

xsl:vendor-url
A string containing a URL for the XSLT processor's vendor; for instance, http://xml.apache.org/xalan for Xalan or http://saxon.sourceforge.net for SAXON.

Implementations may also recognize and return values for other processor-dependent properties.

unparsed-entity-uri( )
string unparsed-entity-uri(string entityName)
The unparsed-entity-uri( ) function returns the URI of the unparsed entity with the specified name declared in the source document's DTD or the empty string, if no unparsed entity with that name exists.

Previous | Home | Next
23.2. XSLT Elements | Book Index | 23.4. TrAX
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


TrAX (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

23.4. TrAX

Unfortunately, there is no standard API for XSLT that works across languages and engines: each vendor provides its own unique API. The closest thing to a standard XSLT API is TrAX (the Transformations API for XML), included in JAXP. However, this is limited to Java and is not even supported by all Java-based XSLT engines. Nonetheless, since it is the closest thing to a standard there is, we will discuss it here.

Code that transforms an XML document using an XSLT stylesheet through TrAX follows these six steps. All of the classes mentioned are in the javax.xml.transform package, a standard part of Java 1.4 and a separately installable option in earlier versions.

  1. Call the TransformerFactory.newInstance( ) factory method to create a new TransformerFactory object.

  2. Construct a Source object from the XSLT stylesheet.

  3. Pass this Source object to the TransformerFactory object's newTransform( ) method to create a Transform object.

  4. Construct a Source object from the input XML document you wish to transform.

  5. Construct a Result object into which the transformed XML document will be output.

  6. Pass the Source and the Result to the Transform object's transform( ) method.

The source can be built from a DOM Document object, a SAX InputSource, or an InputStream -- represented by the javax.xml.transform.dom.DOMSource, javax.xml.transform.sax.SAXSource, and javax.xml.transform.stream.StreamSource classes, respectively. The result of the transform can be a DOM Document object, a SAX ContentHandler, or an OutputStream. These are represented by the javax.xml.transform.dom.DOMResult, javax.xml.transform.sax.SAXResult, and javax.xml.transform.stream.StreamResult classes, respectively.

For example, this code fragment uses the XSLT stylesheet found at http://www.cafeconleche.org/books/xian/examples/08/8-8.xsl to transform the file people.xml in the current working directory onto System.out:

TransformerFactory factory = TransformerFactory.newInstance( );
URL u = new URL(
 "http://www.cafeconleche.org/books/xian/examples/08/8-8.xsl");
InputStream in     = u.openStream( );
Source stylesheet  = new StreamSource(in);
Transformer xform  = factory.newTransformer(stylesheet);
InputStream people = new FileInputStream("people.xml");
Source original    = new StreamSource(people);
Result transformed = new StreamResult(System.out);
xform.transform(original, transformed);

The procedure is much the same when the source or result is a DOM Document object or a SAX event stream. Just use the DOMSource, SAXSource, DOMResult, and/or SAXResult classes as appropriate. For example, this code fragment transforms the DOM Document object doc according to the stylesheet at http://www.cafeconleche.org/books/xian/examples/08/8-8.xsl and passes the result through the SAX ContentHandler object named handler:

Document doc;
// Build the doc object in the usual way...
TransformerFactory factory = TransformerFactory.newInstance( );
URL u = new URL(
 "http://www.cafeconleche.org/books/xian/examples/08/8-8.xsl");
InputStream in     = u.openStream( );
Source stylesheet  = new StreamSource(in);
Transformer xform  = factory.newTransformer(stylesheet);
ContentHandler handler = new XMLCounter( ); // From Chapter 19
Source original    = new DOMSource(doc);
Result transformed = new SAXResult(handler);
xform.transform(original, transformed);
Previous | Home | Next
23.3. XSLT Functions | Book Index | 24. DOM Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


DOM Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 24. DOM Reference

Contents:

Object Hierarchy
Object Reference

The Document Object Model (DOM) is a language- and platform-independent object framework for manipulating structured documents (see Chapter 18 for additional information). The current W3C recommendation specifies what is called the Level 2 DOM. The full Level 2 DOM is designed to support editing of HTML documents, with several classes and methods specific to HTML document structures. This larger DOM is built on top of a smaller, but complete, subset called the Core DOM. Only the Core DOM is required to support editing of XML documents.

TIP: Other parts of DOM Level 2 may be useful for specific kinds of XML processing, particularly the Style, Traversal, and Range modules.

This reference section documents the Levels 1 and 2 Core DOM objects, using the language-neutral OMG IDL object descriptions. Included with each IDL description is the language-specific binding for the Java programming language. Level 2-only constructs are indicated using the 2 symbol after the given attribute or method name.

TIP: This chapter is based on the Document Object Model (DOM) Level 2 Core Specification, which was released on November 13, 2000. The latest version of this recommendation, along with any errata that have been reported, is available on the W3C DOM Activity's web site (http://www.w3.org/DOM/DOMTR).

The DOM structures a document as a hierarchy of Node objects. The Node interface is the base interface for every member of a DOM document tree. It exposes attributes common to every type of document object and provides a few simple methods to retrieve type-specific information without resorting to downcasting. This interface also exposes all methods used to query, insert, and remove objects from the document hierarchy. The Node interface makes it easier to build general- purpose tree-manipulation routines that are not dependent on specific-document element types.

Attr

The Attr interface represents the value assigned to an attribute of an XML element. Since the attributes NamedNodeList attribute of the Element interface is the only access to Attr objects within the DOM, the parentNode, previousSibling, and nextSibling attributes always return null. Although the Attr interface inherits the Node base interface, many basic Node methods are not applicable.

An XML element can acquire an attribute in several ways. An element has an attribute value if:

  • The XML document explicitly provides an attribute value.

  • The document DTD specifies a default attribute value.

  • An attribute is added programmatically using the setAttribute( ) or setAttributeNode( ) methods of the Element interface.

Though an Attr node is not officially part of the DOM document tree, it can be the parent of a value subtree. An Attr object can have EntityReference objects as children. The value attribute provides the expanded DOMString representation of this attribute. To determine if any entity replacements were made, it is necessary to check the Attr node for child nodes.

//Get the element's size attribute as an Attr object
Attr attrName = elem.getAttributeNode("size");

Attributes

The following attributes are defined for the Attr object:

name: DOMString

The name of the attribute. Read-only.

Java binding

Public String getName( );

Java example

// Dump element attribute names
Attr attr;

for (int i = 0; i < elem.getAttributes().getLength( ); i++) {
    // temporarily alias the attribute
    attr = (Attr)elem.getAttributes( ).item(i);
    System.out.println(attr.getName( ));
    }
ownerElement: Element2

This property provides a link to the Element object that owns this attribute. If the attribute is currently unowned, it equals null. Read-only.

Java binding

public Element getOwnerElement( );
specified: boolean

This indicates whether this attribute was explicitly set in the XML source for the parent element or it is a default value specified in the DTD. Read-only.

Java binding

public boolean getSpecified( );

Java example

// Dump element attribute names
for (int i = 0; i < elem.getAttributes().getLength( ); i++) {
    // temporarily alias the attribute
    attr = (Attr)elem.getAttributes( ).item(i);
    // only show attributes that were explicitly included in the XML
    //source file
     // (i.e. ignore default attributes from the DTD.)
     if (attr.getSpecified( )) {
         System.out.println(attr.getName( ));
    }
}
value: DOMString

This attribute provides a simple way to set and retreive the Attr object's text value. When used to get the text value, the attribute includes the expanded value of any general entity references. When used to set the value, it creates a child Text node that contains the string value. Attempting to set the value on a read-only node raises the NO_MODIFICATION_ALLOWED_ERR DOM exception.

Java bindings

public String getValue( );
public void setValue(String value);

Java example

// Make all attribute values lowercase
Attr attr;

for (int i = 0; i < elem.getAttributes().getLength( ); i++) {
    attr = (Attr)elem.getAttributes( ).item(i);
    attr.setValue(attr.getValue().toLowerCase( ));
}
CDATASection

The CDATASection interface contains the unparsed, unescaped data contained within CDATA blocks in an XML document. Though this interface inherits the Text interface, adjacent CDATASection blocks are not merged by the normalize( ) method of the Element interface.

Java example

// Open an XML source file
try {
    FileInputStream fis = new FileInputStream("phone_list.xml");
    StringBuffer sb = new StringBuffer( );
    // read the XML source file into memory
    int ch;
    while ((ch = fis.read( )) != -1) {
        sb.append((char)ch);
    }

    // now, create a CDATASection object to contain it within
    // an element of our document using the CDATA facility
    CDATASection ndCDATA = doc.createCDATASection(sb.toString( ));
} catch (IOException e) {
    ...

CDATASection is a pure subclass of the Text interface and has no attributes or methods of its own. See the Text interface section of this chapter for a list of applicable methods for accessing character data in nodes of this type.

CharacterData

The CharacterData interface is completely abstract, extending the basic Node interface only to support manipulation of character data. Every DOM object type that deals with text data inherits, directly or indirectly, from this interface.

This interface's string-handling facilities are similar to those found in most modern programming languages. Like C/C++ string-processing routines, all CharacterData routines are zero-based.

Java example

// Create a new, unattached Text node
Text ndText = doc.createTextNode("The truth is out there.");
// cast it to the CharacterData interface
CharacterData ndCD = (CharacterData)ndText;

Attributes

The following attributes are defined for CharacterData:

data: DOMString

This attribute allows access to the "raw" data of the CharacterData node. Though a given DOM implementation cannot arbitrarily limit the amount of character data that can be stored in a CharacterData node, you may need to use the substringData method to retrieve the data in manageable sections because of implementation constraints.

Exceptions

NO_MODIFICATION_ALLOWED_ERR
Raised on a write attempt when the data attribute is read-only for this DOM object type.

DOMSTRING_SIZE_ERR
Raised if the read value that would be returned is too large to be contained by a DOMString type in the given implementation.

Java bindings

public String getData( ) throws DOMException;
public void setData(String data) throws DOMException;

Java example

// Quote the CharacterData node contents
CharacterData ndCD =
 (CharacterData)doc.createTextNode("Unquoted text.");
...
ndCD.setData('\"' + ndCD.getData( ) + '\"');
length: unsigned long

The size of the DOMString stored in the data attribute. For all methods of this interface that take an index parameter, the valid range for the index is <= index < length. This value can be 0, since having an empty CharacterData node is possible. Read-only.

Java binding

public long getLength( );

Java example

// Display the contents of a CharacterData node
CharacterData ndCD = (CharacterData)doc.createTextNode("This string has
 30 characters.");

System.out.println("The string \'" + ndCD.getData( ) + "\' has "
            + Long.toString(ndCD.getLength( )) + " characters.");
CharacterData (continued)

Methods

The following methods are defined for CharacterData:

appendData: arg

This method appends contents of the arg parameter to the current contents of the data attribute.

Argument

arg: DOMString
The string to append.

Exception

NO_MODIFICATION_ALLOWED_ERR
Raised if this node is read-only.

Java binding

public void appendData(String arg) throws DOMException;

Java example

// Append to an existing string
// Create a new Text object and reference the CharacterData interface

CharacterData ndCD = (CharacterData)doc.createTextNode("The truth is ");
// flip a coin

ndCD.appendData((Math.random( ) < 0.5) ? "out there." : "in here.");
System.out.println(ndCD.getData( ));
deleteData: offset, count

This truncates the DOMString in the data attribute. This method removes count characters, starting at the offset position.

Arguments

offset: unsigned long
The position in the data attribute to remove count characters.

count: unsigned long
The count of characters to remove. If the offset + count is >= the length attribute, the remainder, starting at position offset, is deleted.

Exceptions

INDEX_SIZE_ERR
Raised if the offset parameter is not a valid zero-based index into the data DOMString.

NO_MODIFICATION_ALLOWED_ERR
Raised if the node is read-only.

Java binding

public void deleteData(long offset, long count)
               throws DOMException;

Java example

// Create a new Text object and reference the CharacterData interface
CharacterData ndCD = (CharacterData)doc.createTextNode("The truth is
                      not out there.");

// change of heart
ndCD.deleteData(12, 4);

System.out.println(ndCD.getData( ));
insertData: offsec, arg

This method takes a string, splits the data attribute's current contents at the given offset, then inserts the string from the arg parameter between the two substrings.

Arguments

offset: unsigned long
The zero-based offset in the data attribute where the insertion is made.

arg: DOMString
The string to be inserted.

Exceptions

INDEX_SIZE_ERR
Raised if the offset parameter is not a valid, zero-based index into the data DOMString.

NO_MODIFICATION_ALLOWED_ERR
Raised if the node is read-only.

Java binding

public void insertData(long offset, String arg) throws
  DOMException;

Java example

// Insert data into a string
boolean fCynical = true;

// create a new Text object, and reference the CharacterData interface
CharacterData ndCD = (CharacterData)doc.createTextNode("The truth is
                      out there.");

...

// check for cynicism
if (fCynical) {
    ndCD.insertData(12, " not");
}

System.out.println(ndCD.getData( ));
replaceData: offset, count, arg

This replaces a substring within the data attribute with another string value arg, using the specifed offset and count parameters.

Arguments

offset: long
The offset of the beginning of the replacement region.

count: long
The number of characters to replace. If offset + count is >= the length attribute, everything beyond the offset character position is replaced.

arg: DOMString
The replacement string.

The replaceData operation is the equivalent of the following code fragment:

cdNode.deleteData(offset, count);
     cdNode.insertData(offset, arg);

Exceptions

INDEX_SIZE_ERR
Raised if the offset parameter is not a valid, zero-based index into the data DOMString.

NO_MODIFICATION_ALLOWED_ERR
Raised if the node is read-only.

Java binding

public void replaceData(long offset, long count,
                        String arg) throws DOMException;

Java example

 // Create a new Text object and reference the CharacterData interface
CharacterData ndCD = (CharacterData)doc.createTextNode("The truth is
                      not out there.");

// replace the truth
String strFind = "truth";
String strReplace = "dog";

ndCD.replaceData(ndCD.getData().indexOf(strFind), strFind.length( ),
                 strReplace);

System.out.println(ndCD.getData( ));
substringData: offset, count

This returns a DOMString that contains a subset of the string stored in the data attribute. The offset and count arguments define the substring. Though the offset argument must represent a valid position within the node data, the end-point of the substring could fall past the end of the data attribute. If this happens, the method returns everything between the offset position and the end of the data string.

Arguments

offset: unsigned long
Zero-based, starting offset of the substring to return. A valid offset must be >= 0 and < the length attribute of the node.

count: unsigned long
Count of characters to return.

Exceptions

INDEX_SIZE_ERR
Raised if the given offset is < 0, >= the length attribute, or if the count parameter is negative.

DOMSTRING_SIZE_ERR
Raised if the value that would be returned is too large to be contained by a DOMString type in the given implementation.

Java binding

public String substringData(unsigned long offset, unsigned long count)
                 throws DOMException;

Java example

// Get a reference to the CharacterData interface
CharacterData ndCD = (CharacterData)doc.createTextNode("The truth is
                      out there.");

// we only want the "truth"
String strTruth = ndCD.substringData(4, 5);

System.out.println("The substring is '" + strTruth + '\'');
Comment

This object contains the text of an XML comment (everything between the opening <!-- and closing -->). It inherits from CharacterData.

NOTE: The DOM specification does not require XML parsers to preserve the original document comments after the document is parsed. Some implementations strip comments as part of the parsing process.

Java example

// Create a comment
Comment ndComment = doc.createComment("Document was parsed by
                    DOM utility.");

// and add it to the document
doc.appendChild(ndComment);
Document

The Document interface represents an entire, well-formed XML document. Once the Document object is created via the DOMImplementation interface, you can access every aspect of the underlying XML document through the various tree-navigation methods exposed by the Node interface, the parent of the Document interface.

In DOM documents, document elements cannot exist outside of a parent document. For this reason, the Document interface exposes several factory methods used to create new document elements.

Attributes

The following attributes are defined for the Document object:

doctype: DocumentType

This attribute returns an instance of the DocumentType interface representing the DTD for this document. If no DTD was declared in the document, this property is null. Read-only.

Java binding

public DocumentType getDoctype( );

Java example

// Get the parsed DTD information for this document
DocumentType docType = docIn.getDoctype( );

if (docType == null) {
    System.out.println("warning: no DTD provided");
}
documentElement: Element

This attribute points to the single Element node that is the root of the XML document tree. Read-only.

Java binding

public Element getDocumentElement( );

// Identify the root element
Element elRoot = docIn.getDocumentElement( );
System.out.println("This is a '" + elRoot.getTagName( ) + "' document.");
implementation: DOMImplementation

This returns a reference to the DOMImplementation that is responsible for this document. It is conceivable (using Adobe's SVG plug-in within Microsoft's Internet Explorer, for example) that a single application might use DOM objects from multiple DOM implementations. Read-only.

Java binding

public DOMImplementation getImplementation( );

Java example

// Ensure the support of DOM Level 1 XML
DOMImplementation di = doc.getImplementation( );
if (!di.hasFeature("XML", "1.0")) {
    return false;
}
Document (continued)

Methods

The following methods are defined for the Document object:

createAttribute: name

This function creates an Attr object with the given name. Attr nodes construct complex element attributes that can include EntityReference objects and text data.

Argument

name: DOMString
The name of the XML attribute.

Return value

The new Attr object.

Exception

INVALID_CHARACTER_ERR
Indicates that the name you passed to createAttribute( ) doesn't conform to a valid XML name. See Chapter 2 for the XML restrictions on name construction.

Java binding

public Attr createAttribute(String name) throws DOMException;

Java example

// Create an entity reference
EntityReference er = doc.createEntityReference("name_entity");

// must create an Attribute object to include an explicit
// entity reference
Attr attr = doc.createAttribute("name");

// append the entity reference
attr.appendChild(er);
createAttributeNS: namespaceURI, qualifiedName2

This method serves the same purpose as the createAttribute method, but includes support for XML namespaces. See Chapter 4 for more information about namespaces.

Arguments

namespaceURI: DOMString
The URI associated with the namespace prefix in the qualifiedName parameter.

qualifiedName: DOMString
The name of the attribute to instantiate; includes the namespace prefix associated with the namespace URI given in the namespaceURI parameter.

Return value

The new Attr object is returned with the following attribute values:

Attribute

Value

Node.nodeName

The complete, fully qualified name given in the qualifiedName parameter

Node.namespaceURI

The given namespace URI

Node.prefix

The namespace prefix, which is parsed from the qualifiedName parameter

Node.localName

The local part of the qualified name, located to the right of the : character

Attr.name

The qualifiedName

Exceptions

INVALID_CHARACTER_ERR
Indicates that the name passed to createAttributeNS( ) doesn't conform to a valid XML name. See Chapter 2 for the XML restrictions on name construction.

NAMESPACE_ERR
Raised if the qualifiedName is malformed or has a prefix but no namespaceURI, or if the reserved xml namespace prefix was used incorrectly.

Java binding

public Attr createAttributeNS(String namespaceURI, String qualifiedName)
               throws DOMException;
createCDATASection: data

This creates a new CDATASection node that contains the data text. CDATASection nodes contain non-XML text content that would be inconvenient or impractical to quote using the standard XML entities, such as &amp;, &lt;, or &gt;.

Argument

data: DOMString
The text contained by the new CDATASection object.

Exception

NOT_SUPPORTED_ERR
Occurs if you try to call this method on an HTML document.

Java binding

public CDATASection createCDATASection(String data) throws DOMException;

Java example

// Use CDATASection to embed XML characters
CDATASection cds = doc.createCDATASection("
<xml_example>This is sample text.</xml_example>
");
createComment: data

This returns a new Comment node containing the specified string. See the Comment object reference earlier in this chapter for special restrictions that apply to the contents of Comment nodes.

Argument

data: DOMString
The comment text.

Comment text restriction

The XML specification indicates that the -- characters must not appear in the comment text for compatibility reasons. Despite this warning, some DOM implementations don't flag comments containing double hyphens as syntax errors.

Java binding

public Comment createComment(String data);

Java example

// Create a timestamp comment
StringBuffer sb = new StringBuffer( );
Date dtNow = new Date( );

sb.append("\tModified " + dtNow.toString( ) + '\n');

Comment cmt = doc.createComment(sb.toString( ));
createDocumentFragment( )

This returns an empty DocumentFragment object. See the DocumentFragment reference later in this chapter for a discussion of a document fragment's uses and limitations.

Java binding

public DocumentFragment createDocumentFragment( );
createElement: tagName

This creates a new, empty Element node for use within the parent document. The element name is given as an argument to the method. The resulting Element node belongs to the parent Document object, but is not part of the document element hierarchy. See Node later in this chapter for more information about how the document hierarchy manipulation methods are used.

Argument

tagName: DOMString
The XML name used to create the new Element node. This name is assigned to the nodeName attribute of the resulting Element node.

Return value

The new Element object.

Exception

INVALID_CHARACTER_ERR
Indicates that the name you passed to createElement( ) doesn't conform to a valid XML name. See Chapter 2 for the XML restrictions on name construction.

Java binding

public Element createElement(String tagName) throws DOMException;

Java example

// Create the new my_tag Element
Element elOut = doc.createElement("my_tag");
createElementNS: namespaceURI, qualifiedName2

This method serves the same purpose as the createElement method, but includes support for XML namespaces. See Chapter 4 for more information about namespaces.

Arguments

namespaceURI: DOMString
The URI associated with the namespace prefix in the qualifiedName parameter.

qualifiedName: DOMString
The name of the element to instantiate, including the namespace prefix associated with the namespace URI given in the namespaceURI parameter.

Return value

The new Element object is returned with the following attribute values:

Attribute

Value

Node.nodeName

The complete, fully qualified name given in the qualifiedName parameter

Node.namespaceURI

The given namespace URI

Node.prefix

The namespace prefix, which is parsed from the qualifiedName parameter

Node.localName

The local part of the qualified name, located to the right of the : character

Element.tagName

The full element tag name, which is the same as the qualifiedName

Exceptions

INVALID_CHARACTER_ERR
Indicates that the name you passed to createElementNS( ) doesn't conform to a valid XML name. See Chapter 2 for the XML restrictions on name construction.

NAMESPACE_ERR
Raised if the qualifiedName is malformed, has a prefix but no namespaceURI, or if the reserved xml namespace prefix was used incorrectly.

Java binding

public Element createElementNS(String namespaceURI,
                               String qualifiedName)
                  throws DOMException;
createEntityReference: name

This creates an EntityReference object.

Argument

name: DOMString
The name of the XML entity to be referenced. The name must match an XML entity declaration that is valid in the current document.

Exceptions

INVALID_CHARACTER_ERR
Indicates that the name you passed to createEntityReference( ) doesn't conform to a valid XML name. See Chapter 2 for the XML restrictions on name construction.

NOT_SUPPORTED_ERR
Generated if you attempted to create an entity reference using an HTML document.

Java binding

public EntityReference createEntityReference(String name)
                          throws DOMException;

Java example

// Create an entity reference
EntityReference er = doc.createEntityReference("name_entity");
createProcessingInstruction: target, data

This creates a new ProcessingInstruction node with the given target name and data values. The processing-instruction target name "xml" (case insensitive) is reserved by the XML working group and can't be used by an application.

Arguments

target: DOMString
The target name of the processing instruction. This name identifies the application that will interpret the data portion of the instruction.

data: DOMString
The application-specific data for the resulting ProcessingInstruction node.

Exceptions

INVALID_CHARACTER_ERR
Indicates that the name you passed in to createProcessing Instruction doesn't conform to a valid XML name. See Chapter 2 for the XML restrictions on name construction.

NOT_SUPPORTED_ERR
Generated if you attempt to create a ProcessingInstruction using an HTML document.

Java binding

public ProcessingInstruction createProcessingInstruction(String target,
                                String data) throws DOMException;

Java example

// Add the application-specific processing instruction
ProcessingInstruction pi = doc.createProcessingInstruction("my_app",
            "action=\"save\"");
createTextNode: data

This creates a new Text node that contains the given data string.

Argument

data: DOMString
The string that will be the contents of the new node.

Java binding

public Text createTextNode(String data);

Java example

// Create a new node that contains character data
Text txtDesc = doc.createTextNode("Character data contents for a new
                                  Element.");
getElementById: elementID2

This method returns the Element node with the given value for its ID attribute.

NOTE: It is important not to confuse attributes that have the name ID with ID attributes. ID attributes are attributes that were declared with the ID attribute type within the document type definition. See the Attribute List Declaration section in Chapter 20 for more information about ID attributes.

Argument

elementID: DOMString
The unique ID value for the desired element.

Return value

A single Element object that has the requested ID attribute or null, if no match is found.

Java binding

public Element getElementById(String elementId);
getElementsByTagName: tagName

This function returns a list of Element nodes from the current document whose tagName attribute matches the given tagName parameter. The nodes are returned in the same order in which they appear in the source document.

Argument

tagName: DOMString
The name of the tag to use as a filter. The special name * matches any tag.

Java binding

public NodeList getElementsByTagName(String tagName);

Java example

// Get a list of all phone numbers in the document
NodeList nl = doc.getElementsByTagName("phone_number");
getElementsByTagNameNS: namespaceURI, localName2

Like the getElementsByTagName( ) method, this method returns a list of Element nodes (a NodeList object) that match the criteria given in the namespaceURI and localName parameters. The resulting list contains all elements matching the namespace URI and local name restrictions, as they would be encountered in the original order of the document on which the tree was constructed.

Arguments

namespaceURI: DOMString
The namespace URI of the elements to be matched. The special * value matches any namespace.

localName: DOMString
The local name part of the elements to be matched. The special value * matches any local name.

Java binding

public NodeList getElementsByTagNameNS(String namespaceURI,
                                       String localName);
importNode: importedNode, deep2

This method's name is somewhat deceptive. It creates a copy of a Node object from another document that can be inserted within the current document's node hierarchy. Specifics of this copy operation vary, depending on the type of copied node.

Node type

Result

Effect of deep flag

ATTRIBUTE_NODE

Copies the source attribute and all its children. The ownerElement attribute is set to null, and the specified flag is set to true.

None.

DOCUMENT_FRAGMENT_NODE

Creates an empty DocumentFragment node.

Fully copies the children of the source DocumentFragment node.

DOCUMENT_NODE

Cannot be imported.

N/A.

DOCUMENT_TYPE_NODE

Cannot be imported.

N/A.

ELEMENT_NODE

Copies the attribute nodes with the specified flag set to the new element.

Recursively copies all the source element's children.

ENTITY_NODE

Copies the publicId, systemId, and notationName attributes.

Recursively copies all of the Entity node's children.

ENTITY_REFERENCE_NODE

Copies only the EntityReference node. Its value, if any, is taken from the DTD of the document doing the import.

None.

NOTATION_NODE

Imports the notation node, but since in Level 2 the DocumentType interface is read-only, it cannot be included in the target document.

None.

PROCESSING_INSTRUCTION_ NODE

Copies the target and data values.

None.

TEXT_NODE,
CDATA_SECTION_NODE, COMMENT_NODE

Copies the data and length attributes.

None.
The new (copied) node object is returned based on the arguments.

Arguments

importedNode: Node
The node duplicated for use in the current document hierarchy.

deep: boolean
Whether to copy the single node given or the entire subtree of its children. For details, see the previous table.

Exception

NOT_SUPPORTED_ERR
Thrown if an attempt is made to import an unsupported Node type, such as a Document node.

Java binding

public Node importNode(Node importedNode, boolean deep)
    throws DOMException;
DocumentFragment

The DocumentFragment is a lightweight container used to store XML document fragments temporarily. Since it has no properties or methods of its own, it can only provide the same functionality exposed by the Node object. It is intended to serve as a container for at least one well-formed XML subtree.

This object's most obvious application is in the case of clipboard or drag-and-drop operations in a visual editor. The user may elect to select several sub-trees that appear at the same level of the tree to be copied:

<xml_example>
    <caption><filename>sample.xml</filename> before DocumentFragment
     copy operation</caption>
    <document>
        <parent>
            <child_1></child_1>
            <child_2></child_2>
        </parent>
        <parent>
        </parent>
    </document>
</xml_example>

If the user decides to copy the two child nodes to the clipboard, the DOM application would:

  • Create a DocumentFragment object.

  • Attach copies of the child nodes to the new object using the cloneNode( ) and appendChild( ) methods.

    <xml_example>
    <caption>DocumentFragment object on clipboard.</caption>
    <DocumentFragment frame="dashed">
        <child_1></child_1>
        <child_2></child_2>
    </DocumentFragment>
</xml_example>

Then, when the user decides to paste the copied nodes to a new location, the new DocumentFragment node is passed to this target node's appendChild( ) method. During the copy operation, the DocumentFragment node itself is ignored, and only the children are attached to the target node.

<xml_example>
    <caption><filename>sample.xml</filename> after DocumentFragment copy
     operation</caption>
    <document>
        <parent>
            <child_1></child_1>
            <child_2></child_2>
        </parent>
        <parent>
            <child_1></child_1>
            <child_2></child_2>
        </parent>
    </document>
</xml_example>

Java example

// Create a Document Fragment object
DocumentFragment dfNorm = doc.createDocumentFragment( );
DocumentType

The Document interface includes a single attribute, docType, that points either to a description of the DTD for the current document or to null if none exists.

Java example

// get document type information
    DocumentType dtDoc = doc.getDoctype( );

Attributes

The DocumentType object contains the following attributes:

entities: NamedNodeMap

This attribute provides a list of all general entities declared in the document's DTD. If the same entity is declared more than once within a single document, only the first occurrence is preserved in this NamedNodeMap. Note that parameter entity declarations are not available through the DocumentType interface. Each member of this list implements the Entity interface. Read-only.

Java binding

public NamedNodeMap getEntities( );

Java example

// Dump the document entities
NamedNodeMap nnm = doc.getDoctype().getEntities( );

Entity ndEnt;
for (int i = 0; i < nnm.getLength( ); i++) {
    ndEnt = (Entity)nnm.item(i);

    System.out.println(ndEnt.getNodeName( ));

    if (ndEnt.getPublicId( ) != null) {
        System.out.println("\tPublic Identifier: " +
                           ndEnt.getPublicId( ));
    }

    if (ndEnt.getSystemId( ) != null) {
        System.out.println("\tSystem Identifier: " +
                           ndEnt.getSystemId( ));
    }

    if (ndEnt.getNotationName( ) != null) {
        System.out.println("\tNotation Name: " +
                           ndEnt.getNotationName( ));
    }
}
internalSubset: DOMString

This attribute contains the document's internal subset as a string value. The content's actual format depends on the level of support provided by a particular XML parser. Read-only.

Java binding

public String getInternalSubset( );
name: DOMString

This is the name of the DTD, which is the XML name following the XML DOCTYPE keyword in the source document. Read-only.

Java binding

public String getName( );

Java example

// Display document type information
DocumentType dtDoc = doc.getDoctype( );

System.out.println("This is a " + dtDoc.getName( ) + " document.");
notations: NamedNodeMap

A NamedNodeMap contains a list of XML notation declarations for the current document. Each member of this list implements the Notation interface, and the list itself is read-only.

Java binding

public NamedNodeMap getNotations( );

Java example

// Dump the document notations
NamedNodeMap nnm = doc.getDoctype().getNotations( );
Notation ndNotation;
for (int i = 0; i < nnm.getLength( ); i++) {
    ndNotation = (Notation)nnm.item(i);

    System.out.println(ndNotation.getNodeName( ));
    if (ndNotation.getPublicId( ) != null) {
        System.out.println("\tPublic Identifier: " +
                           ndNotation.getPublicId( ));
    }
    if (ndNotation.getSystemId( ) != null) {
        System.out.println("\tSystem Identifier: " +
                           ndNotation.getSystemId( ));
    }
}
publicId: DOMString

This is the public identifier of the external subset. Read-only.

Java binding

public String getPublicId( );
systemId: DOMString

The system identifier (URI) of this document's external subset. Read-only.

Java binding

public String getSystemId( );
DOMException

For languages and runtime platforms that support them, structured exceptions provide a way to separate the code that deals with abnormal or unexpected problems from the normal flow of execution. For languages that don't support exceptions, such as ECMAScript or Perl, these conditions are reported to your program as error codes from the method that recognized the condition.

The ExceptionCode is an integer value that indicates what type of exception was detected. The following ExceptionCodes are defined, with unused numeric codes reserved for future use by the W3C:

INDEX_SIZE_ERR [unsigned short, value: 1]

An index outside the expected range was passed to a method that accepts an index. The expected range for most collections is 0 <= index < collection. length.

Java binding

public static final short INDEX_SIZE_ERR = 1;
DOMSTRING_SIZE_ERR [unsigned short, value: 2]

The DOMString that would be returned from a method is too large.

Java binding

public static final short DOMSTRING_SIZE_ERR = 2;
HIERARCHY_REQUEST_ERR [unsigned short, value: 3 ]

The node insertion you requested violates the document structure's integrity. For example, the insertion would cause a node to become one of its own children.

Java binding

public static final short HIERARCHY_REQUEST_ERR = 3;
WRONG_DOCUMENT_ERR [unsigned short, value: 4 ]

An attempt to insert a node from one document directly into another. A given implementer of the DOM framework determines whether this insertion generates an error.

Java binding

public static final short WRONG_DOCUMENT_ERR = 4;
INVALID_CHARACTER_ERR [unsigned short, value: 5 ]

An invalid character was used in a name, e.g., trying to create an Element object with the name "my element", as spaces are not allowed.

Java binding

public static final short INVALID_CHARACTER_ERR = 5;
NO_DATA_ALLOWED_ERR [unsigned short, value: 6 ]

Data was assigned to a node that doesn't support data, like an Element node.

Java binding

public static final short NO_DATA_ALLOWED_ERR = 6;
NO_MODIFICATION_ALLOWED_ERR [unsigned short, value: 7 ]

An attempt was made to modify a node that cannot be modified.

Java binding

public static final short NO_MODIFICATION_ALLOWED_ERR = 7;
NOT_FOUND_ERR [unsigned short, value: 8 ]

A node was modified in a context in which it could not be found.

Java binding

public static final short NOT_FOUND_ERR = 8;
NOT_SUPPORTED_ERR [unsigned short, value: 9 ]

If in the specific implementation of the DOM you chose not to implement an optional feature, this exception would be thrown.

Java binding

public static final short NOT_SUPPORTED_ERR = 9;
INUSE_ATTRIBUTE_ERR [unsigned short, value: 10 ]

An attempt was made to add an attribute that was already in use elsewhere. This error could occur if you acquired an attribute via the getAttributeNode( ) method and tried to add the same object instance to another element using the setAttributeNode( ) method. You would first need to create a new Attr object, probably using the cloneNode( ) method.

Java binding

public static final short INUSE_ATTRIBUTE_ERR = 10;
INVALID_STATE_ERR [unsigned short, value: 11]2

An attempt was made to use an object that is no longer usable.

Java binding

public static final short INVALID_STATE_ERR = 11;
SYNTAX_ERR [unsigned short, value: 12]2

An invalid or illegal string was specified.

Java binding

public static final short SYNTAX_ERR = 12;
INVALID_MODIFICATION_ERR [unsigned short, value: 13]2

An attempt was made to change the type's underlying object.

Java binding

public static final short INVALID_MODIFICATION_ERR = 13;
NAMESPACE_ERR [unsigned short, value: 14]2

An attempt was made to use a method that supports XML namespaces in a way that would violate namespace rules. This error could occur if a qualified name were given to a method without a corresponding namespace URI.

Java binding

public static final short NAMESPACE_ERR = 14;
INVALID_ACCESS_ERR [unsigned short, value: 15]2

The underlying object does not support a parameter or operation.

Java binding

public static final short INVALID_ACCESS_ERR = 15;
DOMImplementation

The DOMImplementation interface provides global information about the DOM implementation you currently use. The only way to obtain a reference to the DOMImplementation interface is through the getImplementation( ) method of the Document object.

Java example

// Check for DOM Level 1 support
DOMImplementation di = doc.getImplementation( );
// make sure that DOM Level 1 XML is supported
if (!di.hasFeature("XML", "1.0")) {
    return null;
}

Methods

The DOMImplementation object defines the following methods:

createDocument: namespaceURI, qualifiedName, doctype2

Creates a new, empty Document object with the given document type. It also creates the single, top-level document element using the given qualified name and namespace URI.

Arguments

namespaceURI: DOMString
The namespace URI used to create the top-level document element. Can be null if no namespace is used.

qualifiedName: DOMString
The namespace-aware qualified name of the top-level document element to be created. The prefix given in this parameter is associated with the namespace URI given in the namespaceURI parameter.

doctype: DOMString
The document type definition object to be associated with the new document. If this parameter is not null, the DocumentType node's ownerDocument attribute is set to point to the new document object.

Exceptions

INVALID_CHARACTER_ERR
Indicates that the qualifiedName parameter has a malformed XML identifier.

NAMESPACE_ERR
Raised if an inconsistency exists between the values given for the namespaceURI and the qualifiedName parameters. Passing in a qualified name with a namespace prefix and not passing in a namespace URI is illegal. This can also be generated if a reserved namespace prefix, such as "xml", is given with an incorrect namespace URI.

WRONG_DOCUMENT_ERR
Raised if the DocumentType node passed in the doctype parameter is already associated with another document object. New DocumentType objects must be created using the new createDocumentType method of the DOMImplementation interface.

Java binding

public Document createDocument(String namespaceURI,
    String qualifiedName, DocumentType doctype) throws DOMException;
createDocumentType: qualifiedName, publicId, systemId2

Creates an empty DocumentType node that is not associated with any document. No entity declarations or notations are available in this new, empty DocumentType object. No support currently exists in the DOM to populate this object.

Arguments

qualifiedName: DOMString
The qualified name of the document type to be created.

publicId: DOMString
The external subset's public identifier.

systemId: DOMString
The system identifier (URI) of the external subset to be created.

Return value

A new DocumentType object with the ownerDocument attribute set to null.

Exceptions

INVALID_CHARACTER_ERR
Indicates that the qualifiedName parameter has a malformed XML identifier.

NAMESPACE_ERR
Raised if the qualified name is malformed.

Java binding

public DocumentType createDocumentType(String qualifiedName,
         String publicId, String systemId) throws DOMException;
hasFeature: feature, version

Tests to see if the DOM implementation supports a given named feature package. It returns true if the particular version of the specified feature is available; otherwise, returns false.

Arguments

feature: DOMString
The package name of the feature to test. The following feature names (and others listed at http://www.w3.org/TR/DOM-Level-2-Core/introduction.html-ID-Conformance) are valid:

XML
Supports DOM Level 1.0 or 2.0 Core objects.

HTML
Supports DOM Level 1.0 or 2.0 HTML objects.

version: DOMString
Represents the DOM version level of the specified feature to test. If no versionnull number is specified, the function returns true if any version is supported.

Java binding

public boolean hasFeature(String feature, String version);

Java example

// Make sure that DOM Level 1 XML is supported
if (!di.hasFeature("XML", "1.0")) {
    return null;
}
NOTE: The HTML-specific DOM objects are beyond the scope of this book, but they are extremely useful tools for building applications that perform transformations on HTML documents. An excellent reference to the HTML DOM objects can be found in the book Dynamic HTML: The Definitive Reference, by Danny Goodman (O'Reilly & Associates).

Element

The Element object type provides access to the XML document's structure and data. Every XML element is translated into a single Element node. The document's root element is accessible through the documentElement property of the Document object. From this node, it is possible to re-create the full structure of the original XML document by traversing the element tree.

Java example

// Get the XML document's root element
Element elem = doc.getDocumentElement( );

This interface extends the basic Node interface to allow access to the XML attributes of the document element. Two sets of methods allow access to attribute values, either as Attr object trees or as simple DOMStrings.

Attribute

The Element object defines one attribute that contains the XML tag name:

tagName: DOMString

The XML tag name from the original document.

Java binding

public String getTagName( );

// Show the name of the root element tag
Element elem = doc.getDocumentElement( );
System.out.println("This is a " + elem.getTagName( ) + " document.");
Element (continued)

Methods

The following methods are defined for this object:

getAttribute: name

Returns the attribute specified by the name parameter as a DOMString. See the getAttributeNode:name for a complete explanation of how an attribute value is determined. This returns an empty string if no attribute is set and if no default attribute value was specified in the DTD.

Java binding

public String getAttribute(String name);

Java example

// Check for the name attribute
Element elem = doc.getDocumentElement( );

if (elem.getAttribute("name") == "") {
    System.out.println("warning: " + elem.getTagName( ) +
                   " element: no name attribute");
}
getAttributeNS: namespaceURI, localName2

Returns an attribute as a DOMString, based on the namespace and local part of the qualified name.

Arguments

namespaceURI: DOMString
The namespace URI of the attribute to return.

localName: DOMString
The local name portion of the qualified attribute name to return.

Return value

Returns an empty string if no attribute is set and if no default attribute value was specified in the DTD.

Java binding

public String getAttributeNS(String namespaceURI, String localName);
getAttributeNode: name

Retrieves the Attr for the name attribute. Returns a reference to the attribute object if it is found; otherwise, null.

Arguments

name: DOMString
Name of the attribute to retrieve.

Java binding

public Attr getAttributeNode(String name);

Java example

// Use the id attribute
Attr attr;

if ((attr = elem.getAttributeNode("id")) == null) {
    System.out.println("warning: element " + elem.getTagName( ) +
                       ": no id attribute provided.");
}
getAttributeNodeNS: namespaceURI, localName2

Retrieves the Attr object for the attribute specified by the given namespace URI and local name. Returns a reference to the attribute object if it is found; otherwise returns null.

Arguments

namespaceURI: DOMString
Namespace URI of the target attribute.

localName: DOMString
Local name of the target attribute. The local name is the part of the name to the right of the : in a qualified name.

Java binding

public Attr getAttributeNodeNS(String namespaceURI, String localName);
getElementsByTagName: name

Returns a NodeList of all descendant Element nodes whose tagName attribute matches the given name parameter. The nodes are returned in the same order in which they would be encountered in a preorder traversal of the document tree. A preorder traversal conforms to the order in which the XML elements appear in the source document.

Argument

name: DOMString
The name of the tag to use as a filter. The special name * matches any tag.

Java binding

public NodeList getElementsByTagName(String name);

Java example

// Find every address element in the document
Element elem = doc.getDocumentElement( );
NodeList nlAddrs = elem.getElementsByTagName("address");
getElementsByTagNameNS: namespaceURI, localName2

Like the getElementsByTagName method, returns a list of Element nodes, descendants of the Element node on which the method is called, that match the criteria given in the namespaceURI and localName parameters. The resulting list contains all elements matching the namespace URI and local name restrictions, as they would be encountered in a preorder traversal of the document tree.

Arguments

namespaceURI: DOMString
The namespace URI of elements to be matched. The special * value matches any namespace.

localName: DOMString
The local name part of elements to be matched. The special value * matches any local name.

Java binding

public NodeList getElementsByTagNameNS(String namespaceURI,
                                       String localName);
hasAttribute: name2

Returns true if an attribute with the given name has been set or has a default value. Returns false if the attribute isn't defined.

Argument

name: DOMString
The name of the attribute to be identified.

Java binding

public boolean hasAttribute(String name);
hasAttributeNS: namespaceURI, localName2

Returns true if an attribute with the given namespaceURI and localName has been set or has a default value. Returns false if the attribute isn't defined.

Arguments

namespaceURI: DOMString
The namespace URI of the attribute to be identified.

localName: DOMString
The local name of the attribute to be identified.

Java binding

public boolean hasAttribute(String namespaceURI, String localName);
normalize

Traverses the subtree of the current Element, combining adjacent Text nodes into a single node.

NOTE: This method was moved to the Node interface as part of the DOM Level 2 specification. It is still accessible from the Element interface, as it inherits from the Node interface.

Java binding

public void normalize( );

Java example

// Merge all adjacent text nodes below this element
elem.normalize( );
removeAttribute: name

Removes the named element attribute from this element's attributes collection. If the attribute to be removed has a default value declared in the DTD, subsequent attempts to retrieve the attribute value return the default value.

Argument

name: DOMString
Name of the attribute to remove.

Exception

NO_MODIFICATION_ALLOWED_ERR
Raised if the element is read-only.

Java binding

public void removeAttribute(String name) throws DOMException;

Java example

// Remove the unique ID
...
elem.removeAttribute("id");
...
removeAttributeNS: namespaceURI, localName2

Uses the given namespace URI and local name parameters to remove the desired attribute from the element's attributes collection.

Arguments

namespaceURI: DOMString
Namespace URI of the target attribute.

localName: DOMString
Local name part of the target attribute. The local name is the part to the right of the final : in a qualified name.

Exception

NO_MODIFICATION_ALLOWED_ERR
Raised if the element is read-only.

Java binding

public void removeAttributeNS(String namespaceURI, String localName)
               throws DOMException;
removeAttributeNode: oldAttr

Removes the referenced attribute node from this element's attributes collection. If the attribute to be removed has a default value declared in the DTD, subsequent attempts to retrieve the attribute value return the default value.

Argument

oldAttr: Attr
The attribute node to remove.

Exceptions

NO_MODIFICATION_ALLOWED_ERR
Raised if the node is read-only.

NOT_FOUND_ERR
Raised if no attribute name matching the oldAttr parameter is found in the map.

Java binding

public Attr removeAttributeNode(Attr oldAttr) throws DOMException;

Java example

// Find and remove temporary attributes
Attr attr;

if ((attr = elem.getAttributeNode("temp")) != null) {
    // remove it
    elem.removeAttributeNode(attr);
}
setAttribute: name, value

Sets the attribute specified by the name parameter to the DOMString passed in the value argument. The string is not parsed for entity references and is set as a Text node child of the corresponding member of the attributes collection. If an attribute with the given name already exists, the value is set to the value argument.

Arguments

name: DOMString
The attribute name to set or modify.

value: DOMString
The new attribute value.

Exceptions

INVALID_CHARACTER_ERR
Indicates that the attribute name you passed in doesn't represent a valid XML attribute name.

NO_MODIFICATION_ALLOWED_ERR
Raised if the element is read-only.

Java binding

public void setAttribute(String name, String value) throws DOMException;

Java example

// Check for the name attribute
if (elem.getAttribute("name") == "") {
    // oh well, set a reasonable default
    elem.setAttribute("name", elem.getTagName( ));
}
setAttributeNS: namespaceURI, qualifiedName, value2

This method is the namespace-enabled version of the basic setAttribute method. The namespace URI and the qualified name update the attributes collection of the element in question.

Arguments

namespaceURI: DOMString
The namespace URI of the attribute value to set.

qualifiedName: DOMString
The qualified name (including namespace prefix) of the new value to set.

value: DOMString
The new attribute value.

Exceptions

INVALID_CHARACTER_ERR
Indicates that the attribute name you passed in doesn't represent a valid XML attribute name.

NO_MODIFICATION_ALLOWED_ERR
Raised if the element is read-only.

NAMESPACE_ERR
Raised if the namespaceURI and qualifiedName parameters would violate rules concerning namespaces. If the qualified name includes a prefix, the namespace URI cannot be null or an empty string. If the reserved xml or xmlns prefixes are used, the namespace URI must match the corresponding specified system URI. See Chapter 4 for more information about namespaces and prefixes.

Java binding

public void setAttributeNS(String namespaceURI, String qualifiedName,
                           String value) throws DOMException;
setAttributeNode: newAttr

Sets or replaces the attribute in the Node interface's attributes collection with the given Attr object. The attribute name is retrieved from the name attribute of the new attribute object. If an Attr object with the given name already exists in the attributes collection, this method returns a reference to the old Attr object. Otherwise, it returns null.

Argument

newAttr: Attr
The new Attr object to set.

Exceptions

WRONG_DOCUMENT_ERR
Raised if the newAttr node was created in a document different than the parent node.

NO_MODIFICATION_ALLOWED_ERR
Raised if the new parent node is read-only.

INUSE_ATTRIBUTE_ERR
Raised if another Element already uses the new Attr node. Each element must have a distinct Attr object.

Java binding

public Attr setAttributeNode(Attr newAttr) throws DOMException;

Java example

// Make sure you have an id attribute to work with
Attr attr;

if ((attr = elem.getAttributeNode("id")) == null) {
    // add a default, unique id
    attr = doc.createAttribute("id");

    elem.setAttributeNode(attr);

    // continue processing
}
setAttributeNodeNS: newAttr2

Sets or replaces the attribute in the element's attributes collection that matches the namespace URI and the given Attr object's local name. This operation is identical to the setAttributeNode method, except that it considers namespace differences between attributes. If an Attr object with the given name in the attributes collection already exists, this method returns a reference to the old Attr object; otherwise, it returns null.

Argument

newAttr: Attr
The new Attr object to set.

Exceptions

WRONG_DOCUMENT_ERR
Raised if the newAttr node was created in a different document than the parent node.

NO_MODIFICATION_ALLOWED_ERR
Raised if the new parent node is read-only.

INUSE_ATTRIBUTE_ERR
Raised if another Element already uses the newAttr node. Each element must have a unique Attr object.

Java binding

public Attr setAttributeNodeNS(Attr newAttr) throws DOMException;
Entity

The Entity object represents a given general XML entity's replacement value. Depending on whether a given DOM implementation is validating or nonvalidating and whether it chooses to expand entity references inline during parsing, Entity objects may not be available to the DOM user.

Java example

// Locate the my_entity entity declaration
Entity ndEnt = (Entity)doc.getDoctype().getEntities( ).
    getNamedItem("my_entity");

Attributes

The following read-only attributes are defined for the Entity object:

notationName:

If this entity is unparsed, the entity's notation name. For parsed entities, this attribute is null.

Java binding

public String getNotationName( );

Java example

// Find out if it's a parsed entity
boolean fParsedEnt = ndEnt.getNotationName( ) == null;
publicId: DOMString

The public identifier URL (URI) given for this entity, or null if none was specified.

Java binding

public String getPublicId( );

Java example

// Locate the my_entity entity declaration
Entity ndEnt = (Entity)doc.getDoctype().getEntities( ).getNamedItem("my_
entity");

// if my_entity type was found and there is a public-ID (URL)...
if (ndEnt != null && ndEnt.getPublicId( ) != null) {
    try {
        // ...get the URL protocol
        URL urlSys = new URL(ndEnt.getPublicId( ));
        System.out.println("Entity " + ndEnt.getNodeName( ) +
                           ": protocol " + urlSys.getProtocol( ));
    } catch (MalformedURLException e) {
    }
}
systemId: DOMString

The system identifier URL (URI) given for this entity, or null if none was specified.

Java binding

public String getSystemId( );

Java example

// Get the Public ID or System ID for this entity
Entity ndEnt = (Entity)doc.getDoctype().getEntities( ).getNamedItem("my_ entity");

String strURL = ndEnt.getPublicId( );

// if can't find the public URL
if (strURL == null) {
    // find the system URL
    strURL = ndEnt.getSystemId( );
}
EntityReference

EntityReference nodes appear within the document hierarchy wherever an XML general entity reference is embedded within the source document. Depending on the DOM implementation, a corresponding Entity object may exist in the entities collection of the docType attribute of the Document object. If such an entity exists, then the child nodes of both the Entity and EntityReference represent the replacement text associated with the given entity.

Java example

// Create a new entity reference
EntityReference ndER = doc.createEntityReference("my_entity");
NamedNodeMap

The NamedNodeMap interface provides a mechanism used to retrieve Node objects from a collection by name. Though this interface exposes the same methods and attributes as the NodeList class, they are not related. While it is possible to enumerate the nodes in a NamedNodeMap using the item( ) method and length attribute, the nodes are not guaranteed to be in any particular order.

Java example

// Get an element's attributes
NamedNodeMap nnm = elem.getAttributes( );

Attribute

The NamedNodeMap defines one attribute:

length: unsigned long

The total number of Node objects in the list.

Java binding

public long getLength( );

Java example

// Iterate over the attribute list
for (int i = 0; i < nnm.getLength( ); i++) {
    ...
}
NamedNodeMap (continued)

Methods

The following methods are defined for the NamedNodeMap object:

getNamedItem: name

Returns a reference to the node with the given nodeName property specified by name.

Argument

name: DOMString
Name of the node to retrieve.

Java binding

public Node getNamedItem(String name);

Java example

// Check to see if an ID attribute exists
// in this map, and add it if necessary
// nnm was created by getting the attributes
// from an element
if (nnm.getNamedItem("id") == null) {
    // get the document
    Document doc = elem.getOwnerDocument( );
    // create a new attribute Node
    Attr attrID = doc.createAttribute("id");

    // set the attribute value
    attrID.appendChild(doc.createTextNode(makeUniqueID(elem)));
    // ... and add it to the NamedNodeMap
    nnm.setNamedItem(attrID);
}
getNamedItemNS: namespaceURI, localName2

Extends the basic getNamedItem method to include support for namespaces. Instead of finding an item in the list based only on the local part of the node name, it is possible to incorporate the namespace URI into the search.

Arguments

namespaceURI: DOMString
Namespace URI of the node to retrieve.

localName: DOMString
Local name of the node to retrieve.

Java binding

public Node getNamedItemNS(String namespaceURI, String localName);
item: index

Returns a reference to the Node object at position index. If the given index is < 0 or >= the length attribute of the NodeList, this function returns null.

Argument

index: unsigned long
Zero-based index of the list of the node to return.

Java binding

public Node item(long index);

Java example

// Remove the last attribute from the list
if (nnm.getLength( ) > 0) {
    nnm.removeNamedItem(nnm.item(nnm.getLength()-1).getNodeName( ));
}
removeNamedItem: name

Removes the Node object with the nodeName property that matches the name parameter and returns a reference to the removed object. If the node you plan to remove is an Attr node and if it has a defined default value, the node will be replaced immediately with a new Node object set to the default value.

Argument

name: DOMString
The nodeName value of the node to be removed.

Exception

NOT_FOUND_ERR
Raised if no node matching the name parameter is found in the map.

Java binding

public Node removeNamedItem(String name) throws DOMException;

Java example

// Remove the ID node attribute
NamedNodeMap nnm = elem.getAttributes( );

if (nnm.removeNamedItem("id") == null) {
    System.err.println("no ID attribute found");
}
removeNamedItemNS: namespaceURI, localName2

Removes the Node object with the matching namespaceURI and localName properties and returns a reference to the removed object. If the node you plan to remove is an Attr node and if it has a defined default value, a new Node object set to the default value will replace the node immediately.

Arguments

namespaceURI: DOMString
Namespace URI of the node to retrieve.

localName: DOMString
Local name of the node to retrieve.

Exception

NOT_FOUND_ERR
Raised if no node matching the namespaceURI and localName parameter is found in the map.

Java binding

public Node removeNamedItemNS(String namespaceURI, String localName);
setNamedItem: arg

Inserts the given Node object into the list, using its nodeName attribute. Since many DOM node types expose the same, hardcoded value for this property, storing only one of them in a single NamedNodeMap is possible. Each subsequent insertion overwrites the previous node entry. See the nodeName: DOMString topic for a discussion of these special name values.

This method returns a reference to the Node object that the new node replaces. If no nodes with the same nodeName value are currently in the map, this method returns null.

Argument

arg: Node
The Node object to be stored in the map. The value of the nodeName property is used as the lookup key. A node with the same nodeName value as the new node is replaced with the node referenced by arg.

Exceptions

WRONG_DOCUMENT_ERR
Raised if a document different than the creator of the target NamedNodeMap created the arg node.

NO_MODIFICATION_ALLOWED_ERR
Raised if the NamedNodeMap is read-only.

INUSE_ATTRIBUTE_ERR
Raised if the arg node is an Attr node that is already in use by another element's attributes map.

Java binding

public Node setNamedItem(Node arg) throws DOMException;

Java example

// Check to see if an ID attribute exists
// in this map, and add it if necessary
if (nnm.getNamedItem("id") == null) {
    // get the document
    Document doc = elem.getOwnerDocument( );
    // create a new attribute Node
    Attr attrID = doc.createAttribute("id");

    // set the attribute value
    attrID.appendChild(doc.createTextNode(makeUniqueID(elem)));

    // ... and add it to the NamedNodeMap
    nnm.setNamedItem(attrID);
}
setNamedItemNS: arg2

Identical in function to the basic setNamedItem method, except that it considers namespace properties in the Node object. A reference to the replaced Node object is returned.

Argument

arg: Node
The Node object to be stored in the map. The values of the namespaceURI and localName properties are used as the lookup key. If another node with identical values for these two properties exists, the new node replaces it.

Exceptions

WRONG_DOCUMENT_ERR
Raised if a document different than the creator of the target NamedNodeMap created the arg node.

NO_MODIFICATION_ALLOWED_ERR
Raised if the NamedNodeMap is read-only.

INUSE_ATTRIBUTE_ERR
Raised if the arg node is an Attr node already in use by another element's attributes map.

Java binding

public Node setNamedItemNS(Node arg) throws DOMException;
Node

The Node interface is the base interface for every member of a DOM document tree. It exposes attributes common to every type of document object and provides simple methods to retrieve type-specific information without resorting to downcasting. For instance, the attributes list provides access to the Element object's attributes, but it would have no meaning for a ProcessingInstruction node. (Extracting pseudoattributes from a processing instruction requires your application to parse the contents of the processing instruction.)

This interface also exposes all methods for querying, inserting, and removing objects from the document hierarchy. The Node interface makes it easier to build general-purpose tree-manipulation routines that are not dependent on specific document element types.

Attributes

The following attributes provide information about where the Node object is located within the document tree. These attributes are read-only. Additional methods allow the insertion and removal of nodes from the document tree.

attributes: NamedNodeMap

Has meaning only for Element objects. It provides access to a list of Attr objects in a NamedNodeMap. For all other object types, it returns null.

Java binding

public NamedNodeMap getAttributes( );

// List the attributes of an Element node
NamedNodeMap nnm = doc.getDocumentElement().getAttributes( );

if (nnm != null) {

    for (int i = 0; i < nnm.getLength( ); i++) {
        // print the attribute and value
        System.out.println(nnm.item(i).getNodeName( ) + " = \"" +
                           nnm.item(i).getNodeValue( ) + "\"");
    }
}
childNodes: NodeList

Returns a NodeList containing a reference to every child of this Node.

Java binding

public NodeList getChildNodes( );

Java example

// List the text contents of an element
NodeList nlChildren = elem.getChildNodes( );
Node ndChild;

for (int iNode = 0; iNode < nlChildren.getLength( ); iNode++) {
    ndChild = nlChildren.item(iNode);

    if (ndChild.getNodeType( ) == Node.TEXT_NODE) {
        System.out.println(ndChild.getNodeValue( ));
    }
}

Dynamic Tree References

Throughout the DOM, several places return lists or collections of nodes that represent the current state of the document tree. These references are all live; any modifications to the document hierarchy, made by inserting or removing nodes, are reflected in the list immediately.

Whether due to multithreading or unforeseen side effects of procedure calls, the contents of the list being used could change. To reduce the likelihood of difficult-to-find bugs resulting from stale values, request values (such as the length of a list) directly from the NodeList or NamedNodeMap objects. This option is safer than storing values in intermediate variables.

firstChild: Node

Points to the head of the linked list of children of this node. If no child nodes exist, it returns null.

Java binding

public Node getFirstChild( );

Java example

// List the contents of a node
for (Node nd = ndDump.getFirstChild( ); nd != null;
     nd = nd.getNextSibling( )) {
    if (nd.getNodeValue( ) != null) {
        System.out.println(nd.getNodeValue( ));
    }
}
lastChild: Node

Returns a pointer to the end of a given Node object's linked list of child nodes. If no children exist, it returns null.

Java binding

public Node getLastChild( );

Java example

// List the value of a node in reverse order
for (Node nd = ndDump.getLastChild( ); nd != null;
     nd = nd.getPreviousSibling( )) {
    if (nd.getNodeValue( ) != null) {
        System.out.println(nd.getNodeValue( ));
    }
}
localName: DOMString2

Returns the local part of the fully qualified node name. This part of the name is to the right of the final : in a namespace-qualified name.

Java binding

public String getLocalName( );
namespaceURI: DOMString2

Represents the namespace URI given to this Node object at creation time; returns null if no namespace was given. The value is null if the node's been created by a create NodeType( ) method rather than a create NodeTypeNS( ) method.

Java binding

public String getNamespaceURI( );
nextSibling: Node

Returns the next node in the sibling list. If this node is the end of the list, nextSibling returns null.

Java binding

public Node getNextSibling( );

Java example

// List the contents of a node
for (Node nd = ndDump.getFirstChild( ); nd != null;
     nd = nd.getNextSibling( )) {
    if (nd.getNodeValue( ) != null) {
        System.out.println(nd.getNodeValue( ));
    }
}
nodeName: DOMString

Intended to represent the underlying DOM object's name. Depending on the object type, this attribute may map to another attribute of the object or a constant string:

Object type

nodeName

Element

Tag name

Attr

Attribute name

Text

"#text"

CDATASection

"#cdata-section"

EntityReference

Name of entity referenced

Entity

Entity name

ProcessingInstruction

Target

Comment

"#comment"

Document

"#document"

DocumentType

Document type name

DocumentFragment

"#document-fragment"

Notation

Notation name

Java binding

public String getNodeName( );

// Print the document root tag name
Node ndDoc = (Node)doc.getDocumentElement( );
System.out.println("Document root element type: " + ndDoc.getNodeName( ));
nodeType: unsigned short

Contains a value that indicates the true type of the object referenced through the Node interface. The following table shows this attribute's possible values, along with the actual object types they represent:

Constant name

Object type

Constant value

ELEMENT_NODE

Element

1

ATTRIBUTE_NODE

Attr

2

TEXT_NODE

Text

3

CDATA_SECTION_NODE

CDATASection

4

ENTITY_REFERENCE_NODE

EntityReference

5

ENTITY_NODE

Entity

6

PROCESSING_INSTRUCTION_NODE

ProcessingInstruction

7

COMMENT_NODE

Comment

8

DOCUMENT_NODE

Document

9

DOCUMENT_TYPE_NODE

DocumentType

10

DOCUMENT_FRAGMENT_NODE

DocumentFragment

11

NOTATION_NODE

Notation

12
The parent-child and sibling relationships between nodes can be visualized as two doubly linked lists. One list links parents to children, while the other links nodes that exist at the same level.

Java binding

public short getNodeType( );

Java example

// Check to see if a node is an Element type node
public boolean isElement(Node nd) {
    return nd.getNodeType( ) == Node.ELEMENT_NODE;
}
nodeValue: DOMString

Intended to provide a reasonable string value for the underlying DOM object. Depending on the nodeType, this property may be read-only, read/ write, or null.

Object type

nodeValue

Element

null

Attr

Attribute value

Text

Text node content

CDATASection

CDATA section content

EntityReference

null

Entity

null

ProcessingInstruction

Entire content, excluding the target

Comment

Comment content

Document

null

DocumentType

null

DocumentFragment

null

Notation

null

Exceptions

NO_MODIFICATION_ALLOWED_ERR
Indicates the nodeValue attribute is read-only for this DOM object type.

DOMSTRING_SIZE_ERR
This exception is raised if the value that would be returned is too large to be contained by a DOMString type in the given implementation.

Java bindings

public String getNodeValue( ) throws DOMException;
public void setNodeValue(String nodeValue) throws DOMException;

Java example

// If this node is a text node, make the value lowercase
if (nd.getNodeType( ) == Node.TEXT_NODE) {
    // make it lowercase
    nd.setNodeValue(nd.getNodeValue().toLowerCase( ));
}
ownerDocument: Document

Returns a reference to the Document used to create this Node object. Since the Document object is the only mechanism exposed for creating new nodes, even these newly created, empty nodes have the ownerDocument property set. This attribute can be null only for Document nodes and DocumentType nodes that are not yet part of a document. You can't move a node directly to another document; instead you must import it. This property can be useful for checking where a node came from.

Java binding

public Document getOwnerDocument( );

Java example

// Add my two cents
Document doc = elem.getOwnerDocument( );
Text txtAdd = doc.createTextNode("My $.02");
elem.appendChild(txtAdd);
parentNode: Node

Provides a reference to the parent of this node. All node types, except Document, DocumentFragment, and Attr, may have a parent node. Every node within a Document hierarchy has a parent. Nodes that are not part of the document tree, such as new nodes and nodes removed from the document using the replaceChild( ) or removeChild( ) methods, have a parentNode attribute of null.

Java binding

Node getParentNode( );

Java example

// Unlink an element from the document tree
elem.getParentNode( ).removeChild(elem);
prefix: DOMString2

Represents the namespace prefix of this node, used for nodes that support namespace prefixes. For ELEMENT_NODE and ATTRIBUTE_NODE type nodes, changing the namespace prefix also affects the nodeName, tagName, and name attributes. Since these properties hold the qualified name of the node, changing the prefix also updates it.

Exceptions

INVALID_CHARACTER_ERR
Raised if the prefix includes an illegal character.

NO_MODIFICATION_ALLOWED_ERR
Indicates that the prefix attribute is read-only for this DOM object type.

NAMESPACE_ERR
Raised if the prefix is malformed, according to the rules of namespace identifier formation. This exception is also raised if the namespaceURI attribute is null, or if an attempt was made to violate the XML rules of identifier formation. Such an attempt includes invalid use of the xml or xmlns identifier. For more information about namespaces, see Chapter 4.

Java bindings

public String getPrefix( );
public void setPrefix(String prefix) throws DOMException;
previousSibling: Node

Returns the preceding node in the sibling list. If this node is the head of the sibling list, it returns null.

Java binding

public Node getPreviousSibling( );

Java example

// List the value of a node in reverse order
for (Node nd = ndDump.getLastChild( ); nd != null;
     nd = nd.getPreviousSibling( )) {
    if (nd.getNodeValue( ) != null) {
        System.out.println(nd.getNodeValue( ));
    }
}
Node (continued)

Methods

The following methods are defined for Node interface objects:

appendChild: newchild

Appends the newchild node to the end of the child list. If newchild is already linked into the document tree, it is unlinked before the append is performed. This method returns a reference to the newchild node.

Argument

newchild: Node
The node to append. If the node is a DocumentFragment node, the children of newchild are appended in sequence to the end of the node's child list.

Exceptions

HIERARCHY_REQUEST_ERR
Raised if the insert operation violates at least one document structure rule. For instance, the node doesn't allow children or doesn't allow children of the newchild node type. This exception is also raised if the operation creates a circular reference (i.e., it tries to insert a node's parent as a node's child).

WRONG_DOCUMENT_ERR
Raised if the newchild node is created in a different document than that of the new parent node.

NO_MODIFICATION_ALLOWED_ERR
Raised if the new parent node is read-only.

Java binding

public Node appendChild(Node newChild) throws DOMException;

Java example

// Move the first child to the end of the child node list
if (elem.getFirstChild( ) != null) {
    elem.appendChild( elem.getFirstChild( ) );
}
cloneNode: deep

Returns a copy of the node without a parent node. If the cloned node is specified as deep (true), the subtree under the node is also copied. Otherwise, the cloned node does not contain child nodes.

Argument

deep: boolean
If true, child nodes are copied to the cloned node. If false, only the original node is copied.

Java binding

public Node cloneNode(boolean deep);

Java example

// Make a copy of this element and all children
elem.cloneNode(true);
hasAttributes ( )

Indicates whether an Element node has any attributes. Returns true if the node has attributes; otherwise, it returns false.

Java binding

public boolean hasAttributes( );
hasChildNodes ( )

Provides a quick way to determine if a node has children. Returns true if the node has any children; otherwise, it returns false.

Java binding

public boolean hasChildNodes( );
insertBefore: newchild, refchild

Inserts the Node object newchild into the child list of the parent node that invokes it. The refchild parameter allows you to specify where to insert the new node in the list. If refchild is null, the new node is inserted at the end of the child list. (This behavior is the same as appendChild.) If it is not null, the new node is inserted into the list in front of the specified node. If the newchild node is already part of the document tree, it is unlinked before it is inserted in its new position. Also, if the newchild node references a DocumentFragment object, each of its children are inserted, in order, before the refchild node. A reference to the newchild node is returned.

Arguments

newchild: Node
The new node to insert.

refchild: Node
The node that follows the new node in the child list, or null, if the new node is inserted at the end of the child list.

Exceptions

HIERARCHY_REQUEST_ERR
Raised if the insert operation would violate at least one document structure rule. For instance, the node doesn't allow children or doesn't allow children of the newchild node type. This exception is also raised if the operation creates a circular reference (i.e., it tries to insert a node's parent as a node's child).

WRONG_DOCUMENT_ERR
Raised if the newchild node was created in a document different than that of the new parent node.

NO_MODIFICATION_ALLOWED_ERR
Raised if the new parent node is read-only.

NOT_FOUND_ERR
Raised if the node pointed to by refchild is not a child of the node performing the insert.

Java binding

public Node insertBefore(Node newChild, Node refChild)
               throws DOMException;

Java example

// Insert a new node at the head of the child list of a parent node
ndParent.insertBefore(ndNew, ndParent.getFirstChild( ));
isSupported: feature, version2

Checks to see if a particular DOM feature is available for this implementation. For more information about the feature names, see the hasFeature: feature, version method of the DOMImplementation object earlier in this chapter. This method returns true if the feature is available, false if it is not.

Arguments

feature: DOMString
The name of the feature to test for. See detail of the hasFeature: feature, version method of the DOMImplementation object for a list of this parameter's valid values.

version: DOMString
The version number of the feature to test. For DOM Level 2, Version 1, this string should be 2.0. If the version is not specified, this method tests for any version of the feature.

Java binding

public boolean supports(String feature, String version);
normalize ( ) 2

Recursively combines all adjacent Text nodes into a single node. It also removes empty Text nodes from the document tree. This operation is useful for operations that require absolute references within a document or if two documents must be compared.

Java binding

public void normalize( );
removeChild: oldchild

Unlinks the oldchild node from the child list of a given node and returns a reference to the now detached Node object.

Argument

oldchild: Node
The node to be removed.

Exceptions

NO_MODIFICATION_ALLOWED_ERR
Raised if the parent node is read-only.

NOT_FOUND_ERR
Raised if the oldchild node is not a child of this node.

Java binding

public Node removeChild(Node oldChild) throws DOMException;

Java example

// Unlink an element and all its children
// from the document tree
elem.getParentNode( ).removeChild(elem);
replaceChild: newchild, oldchild

Replaces the child node oldchild with newchild. If newchild is currently linked into the document tree, it is removed before the replace is performed. The method returns a reference to the oldchild node.

Arguments

newchild: Node
The node to be inserted.

oldchild: Node
The node being replaced.

Exceptions

HIERARCHY_REQUEST_ERR
Raised if the insert operation violates at least one document structure rule. For instance, the node doesn't allow children or doesn't allow children of the newchild node type. This exception is also raised if the operation creates a circular reference (i.e., it tries to insert a node's parent as a node's child).

WRONG_DOCUMENT_ERR
Raised if the newchild node was created in a different document than the new parent node.

NO_MODIFICATION_ALLOWED_ERR
Raised if the new parent node is read-only.

NOT_FOUND_ERR
Raised if the node pointed to by oldchild is not a child of the node performing the replacement.

Java binding

public Node replaceChild(Node newChild, Node oldChild)
               throws DOMException;

Java example

// Replace an old node with a new one
ndOld.getParentNode( ).replaceChild(ndNew, ndOld);
NodeList

The NodeList interface allows DOM classes to expose an ordered collection of nodes. A NodeList represents a read-only, zero-based array of Node objects. Since no mechanism exists for creating, adding, or removing nodes from a NodeList, DOM users cannot use this class as a general-purpose utility class.

Java example

// List the text contents of an element
NodeList nlChildren = elem.getChildNodes( );
Node ndChild;

for (int iNode = 0; iNode < nlChildren.getLength( ); iNode++) {
    ndChild = nlChildren.item(iNode);

    if (ndChild.getNodeType( ) == Node.TEXT_NODE) {
        System.out.println(ndChild.getNodeValue( ));
    }
}

Attributes

The NodeList interface defines one attribute:

length: unsigned long

The total number of Node objects in the list.

Java binding

public long getLength( );
NodeList (continued)

Methods

The NodeList interface defines one method:

item:

Returns a reference to the Node object at position index or returns null if the index is invalid. If the index given is < 0 or >= the length attribute of the NodeList, this function returns null.

Argument

index: unsigned long
Zero-based index into the list of the Node to return.

Java binding

public Node item(long index);
ProcessingInstruction

This interface provides access to the contents of an XML processing instruction. Processing instructions provide a mechanism for embedding commands to an XML processing application that is in line with the XML content.

Java example

// Add an application-specific processing instruction
ProcessingInstruction pi = doc.createProcessingInstruction("my_app",
        "action=\"save\"");

Attributes

The interface defines two attributes:

data: DOMString

Returns the data portion of this processing instruction. The data portion is identified starting at the first nonwhitespace character after the target token and ending at the closing ?>.

Write exception

NO_MODIFICATION_ALLOWED_ERR
Raised if the node is read-only.

Java bindings

public String getData( );
public void setData(String data) throws DOMException;

Java example

// Check the application's data attribute
if (pi.getTarget( ) == "MY_APPLICATION") {
    // check the data attribute for my own application-specific info
    if (pi.getData( ) == "CHECK_SIBLINGS") {
        // check the siblings
        ...
    }

    pi.setData("SIBLINGS_CHECKED");
}
target: DOMString

Returns the target portion of this processing instruction. The target is the first whitespace-delimited token within the processing-instruction block.

Processing instructions are meant to embed application-specific instructions for automatic content generation, parsing, etc., within the XML stream. The instruction's target portion is the flag that allows different processing applications to coexist. Applications that use processing instructions for formatting should ignore processing instructions they do not recognize.

Java binding

public String getTarget( );

// Check to see if your application is targeted
if (pi.getTarget( ) == "MY_APPLICATION") {
    // do my application-specific processing here
}
Text

Text nodes contain the nonmarkup character data contained within the XML document. After the XML document is parsed, exactly one Text node exists for each uninterrupted block of nonmarkup text:

<text_node>This is text.</text_node>

Method

The following method is defined for the Text interface:

splitText: offset

Splits a Text node into two adjacent Text nodes. The contents of the original node are divided at the given split offset, with the second substring used as the new node's value. The first substring remains in the original node. If the node is currently linked into the DOM tree, the new node with the split content becomes the next sibling of the original node. A new Text node containing the second part of the split data is returned.

Argument

offset (unsigned long)
Zero-based offset where the split occurs.

Exceptions

INDEX_SIZE_ERR
Raised if the offset given is < 0 and >= the length attribute.

NO_MODIFICATION_ALLOWED_ERR
Raised if the element is read-only.

Java binding

public Text splitText(long offset) throws DOMException;

Java example

// Make one Text node
long = doc.createTextNode("This text is split.");

// and split it
Text ndSplit = ndText.splitText(9);
NOTE: Text is a subclass of the CharacterData interface. See the CharacterData interface section in this chapter for a list of applicable methods for accessing character data in nodes of this type.

24.1. Object Hierarchy

The following table shows the DOM object hierarchy:

Object

Permitted child objects

Document

Element (one is the maximum)

ProcessingInstruction

Comment

DocumentType (one is the maximum)

DocumentFragment

Element
ProcessingInstruction
Comment
Text
CDATASection
EntityReference

DocumentType

None (leaf node)

EntityReference

Element
ProcessingInstruction
Comment
Text
CDATASection
EntityReference

Element

Element
Text
Comment
ProcessingInstruction
CDATASection
EntityReference

Attr

Text
EntityReference

ProcessingInstruction

None (leaf node)

Comment

None (leaf node)

Text

None (leaf node)

CDATASection

None (leaf node)

Entity

Element
ProcessingInstruction
Comment
Text
CDATASection
EntityReference

Notation

None (leaf node)

Previous | Home | Next
23.4. TrAX | Book Index | 24.2. Object Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Object Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

24.2. Object Reference

This section details the XML DOM Level 2 Core objects. The reference sections detail the descriptions, attributes, and methods of each object in the language- independent IDL specification. Java examples and bindings are presented to illustrate usage.

Previous | Home | Next
24. DOM Reference | Book Index | 25. SAX Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX Reference (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

Chapter 25. SAX Reference

Contents:

The org.xml.sax Package
The org.xml.sax.helpers Package
SAX Features and Properties
The org.xml.sax.ext Package

SAX, the Simple API for XML, is a straightforward, event-based API used to parse XML documents. David Megginson, SAX's original author, placed SAX in the public domain. SAX is bundled with all parsers that implement the API, including Xerces, MSXML, Crimson, the Oracle XML Parser for Java, and Ælfred. However, you can also get it and the full source code from http://sax.sourceforge.net/.

SAX was originally defined as a Java API and is intended primarily for parsers written in Java, so this chapter will focus on its Java implementation. However, its port to other object-oriented languages, such as C++, Python, Perl, and Eiffel, is common and usually quite similar.

TIP: This chapter covers SAX2 exclusively. In 2002, all major parsers that support SAX support SAX2. The major change from SAX1 to SAX2 was the addition of namespace support. This addition necessitated changing the names and signatures of almost every method and class in SAX. The old SAX1 methods and classes are still available, but they're now deprecated and shouldn't be used.

25.1. The org.xml.sax Package

The org.xml.sax package contains the core interfaces and classes that comprise the Simple API for XML.

The Attributes Interface

An object that implements the Attributes interface represents a list of attributes on a start-tag. The order of attributes in the list is not guaranteed to match the order in the document itself. Attributes objects are passed as arguments to the startElement( ) method of ContentHandler. You can access particular attributes in three ways:

  • By number

  • By namespace URI and local name

  • By qualified (prefixed) name

This list does not include namespace declaration attributes (xmlns and xmlns:prefix) unless the http://xml.org/sax/features/namespace-prefixes feature is true. It is false by default.

If the namespace-prefixes feature is false, qualified name access may not be available; if the http://xml.org/sax/features/namespaces feature is false, local names and namespace URIs may not be available:

package org.xml.sax;

public interface Attributes {

  public int    getLength( );
  public String getURI(int index);
  public String getLocalName(int index);
  public String getQName(int index);
  public int    getIndex(String uri, String localName);
  public int    getIndex(String qualifiedName);
  public String getType(int index);
  public String getType(String uri, String localName);
  public String getType(String qualifiedName);
  public String getValue(String uri, String localName);
  public String getValue(String qualifiedName);
  public String getValue(int index);

}
The ContentHandler Interface

ContentHandler is the key piece of SAX. Almost every SAX program needs to use this interface. ContentHandler is a callback interface. An instance of this interface is passed to the parser via the setContentHandler( ) method of XMLReader. As the parser reads the document, it invokes the methods in its ContentHandler to tell the program what's in the document:

package org.xml.sax;

public interface ContentHandler {

  public void setDocumentLocator(Locator locator);
  public void startDocument( ) throws SAXException;
  public void endDocument( ) throws SAXException;
  public void startPrefixMapping(String prefix, String uri)
   throws SAXException;
  public void endPrefixMapping(String prefix) throws SAXException;
  public void startElement(String namespaceURI, String localName,
   String qualifiedName, Attributes atts) throws SAXException;
  public void endElement(String namespaceURI, String localName,
   String qualifiedName) throws SAXException;
  public void characters(char[] text, int start, int length)
   throws SAXException;
  public void ignorableWhitespace(char[] text, int start, int length)
   throws SAXException;
  public void processingInstruction(String target, String data)
   throws SAXException;
  public void skippedEntity(String name) throws SAXException;

}
The DTDHandler Interface

By passing an instance of the DTDHandler interface to the setDTDHandler( ) method of XMLReader, you can receive notification of notation and unparsed entity declarations in the DTD. You can store this information and use it later to retrieve information about the unparsed entities you encounter while reading the document:

package org.xml.sax;

public interface DTDHandler {

  public void notationDecl(String name, String publicID, String systemID)
   throws SAXException;
  public void unparsedEntityDecl(String name, String publicID,
   String systemID, String notationName) throws SAXException;

}
The EntityResolver Interface

By passing an instance of the EntityResolver interface to the setEntityResolver( ) method of XMLReader, you can intercept parser requests for external entities, such as the external DTD subset or external parameter entities, and redirect those requests in order to substitute different entities. For example, you could replace a reference to a remote copy of a standard DTD with a local one or find the sources for particular public IDs in a catalog. The interface is also useful for applications that use URI types other than URLs:

package org.xml.sax;

public interface EntityResolver {

  public InputSource resolveEntity(String publicID, String systemID)
   throws SAXException, IOException;

}
The ErrorHandler Interface

By passing an instance of the ErrorHandler interface to the setErrorHandler( ) method of XMLReader, you can provide custom handling for particular classes of errors detected by the parser. For example, you can choose whether to stop parsing when a validity error is detected. The SAXParseException passed to each of the three methods in this interface provides details about the specific cause and location of the error:

package org.xml.sax;

public interface ErrorHandler {

  public void warning(SAXParseException exception) throws SAXException;
  public void error(SAXParseException exception) throws SAXException;
  public void fatalError(SAXParseException exception)
   throws SAXException;

}

Warnings represent possible problems noticed by the parser that are not technically violations of XML's well-formedness or validity rules. For instance, a parser might issue a warning if an xml:lang attribute's value was not a legal ISO-639 language code. The most common kind of error is a validity problem. The parser should report it, but it should also continue processing. A fatal error violates well-formedness. The parser should not continue parsing after reporting such an error.

The Locator Interface

Unlike most other interfaces in the org.xml.sax package, the Locator interface does not have to be implemented. Instead, the parser has the option to provide an implementation. If it does so, it passes its implementation to the setDocumentLocator( ) method in your ContentHandler instance before it calls startDocument( ). You can save a reference to this object in a field in your ContentHandler class, like this:

private Locator locator;

public void setDocumentLocator(Locator locator) {
  this.locator = locator;
}

Once you've found the locator, you can then use it inside any other ContentHandler method, such as startElement( ) or characters( ), to determine in exactly which document and at which line and column the event took place. For instance, the locator allows you to determine that a particular start-tag began on the third column of the document's seventeenth line at the URL http://www.slashdot.org/slashdot.xml:

package org.xml.sax;

public interface Locator {

  public String getPublicId( );
  public String getSystemId( );
  public int    getLineNumber( );
  public int    getColumnNumber( );

}
The XMLFilter Interface

An XMLFilter is an XMLReader that obtains its events from another parent XMLReader, rather than reading it from a text source such as InputStream. Filters can sit between the original source XML and the application and modify data in the original source before passing it to the application. Implementing this interface directly is unusual. It is almost always much easier to use the more complete org.xml.sax.helpers.XMLFilterImpl class instead.

package org.xml.sax;

public interface XMLFilter extends XMLReader {

  public void      setParent(XMLReader parent);
  public XMLReader getParent( );

}
The XMLReader Interface

The XMLReader interface represents the XML parser that reads XML documents. You generally do not implement this interface yourself. Instead, use the org.xml.sax.helpers.XMLReaderFactory class to build a parser-specific implementation. Then use this parser's various setter methods to configure the parsing process. Finally, invoke the parse( ) method to read the document, while calling back to methods in your own implementations of ContentHandler, ErrorHandler, EntityResolver, and DTDHandler as the document is read:

package org.xml.sax;

public interface XMLReader {

  public boolean getFeature(String name)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public void    setFeature(String name, boolean value)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public Object  getProperty(String name)
   throws SAXNotRecognizedException, SAXNotSupportedException;

  public void    setProperty(String name, Object value)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public void           setEntityResolver(EntityResolver resolver);
  public EntityResolver getEntityResolver( );
  public void           setDTDHandler(DTDHandler handler);
  public DTDHandler     getDTDHandler( );
  public void           setContentHandler(ContentHandler handler);
  public ContentHandler getContentHandler( );
  public void           setErrorHandler(ErrorHandler handler);
  public ErrorHandler   getErrorHandler( );

  public void parse(InputSource input) throws IOException, SAXException;
  public void parse(String systemID) throws IOException, SAXException;

}
The InputSource Class

The InputSource class is an abstraction of a data source from which the raw bytes of an XML document are read. It can wrap a system ID, a public ID, an InputStream, or a Reader. When given an InputSource, the parser tries to read from the Reader. If the InputSource does not have a Reader, the parser will try to read from the InputStream using the specified encoding. If no encoding is specified, then it will try to autodetect the encoding by reading the XML declaration. Finally, if neither a Reader nor an InputStream has been set, then the parser will open a connection to the URL given by the system ID.

package org.xml.sax;

public class InputSource {

    public InputSource( );
    public InputSource(String systemID);
    public InputSource(InputStream byteStream);
    public InputSource(Reader reader);

    public void        setPublicId(String publicID);
    public String      getPublicId( );
    public void        setSystemId(String systemID);
    public String      getSystemId( );
    public void        setByteStream(InputStream byteStream);
    public InputStream getByteStream( );
    public void        setEncoding(String encoding);
    public String      getEncoding( );
    public void        setCharacterStream(Reader reader);
    public Reader      getCharacterStream( );

}
The SAXExceptions Class

Most exceptions thrown by SAX methods are instances of the SAXException class or one of its subclasses. The single exception to this rule is the parse( ) method of XMLReader, which may throw a raw IOException if a purely I/O-related error occurs, for example, if a socket is broken before the parser finishes reading the document from the network.

Besides the usual exception methods, such as getMessage( ) and printStackTrace( ), that SAXException inherits from or overrides in its superclasses, SAXException adds a getException( ) method to return the nested exception that caused the SAXException to be thrown in the first place:

package org.xml.sax;

public class SAXException extends Exception {

    public SAXException(String message);
    public SAXException(Exception ex);
    public SAXException(String message, Exception ex);

    public String    getMessage( );
    public Exception getException( );
    public String    toString( );

}
SAXParseException

If the parser detects a well-formedness error while reading a document, it throws a SAXParseException, a subclass of SAXException. SAXParseException s are also passed as arguments to the methods of the ErrorHandler interface, where you can decide whether you want to throw them.

Besides the methods it inherits from its superclasses, this class adds methods to get the line number, column number, system ID, and public ID of the document where the error was detected:

package org.xml.sax;

public class SAXParseException extends SAXException {

  public SAXParseException(String message, Locator locator);
  public SAXParseException(String message, Locator locator,
   Exception e);
  public SAXParseException(String message, String publicID,
   String systemID, int lineNumber, int columnNumber);
  public SAXParseException(String message, String publicID,
   String systemID, int lineNumber, int columnNumber, Exception e);

  public String getPublicId( );
  public String getSystemId( );
  public int    getLineNumber( );
  public int    getColumnNumber( );

}
SAXNotRecognizedException

A SAXNotRecognizedException is thrown if you attempt to set a property or feature the parser does not recognize. Besides the constructors, all its methods are inherited from superclasses:

package org.xml.sax;

public class SAXNotRecognizedException extends SAXException {

  public SAXNotRecognizedException( );
  public SAXNotRecognizedException(String message);

}
SAXNotSupportedException

A SAXNotSupportedException is thrown if you attempt to set a property or feature that the parser recognizes, but either cannot set or get now or cannot set the value to which you want to set it. Besides the constructors, all of its methods are inherited from superclasses:

package org.xml.sax;

public class SAXNotSupportedException extends SAXException {

  public SAXNotSupportedException( );
  public SAXNotSupportedException(String message);

}
Previous | Home | Next
24.2. Object Reference | Book Index | 25.2. The org.xml.sax.helpers Package
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The org.xml.sax.helpers Package (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

25.2. The org.xml.sax.helpers Package

The org.xml.sax.helpers package contains support classes for the core SAX classes. These classes include factory classes used to build instances of particular org.xml.sax interfaces and default implementations of those interfaces.

The AttributesImpl Class

AttributesImpl is a default implementation of the Attributes interface that SAX parsers and filters may use. Besides the methods of the Attributes interface, this class offers manipulator methods so the list of attributes can be modified or reused. These methods allow you to take a persistent snapshot of an Attributes object in startElement( ) and construct or modify an Attributes object in a SAX driver or filter:

package org.xml.sax.helpers;

public class AttributesImpl implements Attributes {

    public AttributesImpl( );
    public AttributesImpl(Attributes atts);

    public int    getLength( );
    public String getURI(int index);
    public String getLocalName(int index);
    public String getQName(int index);
    public String getType(int index);
    public String getValue(int index);
    public int    getIndex(String uri, String localName);
    public int    getIndex(String qualifiedName);
    public String getType(String uri, String localName);
    public String getType(String qualifiedName);
    public String getValue(String uri, String localName);
    public String getValue(String qualifiedName);
    public void   clear( );
    public void   setAttributes(Attributes atts);
    public void   addAttribute(String uri, String localName,
     String qualifiedName, String type, String value);
    public void   setAttribute(int index, String uri, String localName,
     String qualifiedName, String type, String value);
    public void   removeAttribute(int index)
    public void   setURI(int index, String uri)
    public void   setLocalName(int index, String localName)
    public void   setQName(int index, String qualifiedName);
    public void   setType(int index, String type);
    public void   setValue(int index, String value);

}
The DefaultHandler Class

DefaultHandler is a convenience class that implements the EntityResolver, DTDHandler, ContentHandler, and ErrorHandler interfaces with do-nothing methods. You can subclass DefaultHandler and override methods for events to which you actually want to respond. You never have to use this class. You can always implement the interfaces directly instead. The pattern is similar to the adapter classes in the AWT, such as MouseAdapter and WindowAdapter:

package org.xml.sax.helpers;

public class DefaultHandler
 implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler {

  // Default implementation of the EntityResolver interface.
  public InputSource resolveEntity(String publicID, String systemID)
   throws SAXException {
    return null;
  }

  // Default implementation of the DTDHandler interface.
  public void notationDecl(String name, String publicID, String systemID)
   throws SAXException {}
  public void unparsedEntityDecl(String name, String publicID,
   String systemID, String notationName) throws SAXException{}

  // Default implementation of the ContentHandler interface.
  public void setDocumentLocator(Locator locator) {}
  public void startDocument( ) throws SAXException {}
  public void endDocument( ) throws SAXException {}
  public void startPrefixMapping(String prefix, String uri)
   throws SAXException {}
  public void endPrefixMapping(String prefix) throws SAXException {}
  public void startElement(String uri, String localName,
   String qualifiedName, Attributes attributes) throws SAXException {}
  public void endElement(String uri, String localName,
   String qualifiedName) throws SAXException {}
  public void characters(char[] text, int start, int length)
   throws SAXException {}
  public void ignorableWhitespace(char[] whitespace, int start,
   int length) throws SAXException {}
  public void processingInstruction(String target, String data)
   throws SAXException {}
  public void skippedEntity(String name) throws SAXException {}

  // Default implementation of the ErrorHandler interface.
  public void warning(SAXParseException ex) throws SAXException {}
  public void error(SAXParseException ex) throws SAXException {}
  public void fatalError(SAXParseException ex) throws SAXException {
    throw ex;
  }

}
The LocatorImpl Class

LocatorImpl is a default implementation of the Locator interface for the convenience of parser writers. You probably won't need to use it directly. Besides the constructors, it adds setter methods to set the public ID, system ID, line number, and column number returned by the getter methods declared in Locator:

package org.xml.sax.helpers;

public class LocatorImpl implements Locator {

    public LocatorImpl( );
    public LocatorImpl(Locator locator);

    public String getPublicId( );
    public String getSystemId( );
    public int    getLineNumber( );
    public int    getColumnNumber( );
    public void   setPublicId(String publicID);
    public void   setSystemId(String systemID);
    public void   setLineNumber(int lineNumber);
    public void   setColumnNumber(int columnNumber);

}
The NamespaceSupport Class

NamespaceSupport provides a stack that can track the namespaces in scope at various points in the document. To use it, push a new context at the beginning of each element's namespace mappings, and place it at the end of each element. Each startPrefixMapping( ) invocation should call declarePrefix( ) to add a new mapping to the NamespaceSupport object. Then at any point where you need to figure out to which URI a prefix is bound, you can call getPrefix( ). The empty string indicates the default namespace. The getter methods can then tell you the prefix that is mapped to any URI or the URI that is mapped to any prefix at each point in the document. If you reuse the same NamespaceSupport object for multiple documents, be sure to call reset( ) in between documents.

package org.xml.sax.helpers;

public class NamespaceSupport {

 public final static String XMLNS="http://www.w3.org/XML/1998/namespace";

  public NamespaceSupport( );

  public void        reset( );
  public void        pushContext( );
  public void        popContext( );
  public boolean     declarePrefix(String prefix, String uri);
  public String[]    processName(String qualifiedName, String[] parts,
   boolean isAttribute);
  public String      getURI(String prefix);
  public Enumeration getPrefixes( );
  public String      getPrefix(String uri);
  public Enumeration getPrefixes(String uri);
  public Enumeration getDeclaredPrefixes( );

}
The ParserAdapter Class

The ParserAdapter class uses the adapter design pattern to convert a SAX1 org.xml.sax.Parser object into a SAX2 org.xml.sax.XMLReader object. As more parsers support SAX2, this class becomes less necessary. Note that some SAX2 features are not available through an adapted SAX1 parser. For instance, a parser created with this adapter does not report skipped entities and does not support most features and properties, not even the core features and properties:

package org.xml.sax.helpers;

public class ParserAdapter implements XMLReader, DocumentHandler {

  public ParserAdapter( ) throws SAXException;
  public ParserAdapter(Parser parser);

  // Implementation of org.xml.sax.XMLReader.
  public void           setFeature(String name, boolean state)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public boolean        getFeature(String name)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public void           setProperty(String name, Object value)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public Object         getProperty(String name)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public void           setEntityResolver(EntityResolver resolver);
  public EntityResolver getEntityResolver( );
  public void           setDTDHandler(DTDHandler handler);
  public DTDHandler     getDTDHandler( );
  public void           setContentHandler(ContentHandler handler);
  public ContentHandler getContentHandler( );
  public void           setErrorHandler(ErrorHandler handler);
  public ErrorHandler   getErrorHandler( );
  public void parse(String systemID) throws IOException, SAXException;
  public void parse(InputSource input) throws IOException, SAXException;

  // Implementation of org.xml.sax.DocumentHandler.
  public void setDocumentLocator(Locator locator);
  public void startDocument( ) throws SAXException;
  public void endDocument( ) throws SAXException;
  public void startElement(String qualifiedName,
   AttributeList qualifiedAttributes) throws SAXException;
  public void endElement(String qualifiedName) throws SAXException;
  public void characters(char[] text, int start, int length)
   throws SAXException;
  public void ignorableWhitespace(char[] text, int start, int length)
   throws SAXException;
  public void processingInstruction(String target, String data)
   throws SAXException;

}
The XMLFilterImpl Class

XMLFilterImpl is invaluable for implementing XML filters correctly. An instance of this class sits between an XMLReader and the client application's event handlers. It receives messages from the reader and passes them to the application unchanged, and vice versa. However, by subclassing this class and overriding particular methods, you can change the events that are sent before the application gets to see them. You chain a filter to an XMLReader by passing the reader as an argument to the filter's constructor. When parsing, you invoke the filter's parse( ) method, not the reader's parse( ) method.

package org.xml.sax.helpers;

public class XMLFilterImpl implements XMLFilter, EntityResolver,
 DTDHandler, ContentHandler, ErrorHandler {

  public XMLFilterImpl( );
  public XMLFilterImpl(XMLReader parent);

  // Implementation of org.xml.sax.XMLFilter
  public void      setParent(XMLReader parent);
  public XMLReader getParent( );

  // Implementation of org.xml.sax.XMLReader
  public void           setFeature(String name, boolean state)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public boolean        getFeature(String name)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public void           setProperty(String name, Object value)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public Object         getProperty(String name)
   throws SAXNotRecognizedException, SAXNotSupportedException;
  public void           setEntityResolver(EntityResolver resolver);
  public EntityResolver getEntityResolver( );
  public void           setDTDHandler(DTDHandler handler);
  public DTDHandler     getDTDHandler( );
  public void           setContentHandler(ContentHandler handler);
  public ContentHandler getContentHandler( );
  public void           setErrorHandler(ErrorHandler handler);
  public ErrorHandler   getErrorHandler( );
  public void parse(InputSource input) throws SAXException, IOException;
  public void parse(String systemID) throws SAXException, IOException

  // Implementation of org.xml.sax.EntityResolver
  public InputSource resolveEntity(String publicID, String systemID)
   throws SAXException, IOException;

  // Implementation of org.xml.sax.DTDHandler
  public void notationDecl(String name, String publicID, String systemID)
   throws SAXException;
  public void unparsedEntityDecl(String name, String publicID,
   String systemID, String notationName) throws SAXException;

  // Implementation of org.xml.sax.ContentHandler
  public void setDocumentLocator(Locator locator);
  public void startDocument( ) throws SAXException;
  public void endDocument( ) throws SAXException;
  public void startPrefixMapping(String prefix, String uri)
   throws SAXException;
  public void endPrefixMapping(String prefix) throws SAXException;
  public void startElement(String namespaceURI, String localName,
   String qualifiedName, Attributes atts) throws SAXException;
  public void endElement(String namespaceURI, String localName,
   String qualifiedName) throws SAXException;
  public void characters(char[] text, int start, int length)
   throws SAXException;
  public void ignorableWhitespace(char[] text, int start, int length)
   throws SAXException;
  public void processingInstruction(String target, String data)
   throws SAXException;
  public void skippedEntity(String name) throws SAXException;

  // Implementation of org.xml.sax.ErrorHandler
  public void warning(SAXParseException ex) throws SAXException;
  public void error(SAXParseException ex) throws SAXException;
  public void fatalError(SAXParseException ex) throws SAXException;

}
The XMLReaderAdapter Class

XMLReaderAdapter is the reverse of ParserAdapter; it uses the Adapter design pattern to adapt a SAX2 XMLReader to a SAX1 Parser. This lets you use SAX2 parsers for legacy programs written to a SAX1 interface:

package org.xml.sax.helpers;

public class XMLReaderAdapter implements Parser, ContentHandler {

  public XMLReaderAdapter( ) throws SAXException;
  public XMLReaderAdapter(XMLReader reader);

  // Implementation of org.xml.sax.Parser.
  public void setLocale(Locale locale) throws SAXException;
  public void setEntityResolver(EntityResolver resolver);
  public void setDTDHandler(DTDHandler handler);
  public void setDocumentHandler(DocumentHandler handler);
  public void setErrorHandler(ErrorHandler handler);
  public void parse(String systemID) throws IOException, SAXException;
  public void parse(InputSource input) throws IOException, SAXException

  // Implementation of org.xml.sax.ContentHandler.
  public void setDocumentLocator(Locator locator);
  public void startDocument( ) throws SAXException;
  public void endDocument( ) throws SAXException;
  public void startPrefixMapping(String prefix, String uri)
   throws SAXException;
  public void endPrefixMapping(String prefix) throws SAXException;
  public void startElement(String namespaceURI, String localName,
   String qualifiedName, Attributes atts) throws SAXException;
  public void endElement(String namespaceURI, String localName,
   String qualifiedName) throws SAXException;
  public void characters(char[] text, int start, int length)
   throws SAXException;
  public void ignorableWhitespace(char[] text, int start, int length)
   throws SAXException;
  public void processingInstruction(String target, String data)
   throws SAXException;
  public void skippedEntity(String name) throws SAXException;

}
The XMLReaderFactory Class

XMLReaderFactory creates XMLReader instances in a parser-independent manner. The noargs createXMLReader( ) method instantiates the class named by the org.xml.sax.driver system property. The other createXMLReader( ) method instantiates the class named by its argument. This argument should be a fully packaged qualified name, such as org.apache.xerces.parsers.SAXParser:

package org.xml.sax.helpers;

public final class XMLReaderFactory {

  public static XMLReader createXMLReader( ) throws SAXException;
  public static XMLReader createXMLReader(String className)
   throws SAXException;

}
Previous | Home | Next
25. SAX Reference | Book Index | 25.3. SAX Features and Properties
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


SAX Features and Properties (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

25.3. SAX Features and Properties

Absolute URIs are used to name a SAX parser's properties and features. Features have a boolean value; that is, for each parser, a recognized feature is either true or false. Properties have object values. SAX defines six core features and two core properties that parsers should recognize. In addition, parsers can add features and properties to this list, and most do.

SAX Core Features

All SAX parsers should recognize six core features. Of these six, two (http://xml.org/sax/features/namespaces and http://xml.org/sax/features/namespace-prefixes) must be implemented by all conformant processors. The other four are optional and may not be implemented by all parsers:

http://xml.org/sax/features/namespaces
When true, this feature indicates that the startElement( ) and endElement( ) methods provide namespace URIs and local names for elements and attributes. When false, the parser provides prefixed element and attribute names to the startElement( ) and endElement( ) methods. If a parser does not provide something it is not required to provide, then that value will be set to the empty string. However, most parsers provide all three (URI, local name, and prefixed name) regardless of the value of this feature. This feature is true by default.

http://xml.org/sax/features/namespace-prefixes
When true, this feature indicates that xmlns and xmlns:prefix attributes will be included in the attributes list passed to startElement( ). When false, these attributes are omitted. Furthermore, if this feature is true, then the parser will provide the prefixed names for elements and attributes. The default is false unless http://xml.org/sax/features/namespaces is false, in which case this feature defaults to true. You can set both http://xml.org/sax/features/namespaces and http://xml.org/sax/features/namespace-prefixes to true to guarantee that local names, namespace URIs, and prefixed names are all available.

http://xml.org/sax/features/string-interning
When this feature is true, all element names, prefixes, attribute names, namespace URIs, and local names are internalized using the intern( ) method of java.lang.String; that is, equal names compare equally when using ==.

http://xml.org/sax/features/validation
When true, the parser validates. When false, it doesn't. The default is false for most parsers. If you turn on this feature, you'll probably also want to register an ErrorHandler with the XMLReader to receive notice of any validity errors.

http://xml.org/sax/features/external-general-entities
When true, the parser resolves external parsed general entities. When false, it doesn't. The default is true for most parsers that can resolve external entities. Turning on validation automatically activates this feature because validation requires resolving external entities.

http://xml.org/sax/features/external-parameter-entities
When true, the parser resolves external parameter entities. When false, it doesn't. Turning on validation automatically activates this feature because validation requires resolving external entities.

SAX Core Properties

SAX defines two core properties, though implementations are not required to support them:

http://xml.org/sax/properties/dom-node
This property's value is an org.w3c.dom.Node object that represents the current node the parser is visiting.

http://xml.org/sax/properties/xml-string
This property's value is a java.lang.String object containing the characters that were the source for the current event. As of mid-2001, no Java parsers are known to implement this property.

Previous | Home | Next
25.2. The org.xml.sax.helpers Package | Book Index | 25.4. The org.xml.sax.ext Package
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The org.xml.sax.ext Package (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

25.4. The org.xml.sax.ext Package

The org.xml.sax.ext package provides optional interfaces that parsers may use to provide further functionality. Not all parsers support these interfaces, though most major ones do.

The DeclHandler Interface

DeclHandler is a callback interface that provides information about the ELEMENT, ATTLIST, and parsed ENTITY declarations in the document's DTD. To configure an XMLReader with a DeclHandler, pass the name http://xml.org/sax/properties/DeclHandler and an instance of your handler to the reader's setProperty( ) method:

try {
  parser.setProperty(
   "http://xml.org/sax/properties/DeclHandler",
    new YourDeclHandlerImplementationClass( ));
}
catch(SAXException e) {
  System.out.println("This parser does not provide declarations.");
}

If the parser does not provide declaration events, it throws a SAXNotRecognizedException. If the parser cannot install a DeclHandler at this moment (generally because it's in the middle of parsing a document), then it throws a SAXNotSupportedException. If it doesn't throw one of these exceptions, it will call back to the methods in your DeclHandler as it parses the DTD:

package org.xml.sax.ext;

public interface DeclHandler {

  public void elementDecl(String name, String model) throws SAXException;
  public void attributeDecl(String elementName, String attributeName,
   String type, String defaultValue, String value) throws SAXException;
  public void internalEntityDecl(String name, String value)
   throws SAXException;
  public void externalEntityDecl(String name, String publicID,
   String systemID) throws SAXException;

}
The LexicalHandler Interface

LexicalHandler is a callback interface that provides information about aspects of the document that are not normally relevant, specifically:

  • CDATA sections

  • Entity boundaries

  • DTD boundaries

  • Comments

Without a LexicalHandler, the parser simply ignores comments and expands entity references and CDATA sections. By using the LexicalHandler interface, however, you can read the comments and learn which text came from regular character data, which came from a CDATA section, and which came from which entity reference.

To configure an XMLReader with a LexicalHandler, pass an instance of your handler to the reader's setProperty( ) method with the name http://xml.org/sax/properties/LexicalHandler:

try {
  parser.setProperty(
    "http://xml.org/sax/properties/LexicalHandler",
    new YourLexicalHandlerClass( )
  );
}
catch(SAXException e) {
  System.out.println("This parser does not provide lexical events.");
}

If the parser does not provide lexical events, it throws a SAXNotRecognizedException. If the parser cannot install a LexicalHandler at this moment (generally because it's in the middle of parsing a document), then it throws a SAXNotSupportedException. If it doesn't throw one of these exceptions, it calls back to the methods in your LexicalHandler as it encounters entity references, comments, and CDATA sections. The basic content of the resolved entities and CDATA sections are still reported through the ContentHandler interface, as normal:

package org.xml.sax.ext;

public interface LexicalHandler {

  public void startDTD(String name, String publicID, String systemID)
   throws SAXException;
  public void endDTD( ) throws SAXException;
  public void startEntity(String name) throws SAXException;
  public void endEntity(String name) throws SAXException;
  public void startCDATA( ) throws SAXException;
  public void endCDATA( ) throws SAXException;
  public void comment(char[] text, int start, int length)
   throws SAXException;

}
Previous | Home | Next
25.3. SAX Features and Properties | Book Index | 26. Character Sets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Character Sets (XML in a Nutshell, 2nd Edition)

 Book Home XML in a NutshellSearch this book

Chapter 26. Character Sets

Contents:

Character Tables
HTML4 Entity Sets
Other Unicode Blocks

By default, an XML parser assumes that XML documents are written in the UTF-8 encoding of Unicode. However, documents may instead be written in any character set the XML processor understands, provided that there's either some external metadata like an HTTP header or internal metadata like a byte order mark or an encoding declaration that specifies the character set. For example, a document written in the Latin-5 character set would need this XML declaration:

<?xml version="1.0" encoding="ISO-8859-9"?>

Most good XML processors understand many common character sets. The XML specification recommends the character names shown in Table 26-1. When using any of these character sets, you should use these names. Of these character sets, only UTF-8 and UTF-16 must be supported by all XML processors, though many XML processors support all character sets listed here, and many support additional character sets besides. When using character sets not listed here, you should use the names specified in the IANA character sets registry at http://www.iana.org/assignments/character-sets.

Table 26-1. Character set names defined by the XML 1.0 specification

Name | Character set
UTF-8 | The default encoding used in XML documents, unless an encoding declaration, byte order mark, or external metadata specifies otherwise; a variable-width encoding of Unicode that uses one to six bytes per character. UTF-8 is designed such that all ASCII documents are legal UTF-8 documents, which is not true for other character sets, such as UTF-16 and Latin-1. This character set is the best encoding choice if your XML documents contain limited Chinese, Japanese, or Korean.
UTF-16 | A two-byte encoding of Unicode in which all Unicode characters defined in Unicode 3.0 and earlier (including the ASCII characters) occupy exactly two bytes. However, characters from planes 1 through 14, added in Unicode 3.1 and later, are encoded using surrogate pairs of 4 bytes each. This encoding is the best choice if your XML documents contain substantial amounts of Chinese, Japanese, or Korean.
ISO-10646-UCS-2 | The Basic Multilingual Plane of Unicode, i.e., plane 0. This character set is the same as UTF-16, except that it does not allow surrogate pairs to represent characters with code points beyond 65,535. The difference is only significant in Unicode 3.1 and later. Each Unicode character is represented as exactly one two-byte, unsigned integer. Determining endianness requires a byte-order mark at the beginning of the file.
ISO-10646-UCS-4 | A four-byte encoding of Unicode in which each Unicode character is represented as exactly one four-byte, unsigned integer. Determining endianness requires a byte-order mark at the beginning of the file.
ISO-8859-1 | Latin-1, ASCII plus the characters needed for most Western European languages, including Danish, Dutch, English, Faroese, Finnish, Flemish, German, Icelandic, Irish, Italian, Norwegian, Portuguese, Spanish, and Swedish. Some non-European languages, such as Hawaiian, Indonesian, and Swahili, also use these characters.
ISO-8859-2 | Latin-2, ASCII plus the characters needed for most Central European languages, including Croatian, Czech, Hungarian, Polish, Slovak, and Slovenian.
ISO-8859-3 | Latin-3, ASCII plus the characters needed for Esperanto, Maltese, Turkish, and Galician. Latin-5, ISO-8859-9, however, is now preferred for Turkish.
ISO-8859-4 | Latin-4, ASCII plus the characters needed for the Baltic languages Latvian, Lithuanian, Greenlandic, and Lappish. Now largely replaced by ISO-8859-10, Latin-6.
ISO-8859-5 | ASCII plus the Cyrillic characters used for Byelorussian, Bulgarian, Macedonian, Russian, Serbian, and Ukrainian.
ISO-8859-6 | ASCII plus Arabic
ISO-8859-7 | ASCII plus modern Greek.
ISO-8859-8 | ASCII plus Hebrew.
ISO-8859-9 | Latin-5, which is essentially the same as Latin-1 (ASCII plus Western Europe), except that the Turkish letters Figure , 1, Figure , Figure , Figure , and Figure replace the less-commonly used Icelandic letters Figure , Figure , Figure , Figure , Figure , and Figure .
ISO-8859-10 | Latin-6, which covers the characters needed for the Northern European languages Estonian, Lithuanian, Greenlandic, Icelandic, Inuit, and Lappish. It's similar to Latin-4, but drops some symbols and the Latvian letter, Figure adds a few extra letters needed for Inuit and Lappish, and moves various characters around. ISO-8859-13 now supersedes this character set.
ISO-8859-11 | Adds the Thai alphabet to basic ASCII. However, it is not well supported by current XML parsers, and you're probably better off using Unicode instead.
ISO-8859-12 | Not yet in existence and unlikely to exist in the foreseeable future. At one point, this character set was considered for Devanagari, so the number was reserved. However, this effort is not yet off the ground, and it now seems likely that the increasing acceptance of Unicode will make such a character set unnecessary.
ISO-8859-13 | Another character set designed to cover the Baltic languages. This set adds back in the Latvian letter Figure and other symbols dropped from Latin-6.
ISO-8859-14 | Latin-8; a variant of Latin-1 with extra letters needed for Gaelic and Welsh, such as Figure , Figure , and Figure . These letters mostly replace punctuation marks, such as x and |.
ISO-8859-15 | Known officially as Latin-9 and unofficially as Latin-0; a revision of Latin-1 that replaces the international currency symbol ¤ with the Euro sign Figure . It also replaces the seldom-used fraction characters 1/4, 1/2, and 3/4 with the uncommon French letters , Figure , Figure , Figure and the ¬, Figure , and ′ symbols with the Finnish letters Figure , Figure , and Figure . Otherwise, it's identical to ISO-8859-1.
ISO-8859-16 | Latin-10; intended primarily for Romanian.
ISO-2022-JP | A seven-bit encoding of the character set defined in the Japanese national standard JIS X-0208-1997 used on web pages and in email; see RFC 1468.
Shift_JIS | The encoding of the Japanese national standard character set JIS X-0208-1997 used in Microsoft Windows.
EUC-JP | The encoding of the Japanese national standard character set JIS X-0208-1997 used by most Unixes.
Some parsers do not understand all these encodings. Specifically, parsers based on James Clark's expat often support only UTF-8, UTF-16, ISO-8859-1, and US-ASCII encodings. Xerces-C supports ASCII, UTF-8, UTF-16, UCS4, IBM037, IBM1140, ISO-8859-1, and Windows-1252. IBM's XML4C parser, derived from the Xerces codebase, adds over 100 more encodings, including ISO-8859 character sets 1 through 9 and 15. However, for maximum cross-parser compatibility, you should convert your documents to either UTF-8 or UTF-16 before publishing them, even if you author them in another character set.

26.1. Character Tables

The XML 1.0 specification divides Unicode into five overlapping sets:

Name characters
Characters that can appear in an element, attribute, or entity name. These characters are letters, ideographs, digits, and the punctuation marks _, -, ., and :. In the tables that follow, name characters are shown in bold type, such as A, Å, Figure , Figure , Figure , 1, 2, 3, Figure , Figure , and _.

Name start characters
Characters that can be the first character of an element, attribute, or entity name. These characters are letters, ideographs, and the underscore _. In the tables that follow, these characters are shown with a gray background, such as A, Å, Figure , Figure , Figure , Figure , Figure , and _. Because name start characters are a subset of name characters, they are also shown in bold.

Character data characters
All characters that can be used anywhere in an XML document, including element and attribute content, comments, and DTDs. This set includes almost all Unicode characters, except for surrogates and most C0 control characters. These characters are shown in a normal typeface. If they are name characters, then they will be bold. If they are also name start characters, they'll have a gray background.

Illegal characters
Characters that may not appear anywhere in an XML document, such as in part of a name, character data, or comment text. These characters are shown in italic, such as NUL or BEL. Most of these characters are either C0 control characters or half of a surrogate pair.

Unassigned code points
Bytes or byte sequences that are not assigned to a character as of Unicode 3.1.1. Theoretically, a program could produce a file containing one of these byte sequences, but their meaning is undefined and they should be avoided. They are represented in the following tables as n/a.

Figure 26-1 shows the relationship between these sets. Note that all name start characters are name characters and that all name characters are character data characters.

Figure 26-1. XML's division of Unicode characters

In all the tables that follow, each cell's upper lefthand corner contains the character's two-digit Unicode hexadecimal value and the upper righthand corner contains the character's Unicode decimal value. You can insert a character in an XML document by prefixing the decimal value with &# and suffixing it with a semicolon. Thus, Unicode character 69, the capital letter E, can be written as &#69;. Hexadecimal values work the same way, except that you prefix them with &#x;. In hexadecimal, the letter E is 45, so it can also be written as &#x45;.

26.1.1. ASCII

Most character sets in common use today are supersets of ASCII. That is, code points 0 through 127 are assigned to the same characters to which ASCII assigns them. Figure 26-2 lists the ASCII character set. The only notable exceptions are the EBCDIC-derived character sets. Specifically, Unicode is a superset of ASCII, and code points 1 through 127 identify the same characters in Unicode as they do in ASCII.

Figure 26-2. The first 128 Unicode characters (known as the ASCII character set)

Characters 0 through 31 and character 127 are nonprinting control characters, sometimes called the C0 controls to distinguish them from the C1 controls used in the ISO-8859 character sets. Of these 33 characters, only the carriage return, linefeed, and horizontal tab may appear in XML documents. The other 29 may not appear anywhere in an XML document, including in tags, comments, or parsed character data. They may not be inserted with character references, such as &#31;. For example, you may not use form feeds to insert page breaks.

26.1.2. ISO-8859-1, Latin-1

Character sets defined by the ISO-8859 standard comprise one popular superset of the ASCII character sets. These characters all provide the normal ASCII characters from code points 0 through 127 and the C1 controls from 128 to 159, as well as change the characters from 160 through 255.

In particular, many Western European and American systems use a character set called Latin-1. This set is the first code page defined in the ISO-8859 standard and is also called ISO-8859-1. Though all common encodings of Unicode map code points 128 through 255 differently than Latin-1, code points 128 through 255 map to the same characters in both Latin-1 and Unicode. This situation does not occur in other character sets.

26.1.2.1. C1 controls

All ISO-8859 character sets begin with the same 32 extra nonprinting control characters in code points 128 through 159. These sets are used on terminals like the DEC VT-320 to provide graphics functionality not included in ASCII, for example, erasing the screen and switching it to inverse video or graphics mode. These characters cause severe problems for anyone reading or editing an XML document on a terminal or terminal emulator.

Fortunately, these characters are not necessary in XML documents. Their inclusion in XML 1.0 was an oversight. They should have been banned like the C0 controls. Unfortunately, many editors and documents incorrectly label documents written in the Cp1252 Windows character set as ISO-8859-1. This character set does use the code points between 128 and 159 for noncontrol graphics characters. When documents written with this character set are displayed or edited on a dumb terminal, they can effectively disable the user's terminal. Similar problems exist with most other Windows code pages for single-byte character sets.

In the spirit of being liberal in what you accept and conservative in what you generate, you should never use Cp1252, correctly labeled or otherwise. You should also avoid using other nonstandard code pages for documents that move beyond a single system. On the other hand, if you receive a document labeled as Cp1252 (or any other Windows code page), it can be displayed if you're careful not to throw it at a terminal unchanged. If you suspect that a document labeled as ISO-8859-1 that uses characters between 128 and 159 is in fact a Cp1252 document, you should probably reject it. This decision is difficult, however, given the prevalence of broken software that does not identify documents sent properly.

26.1.2.2. Latin-1

Latin-1 covers most Western European languages that use some variant of the Latin alphabet. Characters 0 through 127 in this set are identical to the ASCII characters with the same code points. Characters 128 to 159 are the C1 control characters used only for dumb terminals. Character 160 is the nonbreaking space. Characters 161 through 255 are accented characters, such as è, á, and ö, non-U.S. punctuation marks, such as £ and ¿, and a few new letters, such as the Icelandic Figure and ß. Figure 26-3 shows the upper half of this character set. The lower half is identical to the ASCII character set shown in Figure 26-2.

Figure 26-3. Unicode characters between 160 and 255 and the second half of the Latin-1, ISO-8859-1 character set

Previous | Home | Next
25.4. The org.xml.sax.ext Package | Book Index | 26.2. HTML4 Entity Sets
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


HTML4 Entity Sets (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

26.2. HTML4 Entity Sets

HTML 4.0 predefines several hundred named entities for use in your documents, many of which are quite useful. For instance, the nonbreaking space is &nbsp;. XML, however, defines only five named entities:

&amp;
The ampersand (&)

&lt;
The less-than sign (<)

&gt;
The greater-than sign (>)

&quot;
The straight double quote (")

&apos;
The apostrophe (')

Other needed characters can be inserted with character references in decimal or hexadecimal format. For instance, the nonbreaking space is Unicode character 160 (decimal). Therefore, you can insert it in your document as either &#160; or &#xA0;. If you really want to type it as &nbsp;, you can define this entity reference in your DTD. Doing so requires you to use a character reference:

<!ENTITY nbsp "&#160;">

The XHTML 1.0 specification includes three DTD fragments that define the familiar HTML character references:

Latin-1 characters (http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent)
The non-ASCII, graphic characters included in ISO-8859-1 from code points 160 through 255, shown in Figure 26-3

Special characters (http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent)
A few useful letters and punctuation marks not included in Latin-1

Symbols (http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent)
The Greek alphabet, plus various arrows, mathematical operators, and other symbols used in mathematics

Feel free to borrow these entity sets for your own use. They should be included in your document's DTD with these parameter entity references and PUBLIC identifiers:

<!ENTITY % HTMLlat1 PUBLIC
   "-//W3C//ENTITIES Latin 1 for XHTML//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
%HTMLlat1;
<!ENTITY % HTMLspecial PUBLIC
    "-//W3C//ENTITIES Special for XHTML//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent">
%HTMLspecial;
<!ENTITY % HTMLsymbol PUBLIC
    "-//W3C//ENTITIES Symbols for XHTML//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent">
%HTMLsymbol;

However, we do recommend saving local copies and changing the system identifier to match the new location, rather than downloading them from the http://www.w3.org every time you need to parse a file. You may import just one, two, or all three of them, depending on what you need. There are no interdependencies.

Alternatively, just use the character references given in Table 26-4 through Table 26-6.

Table 26-4. The HTML Latin-1 entity set

Character

Meaning

XHTMLentity reference

Hexadecimalcharacter reference

Decimalcharacter reference

Nonbreaking space

&nbsp;

&#xA0;

&#160;

¡

Inverted exclamation mark

&iexcl;

&#xA1;

&#161;

¢

Cent sign

&cent;

&#xA2;

&#162;

£

Pound sign

&pound;

&#xA3;

&#163;

¤

Currency sign

&curren;

&#xA4;

&#164;

¥

Yen sign, Yuan sign

&yen;

&#xA5;

&#165;

|

Broken vertical bar

&brvbar;

&#xA6;

&#166;

§

Section sign

&sect;

&#xA7;

&#167;

Figure

Dieresis, spacing dieresis

&uml;

&#xA8;

&#168;

©

Copyright sign

&copy;

&#xA9;

&#169;

ª

Feminine ordinal indicator

&ordf;

&#xAA;

&#170;

«

Left-pointing double angle quotation mark, left-pointing guillemot

&laquo;

&#xAB;

&#171;

¬

Not sign

&not;

&#xAC;

&#172;

-

Soft hyphen, discretionary hyphen

&shy;

&#xAD;

&#173;

®

Registered trademark sign

&reg;

&#xAE;

&#174;

¯

Macron, overline, APL overbar

&macr;

&#xAF;

&#175;

°

Degree sign

&deg;

&#xB0;

&#176;

±

Plus-or-minus sign

&plusmn;

&#xB1;

&#177;

2

Superscript digit two, squared

&sup2;

&#xB2;

&#178;

3

Superscript digit three, cubed

&sup3;

&#xB3;

&#179;

´

Acute accent, spacing acute

&acute;

&#xB4;

&#180;

µ

Micro sign

&micro;

&#xB5;

&#181;

¶

Pilcrow sign, paragraph sign

&para;

&#xB6;

&#182;

Figure

Middle dot, Georgian comma, Greek middle dot

&middot;

&#xB7;

&#183;

¸

Cedilla, spacing cedilla

&cedil;

&#xB8;

&#184;

1

Superscript digit one

&sup1;

&#xB9;

&#185;

º

Masculine ordinal indicator

&ordm;

&#xBA;

&#186;

»

Right-pointing double angle quotation mark, right-pointing guillemot

&raquo;

&#xBB;

&#187;

1/4

Vulgar fraction one-quarter

&frac14;

&#xBC;

&#188;

1/2

Vulgar fraction one-half

&frac12;

&#xBD;

&#189;

3/4

Vulgar fraction three-quarters

&frac34;

&#xBE;

&#190;

¿

Inverted question mark

&iquest;

&#xBF;

&#191;

À

Latin capital letter A with grave

&Agrave;

&#xC0;

&#192;

Á

Latin capital letter A with acute

&Aacute;

&#xC1;

&#193;

Â

Latin capital letter A with circumflex

&Acirc;

&#xC2;

&#194;

Ã

Latin capital letter A with tilde

&Atilde;

&#xC3;

&#195;

Ä

Latin capital letter A with dieresis

&Auml;

&#xC4;

&#196;

Å

Latin capital letter A with ring above, Latin capital letter A ring

&Aring;

&#xC5;

&#197;

Æ

Latin capital letter AE, Latin capital ligature AE

&AElig;

&#xC6;

&#198;

Ç

Latin capital letter C with cedilla

&Ccedil;

&#xC7;

&#199;

È

Latin capital letter E with grave

&Egrave;

&#xC8;

&#200;

É

Latin capital letter E with acute

&Eacute;

&#xC9;

&#201;

Ê

Latin capital letter E with circumflex

&Ecirc;

&#xCA;

&#202;

Ë

Latin capital letter E with dieresis

&Euml;

&#xCB;

&#203;

Ì

Latin capital letter I with grave

&Igrave;

&#xCC;

&#204;

Í

Latin capital letter I with acute

&Iacute;

&#xCD;

&#205;

Î

Latin capital letter I with circumflex

&Icirc;

&#xCE;

&#206;

Ï

Latin capital letter I with dieresis

&Iuml;

&#xCF;

&#207;

Figure

Latin capital letter eth

&ETH;

&#xD0;

&#208;

Ñ

Latin capital letter N with tilde

&Ntilde;

&#xD1;

&#209;

Ò

Latin capital letter O with grave

&Ograve;

&#xD2;

&#210;

Ó

Latin capital letter O with acute

&Oacute;

&#xD3;

&#211;

Ô

Latin capital letter O with circumflex

&Ocirc;

&#xD4;

&#212;

Õ

Latin capital letter O with tilde

&Otilde;

&#xD5;

&#213;

Ö

Latin capital letter O with dieresis

&Ouml;

&#xD6;

&#214;

x

Multiplication sign

&times;

&#xD7;

&#215;

Ø

Latin capital letter O with stroke

&Oslash;

&#xD8;

&#216;

Ù

Latin capital letter U with grave

&Ugrave;

&#xD9;

&#217;

Ú

Latin capital letter U with acute

&Uacute;

&#xDA;

&#218;

Û

Latin capital letter U with circumflex

&Ucirc;

&#xDB;

&#219;

Ü

Latin capital letter U with dieresis

&Uuml;

&#xDC;

&#220;

Figure

Latin capital letter Y with acute

&Yacute;

&#xDD;

&#221;

Figure

Latin capital letter thorn

&THORN;

&#xDE;

&#222;

ß

Latin small letter sharp s, ess-zett

&szlig;

&#xDF;

&#223;

à

Latin small letter a with grave

&agrave;

&#xE0;

&#224;

á

Latin small letter a with acute

&aacute;

&#xE1;

&#225;

â

Latin small letter a with circumflex

&acirc;

&#xE2;

&#226;

ã

Latin small letter a with tilde

&atilde;

&#xE3;

&#227;

ä

Latin small letter a with dieresis

&auml;

&#xE4;

&#228;

å

Latin small letter a with ring above

&aring;

&#xE5;

&#229;

æ

Latin small letter ae, Latin small ligature ae

&aelig;

&#xE6;

&#230;

ç

Latin small letter c with cedilla

&ccedil;

&#xE7;

&#231;

è

Latin small letter e with grave

&egrave;

&#xE8;

&#232;

é

Latin small letter e with acute

&eacute;

&#xE9;

&#233;

ê

Latin small letter e with circumflex

&ecirc;

&#xEA;

&#234;

ë

Latin small letter e with dieresis

&euml;

&#xEB;

&#235;

ì

Latin small letter i with grave

&igrave;

&#xEC;

&#236;

í

Latin small letter i with acute

&iacute;

&#xED;

&#237;

î

Latin small letter i with circumflex

&icirc;

&#xEE;

&#238;

ï

Latin small letter i with dieresis

&iuml;

&#xEF;

&#239;

Figure

Latin small letter eth

&eth;

&#xF0;

&#240;

ñ

Latin small letter n with tilde

&ntilde;

&#xF1;

&#241;

ò

Latin small letter o with grave

&ograve;

&#xF2;

&#242;

ó

Latin small letter o with acute

&oacute;

&#xF3;

&#243;

ô

Latin small letter o with circumflex

&ocirc;

&#xF4;

&#244;

õ

Latin small letter o with tilde

&otilde;

&#xF5;

&#245;

ö

Latin small letter o with dieresis

&ouml;

&#xF6;

&#246;

÷

Division sign

&divide;

&#xF7;

&#247;

ø

Latin small letter o with stroke

&oslash;

&#xF8;

&#248;

ù

Latin small letter u with grave

&ugrave;

&#xF9;

&#249;

ú

Latin small letter u with acute

&uacute;

&#xFA;

&#250;

û

Latin small letter u with circumflex

&ucirc;

&#xFB;

&#251;

ü

Latin small letter u with dieresis

&uuml;

&#xFC;

&#252;

Figure

Latin small letter y with acute

&yacute;

&#xFD;

&#253;

Figure

Latin small letter thorn

&thorn;

&#xFE;

&#254;

ÿ

Latin small letter y with dieresis

&yuml;

&#xFF;

&#255;

Table 26-5. The HTML special characters entity set

Character

Meaning

XHTML entity reference

Hexadecimal character reference

Decimal character reference

"

Quotation mark, APL quote

&quot;

&#x22;

&#34;

&

Ampersand

&amp;

&#x26;

&#38;

'

Apostrophe mark

&apos;

&#x27;

&#39;

<

Less-than sign

&lt;

&#x3C;

&#60;

>

Greater-than sign

&gt;

&#x3E;

&#62;

Figure

Latin capital ligature OE

&OElig;

&#x152;

&#338;

Figure

Latin small ligature oe

&oelig;

&#x153;

&#339;

Figure

Latin capital letter S with caron

&Scaron;

&#x160;

&#352;

Figure

Latin small letter s with caron

&scaron;

&#x161;

&#353;

Figure

Latin capital letter Y with dieresis

&Yuml;

&#x178;

&#376;

~

Modifier letter circumflex accent

&circ;

&#x2C6;

&#710;

~

Small tilde

&tilde;

&#x2DC;

&#732;

En space

&ensp;

&#x2002;

&#8194;

Em space

&emsp;

&#x2003;

&#8195;

Thin space

&thinsp;

&#x2009;

&#8201;

Nonprinting character

Zero width nonjoiner

&zwnj;

&#x200C;

&#8204;

Nonprinting character

Zero width joiner

&zwj;

&#x200D;

&#8205;

Nonprinting character

Left-to-right mark

&lrm;

&#x200E;

&#8206;

Nonprinting character

Right-to-left mark

&rlm;

&#x200F;

&#8207;

-

En dash

&ndash;

&#x2013;

&#8211;

--

Em dash

&mdash;

&#x2014;

&#8212;

`

Left single quotation mark

&lsquo;

&#x2018;

&#8216;

'

Right single quotation mark

&rsquo;

&#x2019;

&#8217;

,

Single low-9 quotation mark

&sbquo;

&#x201A;

&#8218;

"

Left double quotation mark

&ldquo;

&#x201C;

&#8220;

"

Right double quotation mark

&rdquo;

&#x201D;

&#8221;

Figure

Double low-9 quotation mark

&bdquo;

&#x201E;

&#8222;

Figure

Dagger

&dagger;

&#x2020;

&#8224;

Figure

Double dagger

&Dagger;

&#x2021;

&#8225;

Figure

Per mille sign

&permil;

&#x2030;

&#8240;

Figure

Single left-pointing angle quotation mark

&lsaquo;

&#x2039;

&#8249;

Figure

Single right-pointing angle quotation mark

&rsaquo;

&#x203A;

&#8250;

Figure

Euro sign

&euro;
&#x20AC;

&#8364;

Table 26-6. The HTML symbol entity set

Character

Meaning

XHTML entity reference

Hexadecimal character reference

Decimal character reference

Figure

Latin small f with hook, function, florin

&fnof;

&#x192;

&#402;

A

Greek capital letter alpha

&Alpha;

&#x391;

&#913;

B

Greek capital letter beta

&Beta;

&#x392;

&#914;

Figure

Greek capital letter gamma

&Gamma;

&#x393;

&#915;

Figure

Greek capital letter delta

&Delta;

&#x394;

&#916;

E

Greek capital letter epsilon

&Epsilon;

&#x395;

&#917;

Z

Greek capital letter zeta

&Zeta;

&#x396;

&#918;

H

Greek capital letter eta

&Eta;

&#x397;

&#919;

Figure

Greek capital letter theta

&Theta;

&#x398;

&#920;

I

Greek capital letter iota

&Iota;

&#x399;

&#921;

K

Greek capital letter kappa

&Kappa;

&#x39A;

&#922;

Figure

Greek capital letter lambda

&Lambda;

&#x39B;

&#923;

M

Greek capital letter mu

&Mu;

&#x39C;

&#924;

N

Greek capital letter nu

&Nu;

&#x39D;

&#925;

Figure

Greek capital letter xi

&Xi;

&#x39E;

&#926;

O

Greek capital letter omicron

&Omicron;

&#x39F;

&#927;

Figure

Greek capital letter pi

&Pi;

&#x3A0;

&#928;

Figure

Greek capital letter rho

&Rho;

&#x3A1;

&#929;

Figure

Greek capital letter sigma

&Sigma;

&#x3A3;

&#931;

T

Greek capital letter tau

&Tau;

&#x3A4;

&#932;

Figure

Greek capital letter upsilon

&Upsilon;

&#x3A5;

&#933;

Figure

Greek capital letter phi

&Phi;

&#x3A6;

&#934;

Figure

Greek capital letter chi

&Chi;

&#x3A7;

&#935;

Figure

Greek capital letter psi

&Psi;

&#x3A8;

&#936;

Figure

Greek capital letter omega

&Omega;

&#x3A9;

&#937;

Figure

Greek small letter alpha

&alpha;

&#x3B1;

&#945;

Figure

Greek small letter beta

&beta;

&#x3B2;

&#946;

Figure

Greek small letter gamma

&gamma;

&#x3B3;

&#947;

Figure

Greek small letter delta

&delta;

&#x3B4;

&#948;

Figure

Greek small letter epsilon

&epsilon;

&#x3B5;

&#949;

Figure

Greek small letter zeta

&zeta;

&#x3B6;

&#950;

Figure

Greek small letter eta

&eta;

&#x3B7;

&#951;

Figure

Greek small letter theta

&theta;

&#x3B8;

&#952;

Figure

Greek small letter iota

&iota;

&#x3B9;

&#953;

Figure

Greek small letter kappa

&kappa;

&#x3BA;

&#954;

Figure

Greek small letter lambda

&lambda;

&#x3BB;

&#955;

μ

Greek small letter mu

&mu;

&#x3BC;

&#956;

Figure

Greek small letter nu

&nu;

&#x3BD;

&#957;

Figure

Greek small letter xi

&xi;

&#x3BE;

&#958;

Figure

Greek small letter omicron

&omicron;

&#x3BF;

&#959;

Figure

Greek small letter pi

&pi;

&#x3C0;

&#960;

Figure

Greek small letter rho

&rho;

&#x3C1;

&#961;

Figure

Greek small letter final sigma

&sigmaf;

&#x3C2;

&#962;

Figure

Greek small letter sigma

&sigma;

&#x3C3;

&#963;

Figure

Greek small letter tau

&tau;

&#x3C4;

&#964;

Figure

Greek small letter upsilon

&upsilon;

&#x3C5;

&#965;

Figure

Greek small letter phi

&phi;

&#x3C6;

&#966;

Figure

Greek small letter chi

&chi;

&#x3C7;

&#967;

Figure

Greek small letter psi

&psi;

&#x3C8;

&#968;

Figure

Greek small letter omega

&omega;

&#x3C9;

&#969;

Figure

Greek small letter theta symbol

&thetasym;

&#x3D1;

&#977;

Figure

Greek upsilon with hook symbol

&upsih;

&#x3D2;

&#978;

Figure

Greek pi symbol

&piv;

&#x3D6;

&#982;

·

Bullet, black small circle

&bull;

&#x2022;

&#8226;

...

Horizontal ellipsis, three-dot leader

&hellip;

&#x2026;

&#8230;

´

Prime, minutes, feet

&prime;

&#x2032;

&#8242;

Figure

Double prime, seconds, inches

&Prime;

&#x2033;

&#8243;

¯

Overline, spacing overscore

&oline;

&#x203E;

&#8254;

/

Fraction slash

&frasl;

&#x2044;

&#8260;

Figure

Black letter capital I, imaginary part

&image;

&#x2111;

&#8465;

Figure

Script capital P, power set, Weierstrass p

&weierp;

&#x2118;

&#8472;

Figure

Black letter capital R, real part symbol

&real;

&#x211C;

&#8476;

™

Trademark sign

&trade;

&#x2122;

&#8482;

Figure

Aleph symbol, first transfinite cardinal

&alefsym;

&#x2135;

&#8501;

Figure

Leftward arrow

&larr;

&#x2190;

&#8592;

Figure

Upward arrow

&uarr;

&#x2191;

&#8593;

Figure

Rightward arrow

&rarr;

&#x2192;

&#8594;

Figure

Downward arrow

&darr;

&#x2193;

&#8595;

Figure

Left-right arrow

&harr;

&#x2194;

&#8596;

Figure

Downward arrow with corner leftward, carriage return

&crarr;

&#x21B5;

&#8629;

Figure

Leftward double arrow

&lArr;

&#x21D0;

&#8656;

Figure

Upward double arrow

&uArr;

&#x21D1;

&#8657;

Figure

Rightward double arrow

&rArr;

&#x21D2;

&#8658;

Figure

Downward double arrow

&dArr;

&#x21D3;

&#8659;

Figure

Left-right double arrow

&hArr;

&#x21D4;

&#8660;

Figure

For all

&forall;

&#x2200;

&#8704;

Figure

Partial differential

&part;

&#x2202;

&#8706;

Figure

There exists

&exist;

&#x2203;

&#8707;

Figure

Empty set, null set, diameter

&empty;

&#x2205;

&#8709;

Figure

Nabla, backward difference

&nabla;

&#x2207;

&#8711;

Figure

Element of

&isin;

&#x2208;

&#8712;

Figure

Not an element of

&notin;

&#x2209;

&#8713;

Figure

Contains as member

&ni;

&#x220B;

&#8715;

Figure

N-ary product, product sign

&prod;

&#x220F;

&#8719;

Figure

N-ary summation

&sum;

&#x2211;

&#8721;

-

Minus sign

&minus;

&#x2212;

&#8722;

*

Asterisk operator

&lowast;

&#x2217;

&#8727;

Figure

Square root, radical sign

&radic;

&#x221A;

&#8730;

Figure

Proportional to

&prop;

&#x221D;

&#8733;

Figure

Infinity

&infin;

&#x221E;

&#8734;

Figure

Angle

&ang;

&#x2220;

&#8736;

Figure

Logical and, wedge

&and;

&#x2227;

&#8743;

Figure

Logical or, vee

&or;

&#x2228;

&#8744;

Figure

Intersection, cap

&cap;

&#x2229;

&#8745;

Figure

Union, cup

&cup;

&#x222A;

&#8746;

Figure

Integral

&int;

&#x222B;

&#8747;

Figure

Therefore

&there4;

&#x2234;

&#8756;

~

Tilde operator, varies with, similar to

&sim;

&#x223C;

&#8764;

Figure

Approximately equal to

&cong;

&#x2245;

&#8773;

Figure

Almost equal to, asymptotic to

&asymp;

&#x2248;

&#8776;

Figure

Not equal to

&ne;

&#x2260;

&#8800;

Figure

Identical to

&equiv;

&#x2261;

&#8801;

Figure

Less than or equal to

&le;

&#x2264;

&#8804;

Figure

Greater than or equal to

&ge;

&#x2265;

&#8805;

Figure

Subset of

&sub;

&#x2282;

&#8834;

Figure

Superset of

&sup;

&#x2283;

&#8835;

Figure

Not a subset of

&nsub;

&#x2284;

&#8836;

Figure

Subset of or equal to

&sube;

&#x2286;

&#8838;

Figure

Superset of or equal to

&supe;

&#x2287;

&#8839;

Figure

Circled plus, direct sum

&oplus;

&#x2295;

&#8853;

Figure

Circled times, vector product

&otimes;

&#x2297;

&#8855;

Figure

Up tack, orthogonal to, perpendicular

&perp;

&#x22A5;

&#8869;

Figure

Dot operator

&sdot;

&#x22C5;

&#8901;

Figure

Left ceiling, APL upstile

&lceil;

&#x2308;

&#8968;

Figure

Right ceiling

&rceil;

&#x2309;

&#8969;

Figure

Left floor, APL downstile

&lfloor;

&#x230A;

&#8970;

Figure

Right floor

&rfloor;

&#x230B;

&#8971;

Figure

Left-pointing angle bracket, bra

&lang;

&#x2329;

&#9001;

Figure

Right-pointing angle bracket, ket

&rang;

&#x232A;

&#9002;

Figure

Lozenge

&loz;

&#x25CA;

&#9674;

Figure

Black spade suit

&spades;

&#x2660;

&#9824;

Figure

Black club suit, shamrock

&clubs;

&#x2663;

&#9827;

Figure

Black heart suit, valentine

&hearts;

&#x2665;

&#9829;

Figure

Black diamond suit

&diams;

&#x2666;

&#9830;

Previous | Home | Next
26. Character Sets | Book Index | 26.3. Other Unicode Blocks
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Other Unicode Blocks (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book

26.3. Other Unicode Blocks

So far we've accounted for a little over 300 of the more than 90,000 Unicode characters. Many thousands are still unaccounted for. Outside the ranges defined in XHTML and SGML, standard entity names don't exist. You should either use an editor that can produce the characters you need in the appropriate character set or you should use character references. Most of the 90,000-plus Unicode characters are either Han ideographs, Hangul syllables, or rarely used characters. However, we do list a few of the most useful blocks later in this chapter. Others can be found online at http://www.unicode.org/charts/ or in The Unicode Standard Version 3.0 by the Unicode Consortium (Addison Wesley, 2000).

In the tables that follow, the upper lefthand corner contains the character's hexadecimal Unicode value, and the upper righthand corner contains the character's decimal Unicode value. You can use either value to form a character reference so as to use these characters in element content and attribute values, even without an editor or fonts that support them.

26.3.1. Latin Extended-A

The 128 characters in the Latin Extended-A block of Unicode are used in conjunction with the normal ASCII and Latin-1 characters. They cover most European Latin letters missing from Latin-1. The block includes various characters you'll find in the upper halves of the other ISO-8859 Latin character sets, including ISO-8859-2, ISO-8859-3, ISO-8859-4, and ISO-8859-9. When combined with ASCII and Latin-1, this block lets you write Afrikaans, Basque, Breton, Catalan, Croatian, Czech, Esperanto, Estonian, French, Frisian, Greenlandic, Hungarian, Latvian, Lithuanian, Maltese, Polish, Provençal, Rhaeto-Romanic, Romanian, Romany, Sami, Slovak, Slovenian, Sorbian, Turkish, and Welsh. See Figure 26-7.

Figure 26-7. Unicode's Latin Extended-A block

26.3.2. Latin Extended-B

The Latin Extended-B block of Unicode is used in conjunction with the normal ASCII and Latin-1 characters. It mostly contains characters used for transcription of non-European languages not traditionally written in a Roman script. For instance, it's used for the Pinyin transcription of Chinese and for many African languages. See Figure 26-8.

Figure 26-8. The Latin Extended-B block of Unicode

26.3.3. IPA Extensions

Linguists use the International Phonetic Alphabetic (IPA) to identify uniquely and unambiguously particular sounds of various spoken languages. Besides the symbols listed in this block, the IPA requires use of ASCII, various other extended Latin characters, the combining diacritical marks in Figure 26-11, and a few Greek letters. The block shown in Figure 26-9 only contains the characters not used in more traditional alphabets.

Figure 26-9. The IPA Extensions block of Unicode

26.3.4. Spacing Modifier Letters

The Spacing Modifier Letters block, shown in Figure 26-10, includes characters from multiple languages and scripts that modify the preceding or following character, generally by changing its pronunciation.

Figure 26-10. The Spacing Modifier Letters block of Unicode

26.3.5. Combining Diacritical Marks

The Combining Diacritical Marks block contains characters that are not used on their own, such as the accent grave and circumflex. Instead, they are merged with the preceding character to form a single glyph. For example, to write the character Ñ, you could type the ASCII letter N followed by the combining tilde character, like this: N&#x303;. When rendered, this combination would produce the single glyph Ñ. In Figure 26-11 the character to which the diacritical mark is attached is a dotted circle Figure (Unicode code point &0x25CC;) but of course it could be any normal character.

Figure 26-11. The Combining Diacritical Marks block of Unicode

26.3.6. Greek and Coptic

The Greek block of Unicode is used primarily for the modern Greek language. Currently, it's the only option for the Greek-derived Coptic script, but it doesn't really serve that purpose very well, and a separate Coptic block is a likely addition in the future. Extending coverage to classical and Byzantine Greek requires many more accented characters, which are available in the Greek Extended Block, shown in Figure 26-22, or by combining these characters with the Combining Diacritical Marks in Figure 26-11. The Greek alphabet is also a fertile source of mathematical and scientific notation, though some common letters, such as Figure and Figure , are encoded separately in the Mathematical Operators block in Figure 26-27 and the Mathematical Alphanumeric Symbols block in Figure 26-28 for their use as mathematical symbols. The Greek and Coptic block of Unicode is shown in Figure 26-12.

Figure 26-12. The Greek and Coptic block of Unicode

26.3.7. Cyrillic

While the Cyrillic script shown in Figure 26-13 is most familiar to Western readers from its use for Russian, it's also used for other Slavic languages, including Serbian, Ukrainian, and Byelorussian, and for many non-Slavic languages of the former Soviet Union, such as Azerbaijani, Tuvan, and Ossetian. Indeed, many characters in this block are not actually found in Russian, but exist only in other languages written in the Cyrillic script. Following the breakup of the Soviet Union, some non-Slavic languages, such as Moldavian and Azerbaijani, are now reverting to Latin-derived scripts.

Figure 26-13. The Cyrillic block of Unicode

26.3.8. Armenian

The Armenian script shown in Figure 26-14 is used for writing the Armenian language, currently spoken by about seven million people around the world.

Figure 26-14. The Armenian block of Unicode

26.3.9. Hebrew

The Hebrew alphabet is used for Hebrew, Yiddish, and Judezmo. It's also occasionally used for mathematical notation. See Figure 26-15.

Figure 26-15. The Hebrew block of Unicode

26.3.10. Arabic

The Arabic script shown in Figure 26-16 is used for many languages besides Arabic, including Kurdish, Pashto, Persian, Sindhi, and Urdu. Turkish was also written in the Arabic script until early in the twentieth century when Turkey converted to a modified Latin alphabet.

Figure 26-16. The Arabic block of Unicode

26.3.11. Devanagari

The Devanagari script is used for many languages of the Indian subcontinent, including Awadhi, Bagheli, Bhatneri, Bhili, Bihari, Braj Bhasa, Chhattisgarhi, Garhwali, Gondi, Harauti, Hindi, Ho, Jaipuri, Kachchhi, Kanauji, Konkani, Kului, Kumaoni, Kurku, Kurukh, Marwari, Mundari, Newari, Palpa, and Santali. It's also used for the classical language Sanskrit. See Figure 26-17.

Figure 26-17. The Devanagari block of Unicode

26.3.12. Thai

The Thai script is used for Thai and other Southeast Asian languages, including Kuy, Lavna, and Pali. See Figure 26-18.

Figure 26-18. The Thai block of Unicode

26.3.13. Tibetan

The Tibetan script is used to write the various dialects of Tibetan and Dzongkha, Bhutan's main language. Like Chinese, Tibetan is divided into mutually unintelligible spoken languages, though the written forms are identical. See Figure 26-19.

Figure 26-19. The Tibetan block of Unicode

26.3.14. Ethiopic

The Ethiopic script is used by several languages in Ethiopia, including Amharic. Tigre, Oromo, and the liturgical language Ge'ez. See Figure 26-20.

Figure 26-20. The Ethiopic block of Unicode

26.3.15. Latin Extended Additional

The Latin Extended Additional characters are single code-point representations of letters combined with diacritical marks. This block is particularly useful for modern Vietnamese. See Figure 26-21.

Figure 26-21. The Latin Extended Additional block of Unicode

26.3.16. Greek Extended

The Greek Extended block shown in Figure 26-22 contains mostly archaic letters and accented letters that are used in classical and Byzantine Greek, but not in modern Greek.

Figure 26-22. The Greek Extended block of Unicode

26.3.17. General Punctuation

The General Punctuation block shown in Figure 26-23 contains punctuation characters used across a variety of languages and scripts that are not already encoded in Latin-1. Characters 0x2000 through 0x200B are all varying amounts of whitespace ranging from zero width (0x200B) to six ems (0x2007). 0x200C through 0x200F and 0x206A through 0x206F are nonprinting format characters with no graphical representation.

Figure 26-23. The General Punctuation block of Unicode

26.3.18. Currency Symbols

The Currency Symbols block includes a few monetary symbols not already encoded in other blocks, such as the Indian rupee, the Italian lira, and the Greek drachma. See Figure 26-24.

Figure 26-24. The Currency Symbols block of Unicode

26.3.19. Letter-Like Symbols

The Letter-Like Symbols block covers characters that look like letters, but really aren't, such as the Figure symbol used to represent a prescription. See Figure 26-25.

Figure 26-25. The Letter-Like Symbols block of Unicode

26.3.20. Arrows

The Arrows block contains commonly needed arrow characters, as shown in Figure 26-26.

Figure 26-26. The Arrows block of Unicode

26.3.21. Mathematical Operators

The Mathematical Operators block shown in Figure 26-27 contains a wide variety of symbols used in higher mathematics. A few of these symbols superficially resemble letters in other blocks. For instance, in most fonts character 2206, Figure , is virtually identical to the Greek capital letter delta. However, using characters in this block is preferable for mathematical expressions, as it allows software to distinguish between letters and mathematical symbols. Fonts may use the same glyph to represent different code points in cases like this.

Figure 26-27. The Mathematical Operators block of Unicode

Unicode 3.1.1 adds one more block of mathematical alphanumeric symbols in Plane 1 between 0x1D400 and 0x1D7FF as shown in Figure 26-28. Mostly these are repetitions of the ASCII and Greek letters and digits in what would normally be considered font variations. For instance, 0x1D400 is mathematical bold capital A. The justification for these is that when used in an equation, they really aren't the same characters as the equivalent glyphs in text.

Figure 26-28. The Mathematical Alphanumeric Symbols block of Unicode

26.3.22. Miscellaneous Technical

The Miscellaneous Technical block shown in Figure 26-29 contains an assortment of symbols taken from electronics, quantum mechanics, the APL programming language, the ISO-9995-7 standard for language-neutral keyboard pictograms, and other sources.

Figure 26-29. The Miscellaneous Technical block of Unicode

26.3.23. Optical Character Recognition

The Optical Character Recognition (OCR) block shown in Figure 26-30 includes the OCR-A characters that are not already encoded as ASCII and magnetic-ink character-recognition symbols used on checks.

Figure 26-30. The Optical Character Recognition block of Unicode

26.3.24. Geometric Shapes

The Geometric Shapes block combines simple triangles, squares, circles, and other shapes found in various characters sets Unicode attempts to superset. See Figure 26-31.

Figure 26-31. The Geometric Shapes block of Unicode

26.3.25. Miscellaneous Symbols

The Miscellaneous Symbols block contains mostly pictographic symbols found in vendor and national character sets that preceded Unicode. See Figure 26-32.

Figure 26-32. The Miscellaneous Symbols block of Unicode

26.3.26. Dingbats

The Dingbats block shown in Figure 26-33 is based on characters in the popular Adobe Zapf Dingbats font.

Figure 26-33. The Dingbats block of Unicode

Previous | Home | Next
26.2. HTML4 Entity Sets | Book Index | Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML in a Nutshell: Index

XML in a NutshellSearch this book

Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Symbols & Numbers

- (minus sign), XPath arithmetic operator: 9.6.1. Numbers
* (asterisk)
CSS universal selector: 12.4.1. The Universal Selector
node test: 22.3.3. Node Tests
suffix for element names: 3.2.4. The Number of Children
20.3.2. DTD (Document Type Definition)
XPath arithmetic operator: 9.6.1. Numbers
XPath wildcard: 9.2.5. Wildcards
@* (at-sign asterisk), XPath wildcard: 9.2.5. Wildcards
@ (at sign), selecting an attribute: 9.2.3. Attribute Location Steps
22.3.1. Abbreviated Syntax
: (colon), use within names: 20.3.1. Global Syntax Structures
:: (double colon), axis-node test separator: 9.5. Unabbreviated Location Paths
// (double forward slash), selecting from all descendants: 9.3.2. Selecting from Descendants with //
.. (double period), selecting parent element: 9.3.3. Selecting the Parent Element with ..
22.3.1. Abbreviated Syntax
= (equals sign), CSS attribute value selector: 12.4.3. Attribute Selectors
/ (forward slash)
building compound location paths: 9.3.1. Building Compound Location Paths from Location Steps with /
22.3.1. Abbreviated Syntax
root location path: 9.2.1. The Root Location Path
22.3. Location Paths
( ) parentheses in element declarations: 3.2.6. Parentheses
. (period), selecting context node: 9.3.4. Selecting the Context Node with .
22.3.1. Abbreviated Syntax
+ (plus sign)
CSS sibling element selector: 12.4.2. Matching Descendants, Children, and Siblings
suffix for element names: 3.2.4. The Number of Children
20.3.2. DTD (Document Type Definition)
XPath arithmetic operator: 9.6.1. Numbers
? (question mark), suffix for element names: 3.2.4. The Number of Children
20.3.2. DTD (Document Type Definition)
# (sharp sign), CSS attribute selector: 12.4.3. Attribute Selectors
[] (square brackets), CSS attribute selector: 12.4.3. Attribute Selectors
~= (tilde equals sign), CSS attribute selector: 12.4.3. Attribute Selectors
|= (vertical bar equals sign), CSS attribute selector: 12.4.3. Attribute Selectors
| (vertical bar), multiple XPath matches: 9.2.6. Multiple Matches with |
&amp; (ampersand) entity reference: 2.5. Entity References
3.4. General Entity Declarations
declaring: 20.4.2. Validity Constraints
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets
XML specification for: 20.3.1. Global Syntax Structures
&apos; (apostrophe) entity reference: 2.5. Entity References
3.4. General Entity Declarations
declaring: 20.4.2. Validity Constraints
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets
XML specification for: 20.3.1. Global Syntax Structures
&gt; (greater-than) entity reference: 2.5. Entity References
3.4. General Entity Declarations
declaring: 20.4.2. Validity Constraints
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets
XML specification for: 20.3.1. Global Syntax Structures
&lt; (less-than) entity reference: 2.5. Entity References
3.4. General Entity Declarations
declaring: 20.4.2. Validity Constraints
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets
XML specification for: 20.3.1. Global Syntax Structures
&quot; (straight double quotes) entity reference: 2.5. Entity References
3.4. General Entity Declarations
declaring: 20.4.2. Validity Constraints
HTML 4.0 entity sets and: 26.2. HTML4 Entity Sets
XML specification for: 20.3.1. Global Syntax Structures
> (greater-than sign), CSS child element selector: 12.4.2. Matching Descendants, Children, and Siblings
<? and ?> (processing instruction delimiters): 2.8. Processing Instructions
< (less-than sign), not allowed in attribute values): 20.4.1. Well-Formedness Constraints
<!ATTLIST> declaration: 3.3. Attribute Declarations

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

abbreviated location paths: 9.2. Location Paths
22.3. Location Paths
absolute location paths: 9.2.1. The Root Location Path
22.3. Location Paths
building compound location paths: 9.3.1. Building Compound Location Paths from Location Steps with /
id( ) and: 9.7.1. Node-Set Functions
absolute units of length (CSS properties): 12.6. Pixels, Points, Picas, and Other Units of Length
abstract attribute: 16.9.1. Abstract Elements and Types
active pseudoclass: 12.4.4. Pseudoclass Selectors
actuate attribute (XLink): 10.2.2. xlink:actuate
Adapter design pattern: 25.2. The org.xml.sax.helpers Package
25.2. The org.xml.sax.helpers Package
Adobe Zapf Dingbats font, Unicode block for: 26.3.26. Dingbats
after pseudoelement: 12.4.5. Pseudoelement Selectors
alternate pseudoattribute: 7.2.1.4. The alternate and title pseudoattributes
12.3. Associating Stylesheets with XML Documents
ancestor axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
ancestor-or-self axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
annotation elements in schemas: 16.2.2. Annotations
anonymous types: 16.4. Complex Types
Antenna House XSL Formatter: 13.3.2. Generating the Finished Document
ANY element type: 3.2.9. ANY
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
Apache XML Project
Cocoon (XSLT processor): 8.3. Stylesheet Processors
FOP (formatting program): 13.3.2. Generating the Finished Document
Xalan (XSLT processor): 8.3. Stylesheet Processors
Xerces-J (parser): 2.10. Checking Documents for Well-Formedness
16.2. Schema Basics
APL programming language symbols, Unicode block for: 26.3.22. Miscellaneous Technical
appendChild( ) (DOM): 24.2. Object Reference
appendData( ) (DOM): 24.2. Object Reference
Apple Computer and character sets: 5.7. Platform-Dependent Character Sets
applet module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
application/xml MIME media type: 5.1. Character-Set Metadata
Arabic Unicode block: 26.3.10. Arabic
arcrole attribute (XLink): 10.4.2.3. Arc roles
arcs: 10.4.2. Arcs
multiple arcs from one arc element: 10.4.2.1. Multiple arcs from one arc element
role attributes: 10.4.2.3. Arc roles
title attributes: 10.4.2.2. Arc titles
arithmetic operators in XPath: 9.6.1. Numbers
Armenian Unicode block: 26.3.8. Armenian
Arrows Unicode block: 26.3.20. Arrows
ASCII character set: 26.1.1. ASCII
ATTLIST declaration: 3.3. Attribute Declarations
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
xml:lang attribute and: 5.11.3. ATTLIST Declarations of xml:lang
xmlns attribute and: 4.2.5. Attribute Declarations for xmlns
Attr interface (DOM): 18.4.2.4. Attr
24.2. Object Reference
attributes for: 24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
attribute axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
attribute declarations: 3.3. Attribute Declarations
attribute groups and: 16.2.5.1. Attribute groups
schemas and: 16.2.5. Attribute Declarations
xmlns attribute and: 4.2.5. Attribute Declarations for xmlns
attribute types: 3.3.1. Attribute Types
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
enumeration: 3.3.1.4. Enumeration
20.3.2. DTD (Document Type Definition)
vs. NOTATION: 3.3.1.10. NOTATION
NOTATION: 20.3.2. DTD (Document Type Definition)
attribute values
ensuring validity of: 20.4.2. Validity Constraints
external entities and: 20.4.1. Well-Formedness Constraints
no < allowed in: 20.4.1. Well-Formedness Constraints
templates for: 8.9. Attribute Value Templates
attributeFormDefault attribute (xs:schema element): 16.3.2. Controlling Qualification
ATTRIBUTE_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
attributes
accessing: 25.1. The org.xml.sax Package
default declarations for: 3.3.2. Attribute Defaults
normal attribute values and: 20.4.2. Validity Constraints
default namespaces and: 4.2.4. Setting a Default Namespace with the xmlns Attribute
20.3.4. Namespaces
location steps: 9.2.3. Attribute Location Steps
22.3.1. Abbreviated Syntax
matching multiple: 9.2.6. Multiple Matches with |
nodes
default template for: 8.7.1. The Default Template Rule for Text and Attribute Nodes
XPath data model and: 22.1. The XPath Data Model
notation: 20.4.2. Validity Constraints
selecting: 12.4.3. Attribute Selectors
special: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
unique names for: 20.4.1. Well-Formedness Constraints
vs. child elements: 2.3. Attributes
in XML declarations: 2.9. The XML Declaration
XML elements and: 2.3. Attributes
24.2. Object Reference
XML specification for: 20.2. Annotated Sample Documents
20.3.3. Document Body
attributes attribute (Node interface): 24.2. Object Reference
Attributes interface (SAX): 25.1. The org.xml.sax Package
AttributesImpl class (SAX): 25.2. The org.xml.sax.helpers Package
axes: 9.5. Unabbreviated Location Paths
22.3. Location Paths
22.3.2. Axes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

background-color property (CSS): 12.9. Colors
Backus-Naur Form (BNF) grammar: 23.3. XSLT Functions
bare name XPointers: 11.3. Bare Names
base module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
basic forms module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
basic tables module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
before pseudoelement: 12.4.5. Pseudoelement Selectors
bidirectional text module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
big-endian UCS-2: 5.5.1. UCS-2 and UTF-16
binding prefixes to URIs: 4.2.2. Binding Prefixes to URIs
biztalk.org (DTD repository): 3.10. Locating Standard DTDs
block areas (XSL-FO): 13.1. XSL Formatting Objects
flowing content into pages: 13.3.1. Flowing Content into the Pages
splitting: 13.1. XSL Formatting Objects
block-container element (XSL-FO): 13.3.1. Flowing Content into the Pages
block element (XSL-FO): 13.1. XSL Formatting Objects
13.3.1. Flowing Content into the Pages
block elements (CSS display property): 12.5.2. Block Elements
%Block.extra entity reference: 7.3.2. Mixing Your Applications into XHTML
%Block.mix; entity reference: 7.3.1. Mixing XHTML into Your Applications
BNF (Backus-Naur Form) grammar: 23.3. XSLT Functions
boolean( ) (XPath): 9.7.3. Boolean Functions
22.5. XPath Functions
Booleans
used by SAX features: 19.2. SAX Features and Properties
XPath expressions: 9.6.3. Booleans
22.2. Data Types
XPath functions: 9.7.3. Boolean Functions
Borden, Jonathan: 14. Resource Directory Description Language (RDDL)
border-color property (CSS): 12.9. Colors
border-width property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
Bosak, Jon: 1.4. The Evolution of XML
Bray, Tim: 1.4. The Evolution of XML
14. Resource Directory Description Language (RDDL)
Brown University Scholarly Technology Group: 3.1.4. Validating a Document
browsers (see web browsers)
built-in template rules: 8.2. xsl:stylesheet and xsl:transform
8.7. The Built-in Template Rules
Bullard, Claude L.: 14.1. What's at the End of a Namespace URL?
byte order mark: 5.5.1. UCS-2 and UTF-16

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

C0 controls in ASCII character sets: 26.1.1. ASCII
C1 controls in ISO-8859 character sets: 26.1.2.1. C1 controls
Canonical XML: 1.4. The Evolution of XML
17.2.1. What You Get Is Not What You Saw
Cascading Style Sheets (CSS): 1.4. The Evolution of XML
12. Cascading Style Sheets (CSS)
12. Cascading Style Sheets (CSS)
(see also stylesheets)
color properties: 12.9. Colors
display property: 12.5. The Display Property
font properties: 12.7. Font Properties
selectors: 12.4. Selectors
style rules for: 12.2. CSS Syntax
syntax of: 12.2. CSS Syntax
text properties: 12.8. Text Properties
units of length: 12.6. Pixels, Points, Picas, and Other Units of Length
vs. transformation-based presentation: 6.6. Transformation and Presentation
vs. XSL-FO: 13.5. Choosing Between CSS and XSL-FO
web browser support for: 13.5. Choosing Between CSS and XSL-FO
XML documents and: 12.3. Associating Stylesheets with XML Documents
Cascading Style Sheets Level 1 (CSS1): 7.2. Direct Display of XML in Browsers
12.1. The Three Levels of CSS
case sensitivity in elements: 0.. Conventions Used in This Book
2.2.1.2. Case sensitivity
CDATA sections: 2.6. CDATA Sections
2.6. CDATA Sections
(see also character data)
attribute type: 3.3.1.1. CDATA
20.3.2. DTD (Document Type Definition)
LexicalHandler interface and: 25.4. The org.xml.sax.ext Package
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
CDATASection interface (DOM): 18.4.2.9. CDATASection
24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
CDATA_SECTION_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
ceiling( ) (XPath): 9.7.4. Number Functions
22.5. XPath Functions
character data: 2.2. Elements, Tags, and Character Data
2.7. Comments
(see also CDATA entries)
comments in: 2.7. Comments
parsed: 2.5. Entity References
3.1.1. A Simple DTD Example
character data characters (Unicode): 26.1. Character Tables
character encodings: 5. Internationalization
autodetection of: 20.3.1. Global Syntax Structures
character references: 5.10. Character References
for HTML 4.0 entity sets: 26.2. HTML4 Entity Sets
predefined: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
20.4.1. Well-Formedness Constraints
character sets: 5. Internationalization
26. Character Sets
ASCII: 26.1.1. ASCII
converting between: 5.8. Converting Between Character Sets
Cp1252: 26.1.2.1. C1 controls
ISO: 5.6. ISO Character Sets
metadata: 5.1. Character-Set Metadata
names of: 26. Character Sets
national standard: 5.6. ISO Character Sets
platform-dependent: 5.7. Platform-Dependent Character Sets
Unicode: 5.5. Unicode
XML-defined: 5.4. XML-Defined Character Sets
XML document default: 5.9. The Default Character Set for XML Documents
character tables for Unicode: 26.1. Character Tables
CharacterData interface (DOM): 18.4.2.5. CharacterData
24.2. Object Reference
attributes for: 24.2. Object Reference
methods for: 24.2. Object Reference
characters( ) (SAX): 19.1. The ContentHandler Interface
using filters with: 19.3. Filters
charset pseudoattribute: 7.2.1.3. The charset pseudoattribute
12.3. Associating Stylesheets with XML Documents
child axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
child elements: 2.2.2.1. Parents and children
3.2.2. Child Elements
determining the number of: 3.2.4. The Number of Children
location steps: 9.2.2. Child Element Location Steps
22.3.1. Abbreviated Syntax
in narrative-oriented documents: 6.2. Narrative Document Structures
vs. attributes: 2.3. Attributes
childNodes attribute (Node interface): 24.2. Object Reference
Chinese characters
using subcodes for dialects: 5.11.2. Subcodes
UTF-8/UTF-16 encodings: 5.5.2. UTF-8
26. Character Sets
choices in element declarations: 3.2.5. Choices
Clark, James: 1.4. The Evolution of XML
26. Character Sets
client-side image map module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
cloneNode( ) (DOM): 24.2. Object Reference
Cocoon (Apache XML Project XSLT processor): 8.3. Stylesheet Processors
code points: 5. Internationalization
unassigned: 26.1. Character Tables
code samples and CDATA sections: 2.6. CDATA Sections
color property (CSS): 12.9. Colors
command-line XSLT processors: 8.3.1. Command-Line Processors
comment( )
location step: 9.2.4. The comment( ), text(), and processing-instruction( ) Location Steps
node test: 22.3.3. Node Tests
Comment interface (DOM): 18.4.2.6. Comment
24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
comment nodes
default template for: 8.7.3. The Default Template Rule for Comment and Processing Instruction Nodes
XPath data model and: 22.1. The XPath Data Model
COMMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
comments
IGNORE directive acting as: 3.8. Conditional Inclusion
LexicalHandler interface and: 25.4. The org.xml.sax.ext Package
loopholes in XML specification: 17.2.1. What You Get Is Not What You Saw
vs. annotations in schemas: 16.2.2. Annotations
in XML documents: 2.7. Comments
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
communications protocols and XML: 15.1.2. Communications
comparison operators in XPath: 9.6.2. Strings
complex types: 16.4. Complex Types
attribute groups and: 16.2.5.1. Attribute groups
deriving
by extension: 16.8.2.1. Deriving by extension
by restriction: 16.8.2.2. Deriving by restriction
final attribute and: 16.9.2. The Final Attribute
occurrence constraints: 16.4.1. Occurrence Constraints
xs:complexContent element and: 16.5.1. complexContent
concat( ) (XPath): 9.7. XPath Functions
22.5. XPath Functions
conditional sections: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
parameter entity nesting and: 20.4.2. Validity Constraints
constrained child nodes: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
constraints
namespace: 20.4.3. Namespace Constraints
validity: 20.4.2. Validity Constraints
well-formedness: 20.4.1. Well-Formedness Constraints
container nodes: 11.6. Points
contains( ) (XPath): 9.7.2. String Functions
22.5. XPath Functions
content, element
any type: 16.8. Allowing Any Content
empty elements: 16.5. Empty Elements
mixed: 16.7. Mixed Content
simple: 16.6. Simple Content
content nodes (DOM Core): 18.4.2. Content Nodes
content specifications in element declarations: 3.2. Element Declarations
ContentHandler interface (SAX): 19.1. The ContentHandler Interface
25.1. The org.xml.sax Package
methods called by parser: 19.1. The ContentHandler Interface
overriding methods in: 19.1. The ContentHandler Interface
vs. java.net.ContentHandler class: 19.1. The ContentHandler Interface
context node: 9.2. Location Paths
22.3.2. Axes
attribute location steps and: 9.2.3. Attribute Location Steps
changing, using here( ) and origin(): 11.7.5. Relative XPointers
child element location steps and: 9.2.2. Child Element Location Steps
compound location paths and: 9.3. Compound Location Paths
selecting, using a period (.): 9.3.4. Selecting the Context Node with .
22.3.1. Abbreviated Syntax
unabbreviated location paths: 9.5. Unabbreviated Location Paths
Coptic Unicode block: 26.3.6. Greek and Coptic
count( ) (XPath): 9.7.1. Node-Set Functions
22.5. XPath Functions
Cover, Robin: 3.10. Locating Standard DTDs
Cp1252 character set: 5.7.1. Cp1252
26.1.2.1. C1 controls
createAttribute( ) (DOM): 24.2. Object Reference
createAttributeNS( ) (DOM): 24.2. Object Reference
createCDATASection( ) (DOM): 24.2. Object Reference
createComment( ) (DOM): 24.2. Object Reference
createDocument( ) (DOM): 24.2. Object Reference
createDocumentFragment( ) (DOM): 24.2. Object Reference
createDocumentType( ) (DOM): 24.2. Object Reference
createElement( ) (DOM): 24.2. Object Reference
createElementNS( ) (DOM): 24.2. Object Reference
createEntityReference( ) (DOM): 24.2. Object Reference
createProcessingInstruction( ) (DOM): 24.2. Object Reference
createTextNode( ) (DOM): 24.2. Object Reference
createXMLReader( ) (SAX): 19. Simple API for XML (SAX)
25.2. The org.xml.sax.helpers Package
SAX filters and: 19.3. Filters
CSS (see Cascading Style Sheets)
CSS1 (Cascading Style Sheets Level 1): 7.2. Direct Display of XML in Browsers
12.1. The Three Levels of CSS
Currency Symbols Unicode block: 26.3.18. Currency Symbols
current( ) (XSLT): 23.3. XSLT Functions
Cyrillic Unicode block: 26.3.7. Cyrillic

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

data attribute
CharacterData interface: 24.2. Object Reference
ProcessingInstruction interface: 24.2. Object Reference
data format, XML: 1.2. Portable Data
15. XML as a Data Format
data-oriented DTDs: 3.9.1. Data-Oriented DTDs
databases and XML: 1.1.1. What XML Is Not
15.1.4. Data Storage/Retrieval
decimal character references: 5.10. Character References
decimal format patterns: 23.2. XSLT Elements
23.3. XSLT Functions
declarations
attribute: 3.3. Attribute Declarations
commenting out: 3.8. Conditional Inclusion
DeclHandler interface and: 25.4. The org.xml.sax.ext Package
default: 3.3.2. Attribute Defaults
20.4.2. Validity Constraints
document type: 3.1.2. The Document Type Declaration
element (see element declarations)
element type (see element type declarations)
encoding: 5.2. The Encoding Declaration
20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
external
including: 16.8.1.1. Including external declarations
modifying: 16.8.1.2. Modifying external declarations
general entity: 3.4. General Entity Declarations
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
notation: 3.6. External Unparsed Entities and Notations
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
names must be unique: 20.4.2. Validity Constraints
unparsed entities and: 20.4.2. Validity Constraints
standalone: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
text (see text declarations)
DeclHandler interface (SAX): 25.4. The org.xml.sax.ext Package
default declarations for attributes: 3.3.2. Attribute Defaults
XML specification for: 20.4.2. Validity Constraints
default namespaces, setting: 4.2.4. Setting a Default Namespace with the xmlns Attribute
20.3.4. Namespaces
default templates for XML document nodes: 8.7.1. The Default Template Rule for Text and Attribute Nodes
default values, XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
DefaultHandler class (SAX): 19.1. The ContentHandler Interface
25.2. The org.xml.sax.helpers Package
deleteData( ) (DOM): 24.2. Object Reference
derived types: 16.2.5. Attribute Declarations
controlling: 16.9. Controlling Type Derivation
deriving
by extension: 16.8.2.1. Deriving by extension
by restriction: 16.8.2.2. Deriving by restriction
using: 16.8.2.3. Using derived types
xs:complexContent element and: 16.5.1. complexContent
descendant axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
descendant-or-self axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
Devanagari Unicode block: 26.3.11. Devanagari
Dingbats Unicode block: 26.3.26. Dingbats
display property (CSS): 12.5. The Display Property
div (XPath arithmetic operator): 9.6.1. Numbers
DocBook: 6.4. DocBook
entity sets and: 5.10. Character References
doctype attribute (Document interface): 24.2. Object Reference
DOCTYPE declaration: 3.1.2. The Document Type Declaration
20.4.2. Validity Constraints
document element (see root element)
Document interface (DOM): 18.4.2.1. Document
24.2. Object Reference
attributes for: 24.2. Object Reference
methods for: 24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
document model modules, defining parameter entity references with: 7.3.3. Mixing Your Own XHTML
Document Object Model (see DOM)
Document Style Sheet and Semantics Language (DSSSL): 1.4. The Evolution of XML
6.6. Transformation and Presentation
document type declaration: 3.1.2. The Document Type Declaration
20.4.2. Validity Constraints
document type definitions (see DTDs)
document( ) (XSLT): 23.3. XSLT Functions
documentElement attribute (Document interface): 24.2. Object Reference
DocumentFragment interface (DOM): 18.4.2.2. DocumentFragment
24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
DOCUMENT_FRAGMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
DOCUMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
documents, XML (see XML, documents)
DocumentType interface (DOM): 18.4.1.1. DocumentType
24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
DOCUMENT_TYPE_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
DOM Core: 24. DOM Reference
content nodes: 18.4.2. Content Nodes
structural nodes: 18.4.1. Structural Nodes
structure of: 18.2. Structure of the DOM Core
DOM (Document Object Model): 0.. What This Book Covers
18. Document Object Model (DOM)
document structure: 18.3.3. Relating Document Structure to Nodes
generic vs. specific interfaces: 18.2.1. Generic Versus Specific DOM Interfaces
IDL (Interface Definition Language) notation and: 18.1.1. DOM Notation
Level 1 vs. Level 2: 18.1. DOM Foundations
24. DOM Reference
Node interface: 24. DOM Reference
object hierarchy: 24.1. Object Hierarchy
parsing a document with: 18.6. Parsing a Document with DOM
reference: 24. DOM Reference
sample application: 18.7. A Simple DOM Application
strengths/weaknesses: 18.1.2. DOM Strengths and Weaknesses
TrAX and: 23.4. TrAX
DOM Level 3: 1.4. The Evolution of XML
18. Document Object Model (DOM)
18.6. Parsing a Document with DOM
DOMException interface: 24.2. Object Reference
DOMImplementation interface: 18.4.2.1. Document
18.5. The DOMImplementation Interface
methods for: 24.2. Object Reference
DOMSTRING_SIZE_ERR exception (DOM): 24.2. Object Reference
DSSSL (Document Style Sheet and Semantics Language): 1.4. The Evolution of XML
6.6. Transformation and Presentation
DTDHandler interface (SAX): 25.1. The org.xml.sax Package
DTDs: 1.1. The Benefits of XML
3. Document Type Definitions (DTDs)
attribute declarations: 3.3. Attribute Declarations
data-oriented: 3.9.1. Data-Oriented DTDs
defining general entity references: 5.10. Character References
document permanence and: 6.5. Document Permanence
element declarations: 3.2. Element Declarations
external subsets (see external DTD subsets)
finding standard: 3.10. Locating Standard DTDs
general entities: 3.5. External Parsed General Entities
general entity declarations: 3.4. General Entity Declarations
internal subsets (see internal DTD subsets)
namespaces and: 4.4. Namespaces and DTDs
narrative-oriented: 3.9.2. Narrative-Oriented DTDs
6.2. Narrative Document Structures
parameter entities: 3.7. Parameter Entities
rddl:resource elements and: 14.2. RDDL Syntax
repository of: 3.10. Locating Standard DTDs
specifying rules using: 1.3. How XML Works
standalone attribute and: 2.9.2. standalone
TEI (Text Encoding Initiative): 6.3. TEI
validating XML documents: 3.1. Validation
16.1.1. Schemas Versus DTDs
for XHTML: 7.1.2. Three DTDs for XHTML
for XLinks: 10.6. DTDs for XLinks
XML specification for: 20.3.2. DTD (Document Type Definition)
Dublin Core: 7.4.2. Dublin Core
RDF (Resource Description Framework) and: 4.1. The Need for Namespaces
Dynamic HTML: The Definitive Reference: 24.2. Object Reference

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

EBNF (Extended Backus-Naur Form) grammar: 20.5. XML Document Grammar
edit module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
electronics symbols, Unicode block for: 26.3.22. Miscellaneous Technical
element-available( ) (XSLT): 23.3. XSLT Functions
element content
any type: 16.8. Allowing Any Content
empty elements: 16.5. Empty Elements
mixed: 16.7. Mixed Content
simple: 16.6. Simple Content
ELEMENT declaration: 3.2. Element Declarations
element declarations: 3.1.1. A Simple DTD Example
3.2. Element Declarations
abstract attribute: 16.9.1. Abstract Elements and Types
complex types and: 16.4. Complex Types
constructing: 20.4.2. Validity Constraints
matching elements with DTD rules: 20.4.2. Validity Constraints
occurrence constraints: 16.4.1. Occurrence Constraints
schemas and: 16.2.1. Document Organization
16.2.3. Element Declarations
substitutionGroup attribute: 16.8.3. Substitution Groups
Element interface (DOM): 18.4.2.3. Element
attribute for: 24.2. Object Reference
methods for: 24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
element names
matching: 20.4.1. Well-Formedness Constraints
in namespaces and DTDs: 4.4. Namespaces and DTDs
prefixes and: 4.2.1. Qualified Names, Prefixes, and Local Parts
qualified: 20.3.4. Namespaces
suffixes for: 3.2.4. The Number of Children
unqualified: 20.3.4. Namespaces
using choices in: 3.2.5. Choices
using namespaces to resolve conflicts: 4.1. The Need for Namespaces
element nodes
default template for: 8.7.2. The Default Template Rule for Element and Root Nodes
XPath data model and: 22.1. The XPath Data Model
element type declarations
duplicates types not allowed: 20.4.2. Validity Constraints
uniqueness required for: 20.4.2. Validity Constraints
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
elementFormDefault attribute (xs:schema element): 16.3.2. Controlling Qualification
ELEMENT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
elements
attributes and: 2.3. Attributes
24.2. Object Reference
calculating the value of: 8.5. Calculating the Value of an Element with xsl:value-of
case sensitivity in: 0.. Conventions Used in This Book
2.2.1.2. Case sensitivity
complex types: 16.4. Complex Types
controlling placement of: 16.7.2. Controlling Element Placement
documents and: 2.2. Elements, Tags, and Character Data
empty (see empty elements)
enforcing uniqueness: 16.9.4. Uniqueness and Keys
entity references in: 2.5. Entity References
location steps: 9.2.2. Child Element Location Steps
22.3. Location Paths
matching multiple: 9.2.6. Multiple Matches with |
matching names: 20.4.1. Well-Formedness Constraints
minOccurs/maxOccurs attributes: 16.4.1. Occurrence Constraints
mixed content of: 2.2.3. Mixed Content
in narrative-oriented documents: 6.2. Narrative Document Structures
overlapping not allowed: 2.2.2.1. Parents and children
of schemas: 21.2. Schema Elements
XML specification for: 20.3.3. Document Body
empty elements: 2.2.1.1. Empty elements
in element declarations: 3.2.8. Empty Elements
20.3.2. DTD (Document Type Definition)
NOTATION attributes not available for: 20.4.2. Validity Constraints
schemas and: 16.5. Empty Elements
web browser problems with: 7.1.3.2. Empty elements
XML specification for: 20.2. Annotated Sample Documents
20.3.3. Document Body
encoding declaration: 2.9.1. encoding
5.2. The Encoding Declaration
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
encodings, list of: 5.4. XML-Defined Character Sets
Encryption, XML: 1.4. The Evolution of XML
end-point( ): 11.6. Points
end-tags: 2.2. Elements, Tags, and Character Data
XML specification for: 20.2. Annotated Sample Documents
20.3.3. Document Body
endDocument( ) (SAX): 19.1. The ContentHandler Interface
endElement( ) (SAX): 19.1. The ContentHandler Interface
using filters with: 19.3. Filters
ending resources: 10.1. Simple Links
endPrefixMapping( ) (SAX): 19.1. The ContentHandler Interface
enterprise applications and XML: 15.1.1. Mixed Environments
entities
external parsed: 3.5. External Parsed General Entities
text declarations and: 5.3. Text Declarations
external unparsed: 3.6. External Unparsed Entities and Notations
declaring notations: 20.4.2. Validity Constraints
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
parameter (see parameter entity references)
entities attribute (DocumentType interface): 24.2. Object Reference
ENTITIES attribute type: 3.3.1.9. ENTITIES
20.3.2. DTD (Document Type Definition)
ENTITY attribute type: 3.3.1.8. ENTITY
20.3.2. DTD (Document Type Definition)
embedding unparsed entities in documents: 3.6.2. Embedding Unparsed Entities in Documents
unparsed entities in DTD and: 20.4.2. Validity Constraints
ENTITY declaration: 3.4. General Entity Declarations
20.3.1. Global Syntax Structures
absent from entity references: 20.4.1. Well-Formedness Constraints
Entity interface (DOM): 18.4.1.4. Entity
24.2. Object Reference
attributes for: 24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
entity references: 2.5. Entity References
2.5. Entity References
3.4. General Entity Declarations
attribute values and external entities: 20.4.1. Well-Formedness Constraints
LexicalHandler interface and: 25.4. The org.xml.sax.ext Package
loopholes in XML specification: 17.2.1. What You Get Is Not What You Saw
simplifying document parsing: 20.3.1. Global Syntax Structures
unparsed entity names not allowed: 20.4.1. Well-Formedness Constraints
web browser problems with: 7.1.3.3. Entity references
with no entity declarations: 20.4.1. Well-Formedness Constraints
XHTML 1.1 DTD: 7.3.1. Mixing XHTML into Your Applications
XML specification for: 20.3.1. Global Syntax Structures
entity sets
HTML 4.0: 26.2. HTML4 Entity Sets
XHTML 1.0 DTD: 5.10. Character References
26.2. HTML4 Entity Sets
ENTITY_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
EntityReference interface (DOM): 18.4.2.7. EntityReference
24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
ENTITY_REFERENCE_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
EntityResolver interface (SAX): 25.1. The org.xml.sax Package
enumeration attribute type: 3.3.1.4. Enumeration
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
assigning a value to: 20.4.2. Validity Constraints
vs. NOTATION attribute type: 3.3.1.10. NOTATION
enumeration facet element: 16.6.2.3. Enumerations
21.2. Schema Elements
ErrorHandler interface (SAX): 25.1. The org.xml.sax Package
errors
ErrorHandler interface (SAX): 25.1. The org.xml.sax Package
SAXException class and: 25.1. The org.xml.sax Package
validity: 1.3. How XML Works
3. Document Type Definitions (DTDs)
3.1.4. Validating a Document
well-formedness: 1.3. How XML Works
2.10. Checking Documents for Well-Formedness
3. Document Type Definitions (DTDs)
escape sequences: 2.5. Entity References
EUC-JP encoding: 5.4. XML-Defined Character Sets
26. Character Sets
event-based parsers: 17.1.2. Treating XML as Events
Examplotron schema language: 1.1. The Benefits of XML
ExceptionCodes (DOM): 24.2. Object Reference
exceptions (see errors)
expat parser and support for character encodings: 26. Character Sets
Extended Backus-Naur Form (EBNF) grammar: 20.5. XML Document Grammar
Extended Interfaces (DOM Core): 18.2. Structure of the DOM Core
extended links: 10.4. Extended Links
arc elements: 10.4.2. Arcs
local resources: 10.4.3. Local Resources
locator elements: 10.4.1. Locators
title elements: 10.4.4. Title Elements
XPointers and: 11.2. XPointers in Links
Extensible Linking Language (XLink): 0.. What This Book Covers
1.4. The Evolution of XML
Extensible Stylesheet Language Transformations (see XSLT)
Extensible Stylesheet Language (XSL): 1.4. The Evolution of XML
extent attributes (XSL-FO): 13.3. Laying Out the Master Pages
external declarations
including: 16.8.1.1. Including external declarations
modifying: 16.8.1.2. Modifying external declarations
external DTD subsets: 3.1.3. Internal DTD Subsets
general entity references and: 5.10. Character References
parameter entity references and: 3.7.1. Parameter Entity Syntax
text declarations and: 5.3. Text Declarations
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
20.4.1. Well-Formedness Constraints
external general entities: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
parsed: 3.5. External Parsed General Entities
attribute values and: 20.4.1. Well-Formedness Constraints
no recursion allowed: 20.4.1. Well-Formedness Constraints
SAX core feature: 25.3. SAX Features and Properties
text declarations and: 5.3. Text Declarations
unparsed: 3.6. External Unparsed Entities and Notations
20.3.2. DTD (Document Type Definition)
declaring notations: 20.4.2. Validity Constraints
external parameter entity references: 3.7.3. External DTD Subsets
SAX core feature: 25.3. SAX Features and Properties

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

facet elements: 21.2. Schema Elements
enumeration: 16.6.2.3. Enumerations
21.2. Schema Elements
fractionDigits: 16.6.2.4.2. Length and precision
21.2. Schema Elements
length: 16.6.2.2. Restricting length
21.2. Schema Elements
maxExclusive: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
maxInclusive: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
maxLength: 16.6.2.2. Restricting length
21.2. Schema Elements
minExclusive: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
minInclusive: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
minLength: 16.6.2.2. Restricting length
21.2. Schema Elements
pattern: 16.6.2.5. Enforcing format
21.2. Schema Elements
totalDigits: 16.6.2.4.2. Length and precision
21.2. Schema Elements
whiteSpace: 16.6.2.1. Handling whitespace
21.2. Schema Elements
facets: 16.6.2. Facets
false( ) (XPath): 9.7.3. Boolean Functions
22.5. XPath Functions
Feynman, 30Richard: 8.1. An Example Input Document
filters
event-based parsers and: 17.1.2. Treating XML as Events
SAX: 19.3. Filters
final attribute: 16.9.2. The Final Attribute
first-child pseudoclass: 12.4.4. Pseudoclass Selectors
first-letter pseudoelement: 12.4.5. Pseudoelement Selectors
first-line pseudoelement: 12.4.5. Pseudoelement Selectors
firstChild attribute (Node interface): 24.2. Object Reference
fixed attribute: 16.9.3. Setting fixed Facets
#FIXED default declaration: 3.3.2. Attribute Defaults
20.3.2. DTD (Document Type Definition)
explicit value must match default value: 20.4.2. Validity Constraints
floor( ) (XPath): 9.7.4. Number Functions
22.5. XPath Functions
flow child element (XSL-FO): 13.3.1. Flowing Content into the Pages
%Flow.mix; entity reference: 7.3.1. Mixing XHTML into Your Applications
fo:block-container element: 13.3.1. Flowing Content into the Pages
fo:block element: 13.1. XSL Formatting Objects
13.3.1. Flowing Content into the Pages
focus pseudoclass: 12.4.4. Pseudoclass Selectors
fo:flow child element: 13.3.1. Flowing Content into the Pages
fo:layout-master-set element: 13.2. The Structure of an XSL-FO Document
13.3. Laying Out the Master Pages
fo:list-block element: 13.3.1. Flowing Content into the Pages
following axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
following-sibling axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
font-family property (CSS): 12.7. Font Properties
font properties (CSS): 12.7. Font Properties
font-size property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
font-stretch property (CSS): 12.7. Font Properties
font-style property (CSS): 12.7. Font Properties
font-variant property (CSS): 12.7. Font Properties
font-weight property (CSS): 12.7. Font Properties
FOP (Apache XML Project formatting program): 13.3.2. Generating the Finished Document
fo:page-sequence element: 13.2. The Structure of an XSL-FO Document
flowing content into pages: 13.3.1. Flowing Content into the Pages
fo:region-after child element: 13.3. Laying Out the Master Pages
fo:region-before child element: 13.3. Laying Out the Master Pages
fo:region-body child element: 13.3. Laying Out the Master Pages
fo:region-end child element: 13.3. Laying Out the Master Pages
fo:region-start child element: 13.3. Laying Out the Master Pages
format-number( ) (XSLT): 9.7.2. String Functions
23.2. XSLT Elements
23.3. XSLT Functions
formatting objects (see XSL-FO)
forms module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
fo:root element: 13.2. The Structure of an XSL-FO Document
fo:simple-page-master element: 13.3. Laying Out the Master Pages
fo:table-and-caption element: 13.3.1. Flowing Content into the Pages
fo:table element: 13.3.1. Flowing Content into the Pages
fractionDigits facet element: 16.6.2.4.2. Length and precision
21.2. Schema Elements
fragment identifiers: 11.1. XPointers on URLs
frames module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
frameset DTD (XHTML): 7.1.2. Three DTDs for XHTML
from attribute (XLink): 10.4.2. Arcs
function-available( ) (XSLT): 23.3. XSLT Functions
functions
XPath: 9.7. XPath Functions
22.5. XPath Functions
XSLT: 23.3. XSLT Functions
Fundamental Interfaces (DOM Core): 18.2. Structure of the DOM Core

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

Galazka, Wojciech: 5.8. Converting Between Character Sets
general entities
external parsed: 3.5. External Parsed General Entities
attribute values and: 20.4.1. Well-Formedness Constraints
no recursion allowed: 20.4.1. Well-Formedness Constraints
text declarations and: 5.3. Text Declarations
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
external unparsed: 3.6. External Unparsed Entities and Notations
20.3.2. DTD (Document Type Definition)
declaring notations: 20.4.2. Validity Constraints
internal: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
general entity declarations: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
declaring: 20.4.2. Validity Constraints
general entity references: 3.4. General Entity Declarations
(see also entity references)
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
General Punctuation Unicode block: 26.3.17. General Punctuation
generate-id( ) (XSLT): 23.3. XSLT Functions
Geometric Shapes Unicode block: 26.3.24. Geometric Shapes
getAttribute( ) (DOM): 24.2. Object Reference
getAttributeNode( ) (DOM): 24.2. Object Reference
getAttributeNodeNS( ) (DOM): 24.2. Object Reference
getAttributeNS( ) (DOM): 24.2. Object Reference
getElementById( ) (DOM): 18.4.2.1. Document
24.2. Object Reference
getElementsByTagName( ) (DOM): 18.4.2.1. Document
Document interface: 24.2. Object Reference
Element interface: 24.2. Object Reference
getElementsByTagNameNS( ) (DOM): 18.4.2.1. Document
Document interface: 24.2. Object Reference
Element interface: 24.2. Object Reference
getException( ) (SAX): 25.1. The org.xml.sax Package
getFeature( ) (SAX): 19.2. SAX Features and Properties
getNamedItem( ) (DOM): 24.2. Object Reference
getNamedItemNS( ) (DOM): 24.2. Object Reference
getProperty( ) (SAX): 19.2. SAX Features and Properties
glyph areas (XSL-FO): 13.1. XSL Formatting Objects
Goldfarb, Charles F.: 1.4. The Evolution of XML
6.1. SGML's Legacy
Goodman, Danny: 24.2. Object Reference
grammar for XML documents: 20.5. XML Document Grammar
Greek Extended Unicode block: 26.3.16. Greek Extended
Greek Unicode block: 26.3.6. Greek and Coptic

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

hasAttribute( ) (DOM)
Element interface: 24.2. Object Reference
hasAttributeNS( ) (DOM)
Element interface: 24.2. Object Reference
hasAttributes( ) (DOM): 24.2. Object Reference
hasChildNodes( ) (DOM): 24.2. Object Reference
hasFeature( ) (DOM): 18.5. The DOMImplementation Interface
24.2. Object Reference
head elements: 16.8.3. Substitution Groups
Hebrew Unicode block: 26.3.9. Hebrew
height property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
here( ): 11.7.6. here( )
hexadecimal character references: 5.10. Character References
hidden elements (CSS display property): 12.5.4. Hidden Elements
HIERARCHY_REQUEST_ERR exception (DOM): 24.2. Object Reference
Hook schema language: 1.1. The Benefits of XML
hover pseudoclass: 12.4.4. Pseudoclass Selectors
href attribute (Xlink): 10.1. Simple Links
href pseudoattribute: 7.2.1.1. The required href and type pseudoattributes
12.3. Associating Stylesheets with XML Documents
HTML
example of a standard document: 7.1.1. Moving from HTML to XHTML
moving to XHTML from: 7.1.1. Moving from HTML to XHTML
SGML and: 1.4. The Evolution of XML
structure of documents: 6.2. Narrative Document Structures
vs. XHTML: 7.1. XHTML
HTML 4.0 entity sets: 26.2. HTML4 Entity Sets
hypertext module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

IANA (Internet Assigned Numbers Authority): 5.4. XML-Defined Character Sets
7.2.1.1. The required href and type pseudoattributes
IBM and character sets: 5.7. Platform-Dependent Character Sets
ID attribute type: 3.3.1.5. ID
bare name XPointers and: 11.3. Bare Names
default for: 20.4.2. Validity Constraints
in element type declarations: 20.3.2. DTD (Document Type Definition)
only one per element type: 20.4.2. Validity Constraints
values must be unique: 20.4.2. Validity Constraints
id( ) (XPath): 9.7.1. Node-Set Functions
22.5. XPath Functions
bare name XPointers and: 11.3. Bare Names
IDL (Interface Definition Language) and DOM: 18.1.1. DOM Notation
24. DOM Reference
IDREF attribute type: 3.3.1.6. IDREF
in element type declarations: 20.3.2. DTD (Document Type Definition)
matching IDs in document: 20.4.2. Validity Constraints
IDREFS attribute type: 3.3.1.7. IDREFS
in element type declarations: 20.3.2. DTD (Document Type Definition)
IE (see Internet Explorer)
iframe module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
ignorableWhitespace( ) (SAX): 19.1. The ContentHandler Interface
IGNORE directive: 3.8. Conditional Inclusion
illegal characters (Unicode): 26.1. Character Tables
image module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
implementation attribute (Document interface): 24.2. Object Reference
#IMPLIED default declaration: 3.3.2. Attribute Defaults
20.3.2. DTD (Document Type Definition)
importNode( ) (DOM): 24.2. Object Reference
inbound links: 10.5. Linkbases
INCLUDE directive: 3.8. Conditional Inclusion
INDEX_SIZE_ERR exception (DOM): 24.2. Object Reference
Indian languages, Unicode block for: 26.3.11. Devanagari
inline areas (XSL-FO): 13.1. XSL Formatting Objects
inline elements (CSS display property): 12.5.1. Inline Elements
inline-table value (display property): 12.5.5. Table Elements
%Inline.extra entity reference: 7.3.2. Mixing Your Applications into XHTML
%Inline.mix; entity reference: 7.3.1. Mixing XHTML into Your Applications
input markup languages: 6.6. Transformation and Presentation
InputSource class (SAX): 25.1. The org.xml.sax Package
insertBefore( ) (DOM): 24.2. Object Reference
insertData( ) (DOM): 24.2. Object Reference
instance documents: 16.1. Overview
abstract attribute and: 16.9.1. Abstract Elements and Types
attributes of: 21.4. Instance Document Attributes
complex types and: 16.4. Complex Types
controlling namespace qualification: 16.3.2. Controlling Qualification
derived types in: 16.8.2.3. Using derived types
element declarations and: 16.2.3. Element Declarations
empty elements and: 16.5. Empty Elements
namespace issues with: 16.1.2. Namespace Issues
substitution groups and: 16.8.3. Substitution Groups
Interface Definition Language (IDL) and DOM: 18.1.1. DOM Notation
24. DOM Reference
internal DTD subsets: 3.1.3. Internal DTD Subsets
general entity references and: 5.10. Character References
parameter entity references and: 3.7.1. Parameter Entity Syntax
20.4.1. Well-Formedness Constraints
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
internal general entities: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
internalSubset attribute (DocumentType interface): 24.2. Object Reference
International Phonetic Alphabet (IPA) Extensions Unicode block: 26.3.3. IPA Extensions
internationalization: 5. Internationalization
Internet Assigned Numbers Authority (IANA): 5.4. XML-Defined Character Sets
7.2.1.1. The required href and type pseudoattributes
Internet Explorer (IE)
displaying XML in web browsers: 7.2.2. Internet Explorer
lack of support for XSLT: 8.2. xsl:stylesheet and xsl:transform
intrinsic events module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
INUSE_ATTRIBUTE_ERR exception (DOM): 24.2. Object Reference
INVALID_ACCESS_ERR exception (DOM): 24.2. Object Reference
INVALID_CHARACTER_ERR exception (DOM): 24.2. Object Reference
INVALID_MODIFICATION_ERR exception (DOM): 24.2. Object Reference
INVALID_STATE_ERR exception (DOM): 24.2. Object Reference
IPA (International Phonetic Alphabet) Extensions Unicode block: 26.3.3. IPA Extensions
ISO-8859-14 encoding: 5.6. ISO Character Sets
26. Character Sets
ISO-8859 character sets: 26.1.2. ISO-8859-1, Latin-1
ISO-3166 (Codes for the Representation of Names of Countries): 5.11.2. Subcodes
ISO-639 (Codes for the Representation of Names of Languages): 5.11.1. Language Codes
ISO-2022-JP encoding: 5.4. XML-Defined Character Sets
26. Character Sets
ISO-10646-UCS-4 encoding: 5.4. XML-Defined Character Sets
26. Character Sets
ISO character sets: 5.6. ISO Character Sets
isSupported( ) (DOM): 24.2. Object Reference
item( ) (DOM): 24.2. Object Reference
24.2. Object Reference

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

Japanese characters
national standard character sets for: 26. Character Sets
UTF-8/UTF-16 encodings: 5.5.2. UTF-8
26. Character Sets
Java
examples/bindings for DOM Core objects: 24.2. Object Reference
language-specific bindings for: 18.1.1. DOM Notation
24. DOM Reference
TrAX and: 23.4. TrAX
Java Development Kit character set conversion tool: 5.8. Converting Between Character Sets
javax.xml.transform package: 23.4. TrAX
Jelliffe, Rick: 14.1. What's at the End of a Namespace URL?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: K

Kay, Michael: 8.3. Stylesheet Processors
key( ) (XSLT): 23.3. XSLT Functions
keys and references in schemas: 16.9.4.2. Keys and references
Korean characters
national standard character set for: 5.6. ISO Character Sets
UTF-8/UTF-16 encodings: 5.5.2. UTF-8
26. Character Sets

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

label attribute (Xlink): 10.4.1. Locators
local resources and: 10.4.3. Local Resources
lang pseudoclass: 12.4.4. Pseudoclass Selectors
lang( ) (XPath): 22.5. XPath Functions
language codes: 5.11.1. Language Codes
last( ) (XPath): 9.7.1. Node-Set Functions
22.5. XPath Functions
lastChild attribute (Node interface): 24.2. Object Reference
Latin-1 characters entity set: 5.10. Character References
26.2. HTML4 Entity Sets
Latin-6 encoding: 5.6. ISO Character Sets
26. Character Sets
Latin Extended-A Unicode block: 26.3.1. Latin Extended-A
Latin Extended Additional Unicode block: 26.3.15. Latin Extended Additional
Latin Extended-B Unicode block: 26.3.2. Latin Extended-B
layout-master-set element (XSL-FO): 13.2. The Structure of an XSL-FO Document
13.3. Laying Out the Master Pages
left property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
legacy encodings: 5.4. XML-Defined Character Sets
legacy module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
length attribute
CharacterData interface: 24.2. Object Reference
NamedNodeMap interface: 24.2. Object Reference
NodeList interface: 24.2. Object Reference
length facet element: 16.6.2.2. Restricting length
21.2. Schema Elements
length, units of: 12.6. Pixels, Points, Picas, and Other Units of Length
Letter-like Symbols Unicode block: 26.3.19. Letter-Like Symbols
LexicalHandler interface (SAX): 25.4. The org.xml.sax.ext Package
line areas (XSL-FO): 13.1. XSL Formatting Objects
line-height property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
link module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
link pseudoclass: 12.4.4. Pseudoclass Selectors
linkbases: 10.5. Linkbases
links: 17.2.6. Links and References
(see also XLinks)
behavior of: 10.2. Link Behavior
extended (see extended links)
following: 10.2.2. xlink:actuate
inbound/outbound: 10.5. Linkbases
semantics of: 10.3. Link Semantics
simple: 10.1. Simple Links
third-party: 10.5. Linkbases
XML processing issues: 17.2.6. Links and References
XPointers in: 11.2. XPointers in Links
list-block element (XSL-FO): 13.3.1. Flowing Content into the Pages
list elements (CSS display property): 12.5.3. List Elements
list-item value (display property): 12.5.3. List Elements
list module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
list-style-image value (display property): 12.5.3. List Elements
list-style-position value (display property): 12.5.3. List Elements
list-style-type value (display property): 12.5.3. List Elements
literal data characters: 8.4. Templates and Template Rules
literal default declaration: 3.3.2. Attribute Defaults
Literal Result Element as Stylesheet (XSLT 1.0 specification): 8.2. xsl:stylesheet and xsl:transform
literal result elements: 8.4. Templates and Template Rules
attribute value templates and: 8.9. Attribute Value Templates
namespace syntax and: 4.2.1. Qualified Names, Prefixes, and Local Parts
xsl:apply-templates and: 8.6. Applying Templates with xsl:apply-templates
little-endian UCS-2: 5.5.1. UCS-2 and UTF-16
local-name( ) (XPath): 9.7.1. Node-Set Functions
22.5. XPath Functions
local parts: 4.2.1. Qualified Names, Prefixes, and Local Parts
local resources (extended links): 10.4. Extended Links
10.4.3. Local Resources
localName attribute (Node interface): 24.2. Object Reference
location paths: 9.2. Location Paths
22.3. Location Paths
axes: 9.5. Unabbreviated Location Paths
building compound: 9.3.1. Building Compound Location Paths from Location Steps with /
22.3.1. Abbreviated Syntax
node tests: 9.5. Unabbreviated Location Paths
unabbreviated: 9.5. Unabbreviated Location Paths
using predicates in: 9.4. Predicates
22.3. Location Paths
22.4. Predicates
location steps
attribute: 9.2.3. Attribute Location Steps
22.3.1. Abbreviated Syntax
child element: 9.2.2. Child Element Location Steps
22.3.1. Abbreviated Syntax
combining: 9.3. Compound Location Paths
comment( ): 9.2.4. The comment(), text(), and processing-instruction( ) Location Steps
components of: 9.5. Unabbreviated Location Paths
processing-instruction( ): 9.2.4. The comment(), text(), and processing-instruction( ) Location Steps
text( ): 9.2.4. The comment(), text(), and processing-instruction( ) Location Steps
locations and XPath nodes: 11.6. Points
locator elements: 10.4.1. Locators
Locator interface (SAX): 25.1. The org.xml.sax Package
LocatorImpl class (SAX): 25.2. The org.xml.sax.helpers Package
Lorie, Ray: 1.4. The Evolution of XML
6.1. SGML's Legacy

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

Mac-specific character sets: 5.7.2. MacRoman
MacRoman character set: 5.7.2. MacRoman
margin-bottom property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
margin-left property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
margin-right property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
margin-top property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
markup
describing a document's structure: 6.5. Document Permanence
input vs. output languages: 6.6. Transformation and Presentation
tags and: 2.2. Elements, Tags, and Character Data
master-name attribute (XSL-FO): 13.3. Laying Out the Master Pages
master pages for XSL-FO, laying out: 13.3. Laying Out the Master Pages
Mathematical Operators Unicode block: 26.3.21. Mathematical Operators
MathML (Mathematical Markup Language): 0.. What This Book Covers
entity sets and: 5.10. Character References
namespaces
need for: 4.1. The Need for Namespaces
syntax for: 4.2.1. Qualified Names, Prefixes, and Local Parts
maxExclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
maxInclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
maxLength facet element: 16.6.2.2. Restricting length
21.2. Schema Elements
maxOccurs attribute (xs:element element): 16.4.1. Occurrence Constraints
media pseudoattribute: 7.2.1.2. The media pseudoattribute
12.3. Associating Stylesheets with XML Documents
media types, MIME: 5.1. Character-Set Metadata
Megginson, David: 1.4. The Evolution of XML
25. SAX Reference
metadata, character-set: 5.1. Character-Set Metadata
metainformation module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
Microsoft and character sets: 5.7. Platform-Dependent Character Sets
Microsoft DTD repository (biztalk.org): 3.10. Locating Standard DTDs
MIME media types: 5.1. Character-Set Metadata
rddl:resource elements and: 14.2. RDDL Syntax
for stylesheets: 12.3. Associating Stylesheets with XML Documents
minExclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
minInclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
minLength facet element: 16.6.2.2. Restricting length
21.2. Schema Elements
minOccurs attribute (xs:element element): 16.4.1. Occurrence Constraints
Miscellaneous Symbols Unicode block: 26.3.25. Miscellaneous Symbols
Miscellaneous Technical Unicode block: 26.3.22. Miscellaneous Technical
%Misc.extra entity reference: 7.3.2. Mixing Your Applications into XHTML
mixed content
element type: 3.2.7. Mixed Content
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
in elements: 2.2.3. Mixed Content
schemas and: 16.7. Mixed Content
mod (XPath arithmetic operator): 9.6.1. Numbers
mode attributes for XSLT elements: 8.8. Modes
Modular XHTML (see XHTML 1.1)
Modularization of XHTML (W3C specification): 7.3.1. Mixing XHTML into Your Applications
modules, XHTML: 7.3. Authoring Compound Documents with Modular XHTML
monetary symbols, Unicode block for: 26.3.18. Currency Symbols
Mosher, Ed: 1.4. The Evolution of XML
6.1. SGML's Legacy
Mozilla, displaying XML in: 7.2.3. Netscape and Mozilla
MSXML and stylesheet processors: 8.3. Stylesheet Processors
Muellner, Leonard: 6.4. DocBook
multilingual documents: 5.11. xml:lang
Murray-Rust, Peter: 1.4. The Evolution of XML

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

name attribute
Attr interface: 24.2. Object Reference
DocumentType interface: 24.2. Object Reference
name characters (Unicode): 26.1. Character Tables
name identification module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
name start characters (Unicode): 26.1. Character Tables
name tokens, XML: 3.3.1.2. NMTOKEN
name-value pairs: 2.3. Attributes
name( ) (XPath): 9.7.1. Node-Set Functions
22.5. XPath Functions
NamedNodeMap interface (DOM): 18.3.2. The NamedNodeMap Interface
attribute for: 24.2. Object Reference
methods for: 24.2. Object Reference
names
element (see element names)
qualified: 4.2.1. Qualified Names, Prefixes, and Local Parts
defining: 4.4.1. Parameter Entity References for Namespace Prefixes
XML specification for: 20.3.4. Namespaces
unqualified: 20.3.4. Namespaces
XML: 2.4. XML Names
specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
vs.name tokens: 3.3.1.2. NMTOKEN
namespace axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
namespace-uri( ) (XPath): 9.7.1. Node-Set Functions
22.5. XPath Functions
NAMESPACE_ERR exception (DOM): 24.2. Object Reference
namespaces: 0.. What This Book Covers
4. Namespaces
constraints for: 20.4.3. Namespace Constraints
controlling qualification: 16.3.2. Controlling Qualification
DTDs and: 4.4. Namespaces and DTDs
element/attribute declarations: 20.3.2. DTD (Document Type Definition)
how parsers handle: 4.3. How Parsers Handle Namespaces
nodes
default template for: 8.7.4. The Default Template Rule for Namespace Nodes
location paths and: 9.2.4. The comment( ), text(), and processing-instruction( ) Location Steps
node set functions for: 9.7.1. Node-Set Functions
XPath data model and: 22.1. The XPath Data Model
prefixes (see prefixes)
RDDL and namespace URLs: 14. Resource Directory Description Language (RDDL)
reasons for using: 4.1. The Need for Namespaces
schemas and: 16.1.2. Namespace Issues
16.3. Working with Namespaces
21.1. The Schema Namespaces
setting default: 4.2.4. Setting a Default Namespace with the xmlns Attribute
20.3.4. Namespaces
syntax of: 4.2. Namespace Syntax
target namespaces: 16.3.1. Target Namespaces
URIs: 4.2.3. Namespace URIs
returned by SAX parsers: 25.3. SAX Features and Properties
XSLT processors and: 8.2. xsl:stylesheet and xsl:transform
XML specification for: 20.3.4. Namespaces
XPointers and: 11.5. Namespaces
XSLT and: 8.10. XSLT and Namespaces
23.1. The XSLT Namespace
Namespaces in XML recommendation: 20.2. Annotated Sample Documents
: (colon) character is reserved: 20.3.1. Global Syntax Structures
impacting element/attribute names: 20.3.4. Namespaces
namespace constraints: 20.4.3. Namespace Constraints
schemas and: 16.1.2. Namespace Issues
NamespaceSupport class (SAX): 25.2. The org.xml.sax.helpers Package
namespaceURI attribute (Node interface): 24.2. Object Reference
narrative-oriented DTDs: 3.9.2. Narrative-Oriented DTDs
narrative-oriented XML documents
DocBook: 6.4. DocBook
structure of: 6.2. Narrative Document Structures
TEI (Text Encoding Initiative): 6.3. TEI
national standard character sets: 5.6. ISO Character Sets
native2ascii conversion tool (JDK): 5.8. Converting Between Character Sets
natures of related resources: 14.3. Natures
NDATA declaration: 3.6.1. Notations
Netscape, displaying XML in: 7.2.3. Netscape and Mozilla
newTransform( ): 23.4. TrAX
nextSibling attribute (Node interface): 24.2. Object Reference
NMTOKEN attribute type: 3.3.1.2. NMTOKEN
20.3.2. DTD (Document Type Definition)
values must be legal name tokens: 20.4.2. Validity Constraints
NMTOKENS attribute type: 3.3.1.3. NMTOKENS
20.3.2. DTD (Document Type Definition)
values must be legal name tokens: 20.4.2. Validity Constraints
NO_DATA_ALLOWED_ERR exception (DOM): 24.2. Object Reference
node( )
node test: 22.3.3. Node Tests
XPath wildcard: 9.2.5. Wildcards
Node interface (DOM): 18.3. Node and Other Generic Interfaces
24. DOM Reference
attributes for: 24.2. Object Reference
methods for: 24.2. Object Reference
node-set functions (XPath): 9.7.1. Node-Set Functions
node sets in XPath: 22.2. Data Types
node tests in location paths: 9.5. Unabbreviated Location Paths
22.3. Location Paths
22.3.3. Node Tests
NodeList interface (DOM): 18.3.1. The NodeList Interface
24.2. Object Reference
attribute for: 24.2. Object Reference
method for: 24.2. Object Reference
nodeName attribute (Node interface): 18.3. Node and Other Generic Interfaces
24.2. Object Reference
nodes in XML documents
DOM and: 18.3. Node and Other Generic Interfaces
XPath and: 9.1. The Tree Structure of an XML Document
XSLT and: 8.7. The Built-in Template Rules
nodeType attribute (Node interface): 18.3. Node and Other Generic Interfaces
24.2. Object Reference
nodeValue attribute (Node interface): 18.3. Node and Other Generic Interfaces
24.2. Object Reference
NO_MODIFICATION_ALLOWED_ERR exception (DOM): 24.2. Object Reference
none value (display property): 12.5.4. Hidden Elements
normalize( ) (DOM)
Element interface: 24.2. Object Reference
Node interface: 24.2. Object Reference
normalize-space( ) (XPath): 9.7.2. String Functions
22.5. XPath Functions
not( ) (XPath): 9.7.3. Boolean Functions
22.5. XPath Functions
NOTATION attribute type: 3.3.1.10. NOTATION
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
not available for empty elements: 20.4.2. Validity Constraints
only one per element type: 20.4.2. Validity Constraints
notation attributes: 20.4.2. Validity Constraints
NOTATION declaration: 3.6. External Unparsed Entities and Notations
20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
names must be unique: 20.4.2. Validity Constraints
unparsed entities and: 20.4.2. Validity Constraints
Notation interface (DOM): 18.4.1.3. Notation
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy for: 24.1. Object Hierarchy
notation syntax of XML: 17.2.4. Notations
notationName attribute (Entity interface): 24.2. Object Reference
NOTATION_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
notations attribute (DocumentType interface): 24.2. Object Reference
NOT_FOUND_ERR exception (DOM): 24.2. Object Reference
NOT_SUPPORTED_ERR exception (DOM): 24.2. Object Reference
number( ) (XPath): 9.7.4. Number Functions
22.5. XPath Functions
numbers in XPath: 9.6.1. Numbers
22.2. Data Types
numeric functions (XPath): 9.7.4. Number Functions

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

OASIS DTD repository (xml.org): 3.10. Locating Standard DTDs
object hierarchy (DOM): 24.1. Object Hierarchy
object module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
object reference (DOM Level 2 Core): 24.2. Object Reference
object serialization and XML: 15.1.3. Object Serialization
OCR-A characters, Unicode block for: 26.3.23. Optical Character Recognition
online validators: 3.1.4. Validating a Document
Optical Character Recognition Unicode block: 26.3.23. Optical Character Recognition
org.xml.sax package: 19. Simple API for XML (SAX)
25.1. The org.xml.sax Package
ContentHandler interface and: 19.1. The ContentHandler Interface
org.xml.sax.ext package: 25.4. The org.xml.sax.ext Package
org.xml.sax.helpers package: 25.2. The org.xml.sax.helpers Package
org.xml.sax.helpers.XMLFilterImpl class: 19.3. Filters
org.xml.sax.XMLFilter interface: 19.3. Filters
origin( ): 11.7.7. origin( )
outbound links: 10.5. Linkbases
output formats: 6.6. Transformation and Presentation
ownerDocument attribute (Node interface): 24.2. Object Reference
ownerElement attribute (Attr interface): 24.2. Object Reference

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

page-sequence element (XSL-FO): 13.2. The Structure of an XSL-FO Document
flowing content into pages: 13.3.1. Flowing Content into the Pages
param module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
parameter entities, XML specification for: 20.3.2. DTD (Document Type Definition)
parameter entity declarations: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
declaring: 20.4.2. Validity Constraints
parameter entity references: 3.7. Parameter Entities
3.7. Parameter Entities
changing prefixes using: 4.4.1. Parameter Entity References for Namespace Prefixes
connecting XHTML modules: 7.3. Authoring Compound Documents with Modular XHTML
defining, with document model modules: 7.3.3. Mixing Your Own XHTML
external DTD subsets and: 3.7.3. External DTD Subsets
in DTD only: 20.4.1. Well-Formedness Constraints
including declarations in: 20.4.1. Well-Formedness Constraints
internal subsets and: 20.4.1. Well-Formedness Constraints
location in DTD is important: 20.3.1. Global Syntax Structures
mixing applications into XHTML: 7.3.2. Mixing Your Applications into XHTML
mixing XHTML into applications: 7.3.1. Mixing XHTML into Your Applications
proper nesting with
conditional sections: 20.4.2. Validity Constraints
markup declarations: 20.4.2. Validity Constraints
parenthesized groups: 20.4.2. Validity Constraints
redefining: 3.7.2. Redefining Parameter Entities
syntax of: 3.7.1. Parameter Entity Syntax
XLink elements and: 10.6. DTDs for XLinks
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
parent axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
parent elements: 2.2.2.1. Parents and children
selecting with .. (double period): 9.3.3. Selecting the Parent Element with ..
22.3.1. Abbreviated Syntax
parentheses in element declarations: 3.2.6. Parentheses
parentNode attribute (Node interface): 24.2. Object Reference
parse( ) (SAX): 19. Simple API for XML (SAX)
using filters with: 19.3. Filters
parse( ) (XMLReader interface): 25.1. The org.xml.sax Package
parsed character data: 2.5. Entity References
3.1.1. A Simple DTD Example
parsed general entities: 3.5. External Parsed General Entities
attribute values and: 20.4.1. Well-Formedness Constraints
no recursion allowed: 20.4.1. Well-Formedness Constraints
text declarations and: 5.3. Text Declarations
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
ParserAdapter class (SAX): 25.2. The org.xml.sax.helpers Package
parsers, validating: 1.3. How XML Works
1.3. How XML Works
18.6. Parsing a Document with DOM
associating schema documents with instance documents: 16.1. Overview
16.2. Schema Basics
PassiveTeX library: 13.3.2. Generating the Finished Document
pattern facet element: 16.6.2.5. Enforcing format
21.2. Schema Elements
#PCDATA keyword: 3.1.1. A Simple DTD Example
3.2.1. #PCDATA
in narrative-oriented documents: 6.2. Narrative Document Structures
PDF format for XSL-FO documents: 13.5. Choosing Between CSS and XSL-FO
pictographic symbols, Unicode block for: 26.3.25. Miscellaneous Symbols
Pinard, Francois: 5.8. Converting Between Character Sets
pixels (relative unit of length): 12.6. Pixels, Points, Picas, and Other Units of Length
platform-dependent character sets: 5.7. Platform-Dependent Character Sets
points in XPointers: 11.6. Points
position( ) (XPath): 9.7. XPath Functions
22.5. XPath Functions
Post Schema Validation Infoset (PSVI): 21. Schemas Reference
preceding axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
preceding-sibling axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
predefined character references: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
predicates
in location paths: 9.4. Predicates
22.3. Location Paths
22.4. Predicates
optional part of location steps: 9.5. Unabbreviated Location Paths
prefix attribute (Node interface): 24.2. Object Reference
prefixed names in XPointers: 11.5. Namespaces
prefixes
binding to URIs: 4.2.2. Binding Prefixes to URIs
controlling namespace qualification: 16.3.2. Controlling Qualification
default namespaces and: 4.2.4. Setting a Default Namespace with the xmlns Attribute
20.3.4. Namespaces
element names and: 4.2.1. Qualified Names, Prefixes, and Local Parts
namespace constraints for: 20.4.3. Namespace Constraints
using parameter entity references to change: 4.4.1. Parameter Entity References for Namespace Prefixes
preorder traversal: 8.6. Applying Templates with xsl:apply-templates
presentation module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
previousSibling attribute (Node interface): 24.2. Object Reference
primitive types for schemas: 16.2.4. Simple Types
21.3. Primitive Types
defining: 16.6.1. Defining New Simple Types
processing-instruction( )
location step: 9.2.4. The comment( ), text(), and processing-instruction( ) Location Steps
node test: 22.3.3. Node Tests
processing instruction nodes
default template for: 8.7.3. The Default Template Rule for Comment and Processing Instruction Nodes
XPath data model and: 22.1. The XPath Data Model
processing instructions: 2.8. Processing Instructions
notations for targets of: 3.6.3. Notations for Processing Instruction Targets
parsers and: 17.2.3. Processing Instructions
robots: 7.4.3. Robots
web browser problems with: 7.1.3.1. The XML declaration and processing instructions
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
ProcessingInstruction interface (DOM): 18.4.1.2. ProcessingInstruction
24.2. Object Reference
attributes for: 24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
processingInstruction( ) (SAX): 19.1. The ContentHandler Interface
overriding, using filters: 19.3. Filters
PROCESSING_INSTRUCTION_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
programming models used with XML: 17. Programming Models
prolog (XML document): 3.1.2. The Document Type Declaration
20.3.1. Global Syntax Structures
properties
of Cascading Style Sheets: 12.5. The Display Property
controlling parser behavior for SAX: 19.2. SAX Features and Properties
25.3. SAX Features and Properties
of XSL-FO: 13.4. XSL-FO Properties
proprietary character sets: 5.7. Platform-Dependent Character Sets
pseudoattributes for xml-stylesheet processing instruction: 7.2.1.1. The required href and type pseudoattributes
12.3. Associating Stylesheets with XML Documents
pseudoclass selectors: 12.4.4. Pseudoclass Selectors
pseudoelement selectors: 12.4.5. Pseudoelement Selectors
PSVI (Post Schema Validation Infoset): 21. Schemas Reference
PUBLIC keyword (document type declaration): 3.1.2.1. Public IDs
publicId attribute
DocumentType interface: 24.2. Object Reference
Entity interface: 24.2. Object Reference
punctuation characters, Unicode block for: 26.3.17. General Punctuation
purposes of related resources: 14.4. Purposes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

qualified names: 4.2.1. Qualified Names, Prefixes, and Local Parts
defining: 4.4.1. Parameter Entity References for Namespace Prefixes
XML specification for: 20.3.4. Namespaces
quantum mechanics symbols, Unicode block for: 26.3.22. Miscellaneous Technical
Query Language, XML: 1.4. The Evolution of XML

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

Ragget, Dave: 7.1.1. Moving from HTML to XHTML
Rahtz, Sebastian: 13.3.2. Generating the Finished Document
range( ): 11.7.1. The range( ) function
range-inside( ): 11.7.2. The range-inside( ) function
range-to( ): 11.7.3. The range-to( ) function
ranges in XPointers: 11.6. Points
11.7. Ranges
raw names: 4.2.1. Qualified Names, Prefixes, and Local Parts
defining: 4.4.1. Parameter Entity References for Namespace Prefixes
RDDL (Resource Directory Description Language): 0.. What This Book Covers
14. Resource Directory Description Language (RDDL)
document example: 14.2. RDDL Syntax
namespace URIs and: 4.2.3. Namespace URIs
namespace URLs and: 14. Resource Directory Description Language (RDDL)
natures of related resources: 14.3. Natures
purposes of related resources: 14.4. Purposes
syntax for: 14.2. RDDL Syntax
rddl:resource element: 14. Resource Directory Description Language (RDDL)
RDF (Resource Description Framework): 0.. What This Book Covers
7.4.1. RDF
Dublin Core and: 4.1. The Need for Namespaces
7.4.2. Dublin Core
recode (conversion tool): 5.8. Converting Between Character Sets
region-after child element (XSL-FO): 13.3. Laying Out the Master Pages
region-before child element (XSL-FO): 13.3. Laying Out the Master Pages
region-body child element (XSL-FO): 13.3. Laying Out the Master Pages
region-end child element (XSL-FO): 13.3. Laying Out the Master Pages
region-start child element (XSL-FO): 13.3. Laying Out the Master Pages
regular expressions
treating XML as text: 17.1.1. Treating XML as Text
xs:pattern element and: 21.2. Schema Elements
related resources: 14.2. RDDL Syntax
natures of: 14.3. Natures
purposes of: 14.4. Purposes
relational operators in XPath: 9.4. Predicates
9.6.2. Strings
relative location paths: 22.3. Location Paths
building compound location paths: 9.3.1. Building Compound Location Paths from Location Steps with /
relative units of length (CSS properties): 12.6. Pixels, Points, Picas, and Other Units of Length
relative XPointers: 11.7.5. Relative XPointers
RELAX NG schema language: 1.1. The Benefits of XML
1.4. The Evolution of XML
16.1. Overview
remote resources (extended links): 10.4. Extended Links
third-party links and: 10.5. Linkbases
removeAttribute( ) (DOM): 24.2. Object Reference
removeAttributeNode( ) (DOM): 24.2. Object Reference
removeAttributeNS( ) (DOM): 24.2. Object Reference
removeChild( ) (DOM): 24.2. Object Reference
removeNamedItem( ) (DOM): 24.2. Object Reference
removeNamedItemNS( ) (DOM): 24.2. Object Reference
RenderX formatting program (XEP): 13.3.2. Generating the Finished Document
replaceChild( ) (DOM): 24.2. Object Reference
replaceData( ) (DOM): 24.2. Object Reference
repository of DTDs: 3.10. Locating Standard DTDs
#REQUIRED default declaration: 3.3.2. Attribute Defaults
20.3.2. DTD (Document Type Definition)
value must be assigned to attributes: 20.4.2. Validity Constraints
Resource Description Framework (see RDF)
Resource Directory Description Language (see RDDL)
resource element (RDDL): 14. Resource Directory Description Language (RDDL)
resource elements: 10.4.3. Local Resources
resources and RDF documents: 7.4.1. RDF
robots processing instruction: 7.4.3. Robots
role attribute (Xlink): 10.3. Link Semantics
role attributes for arcs: 10.4.2.3. Arc roles
root element: 2.2.2.2. The root element
20.3.1. Global Syntax Structures
determining name of: 20.4.2. Validity Constraints
in narrative-oriented documents: 6.2. Narrative Document Structures
of TEI documents: 6.3. TEI
vs. root node: 9.1. The Tree Structure of an XML Document
of XSL-FO documents: 13.2. The Structure of an XSL-FO Document
root interface (DOM Core): 18.3. Node and Other Generic Interfaces
root location path: 9.2.1. The Root Location Path
22.3. Location Paths
root node
default template for: 8.7.2. The Default Template Rule for Element and Root Nodes
vs. root element: 9.1. The Tree Structure of an XML Document
XPath data model and: 22.1. The XPath Data Model
round( ) (XPath): 9.7. XPath Functions
9.7.4. Number Functions
22.5. XPath Functions
Ruby Annotation (W3C specification): 7.3.1. Mixing XHTML into Your Applications
ruby module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
Russian language, Unicode block for: 26.3.7. Cyrillic

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

Sanskrit language, Unicode block for: 26.3.11. Devanagari
SAX 2.1: 1.4. The Evolution of XML
SAX (Simple API for XML): 0.. What This Book Covers
1.4. The Evolution of XML
19. Simple API for XML (SAX)
Attributes interface: 25.1. The org.xml.sax Package
AttributesImpl class: 25.2. The org.xml.sax.helpers Package
ContentHandler interface: 19.1. The ContentHandler Interface
25.1. The org.xml.sax Package
controlling parser behavior with URIs: 19.2. SAX Features and Properties
25.3. SAX Features and Properties
DeclHandler interface: 25.4. The org.xml.sax.ext Package
DefaultHandler class: 19.1. The ContentHandler Interface
25.2. The org.xml.sax.helpers Package
DTDHandler interface: 25.1. The org.xml.sax Package
EntityResolver interface: 25.1. The org.xml.sax Package
ErrorHandler interface: 25.1. The org.xml.sax Package
features of: 19.2. SAX Features and Properties
25.3. SAX Features and Properties
filters: 19.3. Filters
InputSource class: 25.1. The org.xml.sax Package
LexicalHandler interface: 25.4. The org.xml.sax.ext Package
Locator interface: 25.1. The org.xml.sax Package
LocatorImpl class: 25.2. The org.xml.sax.helpers Package
methods called by parser in ContentHandler: 19.1. The ContentHandler Interface
NamespaceSupport class: 25.2. The org.xml.sax.helpers Package
nondeterministic code details: 19.1. The ContentHandler Interface
org.xml.sax package: 19. Simple API for XML (SAX)
25.1. The org.xml.sax Package
org.xml.sax.ext package: 25.4. The org.xml.sax.ext Package
org.xml.sax.helpers package: 25.2. The org.xml.sax.helpers Package
ParserAdapter class: 25.2. The org.xml.sax.helpers Package
parsing a document with: 19. Simple API for XML (SAX)
properties of: 19.2. SAX Features and Properties
25.3. SAX Features and Properties
reference: 25. SAX Reference
SAX1 vs. SAX2: 19. Simple API for XML (SAX)
25. SAX Reference
SAXException class: 19. Simple API for XML (SAX)
25.1. The org.xml.sax Package
SAXNotRecognizedException class: 19.2. SAX Features and Properties
25.1. The org.xml.sax Package
25.4. The org.xml.sax.ext Package
SAXNotSupportedException class: 19.2. SAX Features and Properties
25.1. The org.xml.sax Package
SAXParseException class: 25.1. The org.xml.sax Package
TrAX and: 23.4. TrAX
vs. DOM: 19.1. The ContentHandler Interface
XMLFilter interface: 25.1. The org.xml.sax Package
XMLFilterImpl class: 25.2. The org.xml.sax.helpers Package
XMLReader interface: 19. Simple API for XML (SAX)
25.1. The org.xml.sax Package
XMLReaderAdapter class: 25.2. The org.xml.sax.helpers Package
XMLReaderFactory class: 19. Simple API for XML (SAX)
25.2. The org.xml.sax.helpers Package
SAXException class: 19. Simple API for XML (SAX)
25.1. The org.xml.sax Package
SAXNotRecognizedException class: 19.2. SAX Features and Properties
25.1. The org.xml.sax Package
25.4. The org.xml.sax.ext Package
SAXNotSupportedException class: 19.2. SAX Features and Properties
25.1. The org.xml.sax Package
SAXON (XSLT processor): 8.3. Stylesheet Processors
SAXParseException class (SAX): 25.1. The org.xml.sax Package
sax.SAXCount class: 2.10. Checking Documents for Well-Formedness
3.1.4. Validating a Document
Scalable Vector Graphics (see SVG)
schema languages: 1.1. The Benefits of XML
1.4. The Evolution of XML
schema.net (DTD repository): 3.10. Locating Standard DTDs
schemas: 0.. What This Book Covers
16. XML Schemas
abstract attribute: 16.9.1. Abstract Elements and Types
annotation elements in: 16.2.2. Annotations
attribute declarations and: 16.2.5. Attribute Declarations
complex types and: 16.4. Complex Types
constructing: 16.2. Schema Basics
controlling namespace qualification: 16.3.2. Controlling Qualification
document organization: 16.2.1. Document Organization
element content
any type: 16.8. Allowing Any Content
empty elements: 16.5. Empty Elements
mixed: 16.7. Mixed Content
simple: 16.6. Simple Content
elements of: 21.2. Schema Elements
enforcing element uniqueness: 16.9.4. Uniqueness and Keys
facets and: 16.6.2. Facets
final attribute: 16.9.2. The Final Attribute
fixed attribute: 16.9.3. Setting fixed Facets
instance documents (see instance documents)
keys and references: 16.9.4.2. Keys and references
lists and: 16.6.2.6. Lists
21.2. Schema Elements
using multiple documents: 16.8.1. Using Multiple Documents
namespaces and: 16.1.2. Namespace Issues
16.3. Working with Namespaces
21.1. The Schema Namespaces
reference: 21. Schemas Reference
simple types: 16.2.4. Simple Types
21.3. Primitive Types
defining: 16.6.1. Defining New Simple Types
targetNamespace attribute and: 16.3.1. Target Namespaces
unions and: 16.6.2.7. Unions
21.2. Schema Elements
valid/invalid documents and: 1.1. The Benefits of XML
vs. DTDs: 16.1.1. Schemas Versus DTDs
Schematron schema language: 1.1. The Benefits of XML
1.4. The Evolution of XML
16.1. Overview
annotations and: 16.2.2.2. The xs:appinfo element
scripting module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
search methods on the web: 7.4. Prospects for Improved Web-Search Methods
selectors for Cascading Style Sheets: 12.4. Selectors
self axis: 9.5. Unabbreviated Location Paths
22.3.2. Axes
sequences in element declarations: 3.2.3. Sequences
server-side image map module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
setAttribute( ) (DOM): 24.2. Object Reference
24.2. Object Reference
setAttributeNode( ) (DOM): 24.2. Object Reference
24.2. Object Reference
setAttributeNodeNS( ) (DOM): 24.2. Object Reference
setAttributeNS( ) (DOM): 24.2. Object Reference
setContentHandler( ) (SAX): 25.1. The org.xml.sax Package
setDocumentLocator( ) (SAX): 19.1. The ContentHandler Interface
25.1. The org.xml.sax Package
setDTDHandler( ) (SAX): 25.1. The org.xml.sax Package
setEntityResolver( ) (SAX): 25.1. The org.xml.sax Package
setErrorHandler( ) (SAX): 25.1. The org.xml.sax Package
setFeature( ) (SAX): 19.2. SAX Features and Properties
setNamedItem( ) (DOM): 24.2. Object Reference
setNamedItemNS( ) (DOM): 24.2. Object Reference
setParent( ) (SAX): 19.3. Filters
setProperty( ) (SAX): 19.2. SAX Features and Properties
25.4. The org.xml.sax.ext Package
SGML and XML: 1.4. The Evolution of XML
6.1. SGML's Legacy
Shift_JIS encoding: 5.4. XML-Defined Character Sets
26. Character Sets
show attribute (Xlink): 10.2.1. xlink:show
sibling elements: 2.2.2.1. Parents and children
Signatures, XML: 1.4. The Evolution of XML
Simple API for XML (see SAX)
simple content and schemas: 16.6. Simple Content
simple links: 10.1. Simple Links
Simple Object Access Protocol (SOAP): 15.1.2. Communications
simple-page-master element (XSL-FO): 13.3. Laying Out the Master Pages
simple types for schemas: 16.2.4. Simple Types
21.3. Primitive Types
defining: 16.6.1. Defining New Simple Types
facet elements for: 21.2. Schema Elements
Slavic languages, Unicode block for: 26.3.7. Cyrillic
SOAP (Simple Object Access Protocol): 15.1.2. Communications
source code samples and CDATA sections: 2.6. CDATA Sections
Spacing Modifier Letters Unicode block: 26.3.4. Spacing Modifier Letters
special attributes: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
special characters entity set: 5.10. Character References
26.2. HTML4 Entity Sets
26.2. HTML4 Entity Sets
specified attribute (Attr interface): 24.2. Object Reference
Sperberg-McQueen, C.M.: 1.4. The Evolution of XML
splitText( ) (DOM): 24.2. Object Reference
standalone declaration: 2.9.2. standalone
external DTD subsets and: 3.1.3. Internal DTD Subsets
setting value to 'no': 20.4.2. Validity Constraints
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
start-point( ): 11.6. Points
start-tags: 2.2. Elements, Tags, and Character Data
attributes and: 2.3. Attributes
XML specification for: 20.2. Annotated Sample Documents
20.3.3. Document Body
startDocument( ) (SAX): 19.1. The ContentHandler Interface
19.1. The ContentHandler Interface
startElement( ) (SAX): 19.1. The ContentHandler Interface
25.1. The org.xml.sax Package
using filters with: 19.3. Filters
starting resources: 10.1. Simple Links
startPrefixMapping( ) (SAX): 19.1. The ContentHandler Interface
starts-with( ) (XPath): 9.7. XPath Functions
9.7.2. String Functions
22.5. XPath Functions
strict DTD (XHTML): 7.1.2. Three DTDs for XHTML
string functions (XPath): 9.7.2. String Functions
string-length( ) (XPath): 9.7.2. String Functions
22.5. XPath Functions
string-range( ): 11.7.4. The string-range( ) function
string( ) (XPath): 9.7.2. String Functions
22.5. XPath Functions
strings
in XPath: 9.6.2. Strings
22.2. Data Types
internalized (SAX feature): 25.3. SAX Features and Properties
structural nodes (DOM Core): 18.4.1. Structural Nodes
structure module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
style attribute module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
style rules: 12.2. CSS Syntax
stylesheet element (XSLT): 8.2. xsl:stylesheet and xsl:transform
stylesheet module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
stylesheets: 7.2. Direct Display of XML in Browsers
(see also Cascading Style Sheets)
displaying XML in web browsers: 7.2. Direct Display of XML in Browsers
document permanence and: 6.5. Document Permanence
subcodes (languages): 5.11.2. Subcodes
substitution groups: 16.8.3. Substitution Groups
substring-after( ) (XPath): 9.7.2. String Functions
22.5. XPath Functions
substring-before( ) (XPath): 9.7.2. String Functions
22.5. XPath Functions
substring( ) (XPath): 9.7.2. String Functions
22.5. XPath Functions
substringData( ) (DOM): 24.2. Object Reference
suffixes in element declarations: 3.2.4. The Number of Children
sum( ) (XPath): 9.7.4. Number Functions
22.5. XPath Functions
super keyword and SAX filters: 19.3. Filters
SVG (Scalable Vector Graphics): 0.. What This Book Covers
namespaces
need for: 4.1. The Need for Namespaces
syntax for: 4.2.1. Qualified Names, Prefixes, and Local Parts
xmlns attribute and: 4.2.4. Setting a Default Namespace with the xmlns Attribute
prefixes and namespace URIs: 4.2.3. Namespace URIs
symbols entity set: 5.10. Character References
26.2. HTML4 Entity Sets
26.2. HTML4 Entity Sets
syntax structures of XML documents: 20.3.1. Global Syntax Structures
SYNTAX_ERR exception (DOM): 24.2. Object Reference
SYSTEM keyword (document type declaration): 3.1.2.1. Public IDs
system-property( ) (XSLT): 23.3. XSLT Functions
systemId attribute
DocumentType interface: 24.2. Object Reference
Entity interface: 24.2. Object Reference

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

table-and-caption element (XSL-FO): 13.3.1. Flowing Content into the Pages
table-caption value (display property): 12.5.5. Table Elements
table-cell value (display property): 12.5.5. Table Elements
table-column-group value (display property): 12.5.5. Table Elements
table-column value (display property): 12.5.5. Table Elements
table element (XSL-FO): 13.3.1. Flowing Content into the Pages
table elements (CSS display property): 12.5.5. Table Elements
table-footer-group value (display property): 12.5.5. Table Elements
table-header-group value (display property): 12.5.5. Table Elements
table-row-group value (display property): 12.5.5. Table Elements
table-row value (display property): 12.5.5. Table Elements
table value (display property): 12.5.5. Table Elements
tables module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
tag sets: 1.1. The Benefits of XML
tagName attribute (Element interface): 24.2. Object Reference
tags
empty-element: 20.2. Annotated Sample Documents
20.3.3. Document Body
matching: 20.4.1. Well-Formedness Constraints
overlapping not allowed: 2.2.2.1. Parents and children
start/end: 2.2. Elements, Tags, and Character Data
20.2. Annotated Sample Documents
20.3.3. Document Body
syntax of: 2.2.1. Tag Syntax
target attribute (ProcessingInstruction interface): 24.2. Object Reference
target module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
targetNamespace attribute: 16.3.1. Target Namespaces
Tauber, James: 3.10. Locating Standard DTDs
TEI (Text Encoding Initiative): 6.3. TEI
template rules, built-in: 8.2. xsl:stylesheet and xsl:transform
8.7. The Built-in Template Rules
templates
adding to XSLT stylesheets: 8.4. Templates and Template Rules
xsl:apply-templates and: 8.6. Applying Templates with xsl:apply-templates
text( )
location step: 9.2.4. The comment( ), text(), and processing-instruction( ) Location Steps
node test: 22.3.3. Node Tests
text-align property (CSS): 12.8. Text Properties
text declarations
external entities and: 3.5.1. Text Declarations
vs. XML declarations: 5.3. Text Declarations
XML specification for: 20.3.2. DTD (Document Type Definition)
text-decoration property (CSS): 12.8. Text Properties
Text Encoding Initiative (TEI): 6.3. TEI
text-indent property (CSS): 12.8. Text Properties
Text interface (DOM): 18.4.2.8. Text
24.2. Object Reference
method for: 24.2. Object Reference
nodeName attribute and: 24.2. Object Reference
nodeValue attribute and: 24.2. Object Reference
object hierarchy: 24.1. Object Hierarchy
text module (XHTML DTD): 7.3. Authoring Compound Documents with Modular XHTML
text nodes
default template for: 8.7.1. The Default Template Rule for Text and Attribute Nodes
XPath data model and: 22.1. The XPath Data Model
text properties
Cascading Style Sheets: 12.8. Text Properties
XSL-FO: 13.1. XSL Formatting Objects
text-transform property (CSS): 12.8. Text Properties
TEXT_NODE (DOM node type) and nodeType attribute: 18.3. Node and Other Generic Interfaces
24.2. Object Reference
textual tools for XML documents: 17.1.1. Treating XML as Text
text/xml MIME media type: 5.1. Character-Set Metadata
Thai characters
ISO-8859-11 encoding: 5.6. ISO Character Sets
26. Character Sets
Unicode block for: 26.3.12. Thai
third-party links: 10.5. Linkbases
Tibetan Unicode block: 26.3.13. Tibetan
Tidy (open source tool): 7.1.1. Moving from HTML to XHTML
title attribute (Xlink): 10.3. Link Semantics
title attributes for arcs: 10.4.2.2. Arc titles
title elements (extended links): 10.4.4. Title Elements
title pseudoattribute: 7.2.1.4. The alternate and title pseudoattributes
12.3. Associating Stylesheets with XML Documents
to attribute (XLink): 10.4.2. Arcs
Tobin, Richard: 3.1.4. Validating a Document
top property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
totalDigits facet element: 16.6.2.4.2. Length and precision
21.2. Schema Elements
transform( ): 23.4. TrAX
transformation languages: 6.6. Transformation and Presentation
transformations, XSL (see XSLT)
transitional DTD (XHTML): 7.1.2. Three DTDs for XHTML
translate( ) (XPath): 22.5. XPath Functions
traversal
Node interface and: 18.3.3. Relating Document Structure to Nodes
xsl:apply-templates and: 8.6. Applying Templates with xsl:apply-templates
TrAX (Transformations API for XML): 23.4. TrAX
tree models for XML documents: 17.1.3. Treating XML as Tree Models
true( ) (XPath): 9.7.3. Boolean Functions
22.5. XPath Functions
Turing, Alan: 2.1. XML Documents and XML Files
3.1.1. A Simple DTD Example
3.2.8. Empty Elements
3.9.1. Data-Oriented DTDs
8.1. An Example Input Document
9.1. The Tree Structure of an XML Document
9.2.2. Child Element Location Steps
9.4. Predicates
9.7.1. Node-Set Functions
JPEG image of: 3.6. External Unparsed Entities and Notations
10.2. Link Behavior
Turing complete programming languages: 1.1. The Benefits of XML
type attribute (Xlink): 10.1. Simple Links
type derivation (see derived types)
type libraries: 16.8.1.3. Importing schemas for other namespaces
type pseudoattribute: 7.2.1.1. The required href and type pseudoattributes
12.3. Associating Stylesheets with XML Documents

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

UCS-2 encoding: 5.5.1. UCS-2 and UTF-16
unabbreviated location paths: 9.5. Unabbreviated Location Paths
unassigned code points (Unicode): 26.1. Character Tables
Underwood, Walter: 7.4.3. Robots
Unicode blocks: 26.3. Other Unicode Blocks
Arabic: 26.3.10. Arabic
Armenian: 26.3.8. Armenian
Arrows: 26.3.20. Arrows
Currency Symbols: 26.3.18. Currency Symbols
Cyrillic: 26.3.7. Cyrillic
Devanagari: 26.3.11. Devanagari
Dingbat: 26.3.26. Dingbats
General Punctuation: 26.3.17. General Punctuation
Geometric Shapes: 26.3.24. Geometric Shapes
Greek Extended: 26.3.16. Greek Extended
Greek/Coptic: 26.3.6. Greek and Coptic
Hebrew: 26.3.9. Hebrew
IPA Extensions: 26.3.3. IPA Extensions
Latin Extended-A: 26.3.1. Latin Extended-A
Latin Extended Additional: 26.3.15. Latin Extended Additional
Latin Extended-B: 26.3.2. Latin Extended-B
Letter-like Symbols: 26.3.19. Letter-Like Symbols
Mathematical Operators: 26.3.21. Mathematical Operators
Miscellaneous Symbols: 26.3.25. Miscellaneous Symbols
Miscellaneous Technical: 26.3.22. Miscellaneous Technical
Optical Character Recognition: 26.3.23. Optical Character Recognition
Spacing Modifier Letters: 26.3.4. Spacing Modifier Letters
Thai: 26.3.12. Thai
Tibetan: 26.3.13. Tibetan
Unicode character classes: 21.2. Schema Elements
Unicode characters: 5. Internationalization
character references and: 20.3.1. Global Syntax Structures
character set names: 26. Character Sets
character tables for: 26.1. Character Tables
encoding attribute in XML declaration: 2.9.1. encoding
Uniform Resource Identifiers (see URIs)
Uniform Resource Names (URNs): 3.1.2. The Document Type Declaration
units of length (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
unparsed-entity-uri( ) (XSLT): 23.3. XSLT Functions
unparsed general entities: 3.6. External Unparsed Entities and Notations
17.2.5. Unparsed Entities
declaring notations: 20.4.2. Validity Constraints
not allowed in entity references: 20.4.1. Well-Formedness Constraints
XML specification for: 20.2. Annotated Sample Documents
20.3.2. DTD (Document Type Definition)
unqualified names, XML specification for: 20.3.4. Namespaces
URIs (Uniform Resource Identifiers): 3.1.2. The Document Type Declaration
assigning elements to: 4.2. Namespace Syntax
binding prefixes to: 4.2.2. Binding Prefixes to URIs
controlling parser behavior for SAX: 19.2. SAX Features and Properties
25.3. SAX Features and Properties
conventions for using: 4.2.2. Binding Prefixes to URIs
XPointer syntax and: 0.. What This Book Covers
xs:anyURI type and: 16.2.4. Simple Types
21.3. Primitive Types
URLs (Uniform Resource Locators) and XPointers: 11.1. XPointers on URLs
URNs (Uniform Resource Names): 3.1.2. The Document Type Declaration
UTF-16 encoding: 5.4. XML-Defined Character Sets
5.5.1. UCS-2 and UTF-16
26. Character Sets
encoding declaration and: 20.3.1. Global Syntax Structures
omitting encoding declaration: 5.2. The Encoding Declaration

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

validating parsers: 1.3. How XML Works
1.3. How XML Works
18.6. Parsing a Document with DOM
associating schema documents with instance documents: 16.1. Overview
16.2. Schema Basics
validity constraints: 20.4.2. Validity Constraints
validity errors: 1.3. How XML Works
3. Document Type Definitions (DTDs)
3.1.4. Validating a Document
value attribute (Attr interface): 24.2. Object Reference
version information attribute, XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
Vietnamese language, Unicode block most useful for: 26.3.15. Latin Extended Additional
visited pseudoclass: 12.4.4. Pseudoclass Selectors

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

Walsh, Norman: 6.4. DocBook
W3C
recommended form of XHTML: 7.1.2. Three DTDs for XHTML
standard DTDs and: 3.10. Locating Standard DTDs
standards endorsed by: 0.. What This Book Covers
styling XML documents: 1.4. The Evolution of XML
templates for
defining the namespace URI and prefix names: 7.3.3. Mixing Your Own XHTML
importing standard modules: 7.3.3. Mixing Your Own XHTML
naming attributes and elements: 7.3.3. Mixing Your Own XHTML
web browsers
CSS stylesheets in: 12.3. Associating Stylesheets with XML Documents
13.5. Choosing Between CSS and XSL-FO
displaying XML in: 7.2. Direct Display of XML in Browsers
document validation and: 3.1.4. Validating a Document
link behavior in: 10.2. Link Behavior
linkbases and: 10.5. Linkbases
support for XHTML: 7.1.3. Browser Support for XHTML
XLinks support: 10. XLinks
XPointers and: 11.1. XPointers on URLs
web search methods: 7.4. Prospects for Improved Web-Search Methods
well-formedness: 1.1. The Benefits of XML
2. XML Fundamentals
changing HTML to XHTML: 7.1.1. Moving from HTML to XHTML
checking documents for: 2.10. Checking Documents for Well-Formedness
constraints recognized by XML 1.0 parsers: 20.4.1. Well-Formedness Constraints
errors: 1.3. How XML Works
2.10. Checking Documents for Well-Formedness
3. Document Type Definitions (DTDs)
SAX filters, problems with: 19.3. Filters
SAXParseException and: 25.1. The org.xml.sax Package
XSLT stylesheets and: 8.4. Templates and Template Rules
white-space property (CSS): 12.8. Text Properties
whitespace
in element contents: 2.2. Elements, Tags, and Character Data
in XML names: 2.4. XML Names
stripping: 23.2. XSLT Elements
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
xs:whiteSpace facet element: 16.6.2.1. Handling whitespace
21.2. Schema Elements
width property (CSS): 12.6. Pixels, Points, Picas, and Other Units of Length
wildcards and location paths: 9.2.5. Wildcards
Windows ANSI character set: 5.7.1. Cp1252
World Wide Web Consortium (see W3C)
WRONG_DOCUMENT_ERR exception (DOM): 24.2. Object Reference

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

Xalan (Apache XML Project XSLT processor): 8.3. Stylesheet Processors
XEP (RenderX formatting program): 13.3.2. Generating the Finished Document
Xerces-J parser: 2.10. Checking Documents for Well-Formedness
16.2. Schema Basics
#xFEFF Unicode character: 5.5.1. UCS-2 and UTF-16
XFragment: 1.4. The Evolution of XML
XHTML: 0.. What This Book Covers
7. XML on the Web
creating a valid document: 7.1.1. Moving from HTML to XHTML
DTDs for: 7.1.2. Three DTDs for XHTML
example of a valid document: 7.1.1. Moving from HTML to XHTML
moving from HTML to: 7.1.1. Moving from HTML to XHTML
replacing standard DTD: 7.3.3. Mixing Your Own XHTML
vs. HTML: 7.1. XHTML
web browser support for: 7.1.3. Browser Support for XHTML
XHTML 1.1: 7. XML on the Web
7.3. Authoring Compound Documents with Modular XHTML
creating your own XHTML: 7.3.3. Mixing Your Own XHTML
list of modules: 7.3. Authoring Compound Documents with Modular XHTML
mixing applications into: 7.3.2. Mixing Your Applications into XHTML
mixing modules into applications: 7.3.1. Mixing XHTML into Your Applications
Module-based XHTML (W3C specification): 7.3.1. Mixing XHTML into Your Applications
%xhtml-applet.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-base.module: 7.3. Authoring Compound Documents with Modular XHTML
XHTML Basic and RDDL: 14.2. RDDL Syntax
%xhtml-basic-form.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-basic-table.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-bdo.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-csismap.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-edit.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-events.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-form.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-frames.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-hypertext.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-iframe.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-image.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-inlstyle.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-legacy.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-link.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-list.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-meta.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-nameident.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-object.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-param.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-pres.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml11-ruby.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-script.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-ssismap.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-struct.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-style.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-table.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-target.module: 7.3. Authoring Compound Documents with Modular XHTML
%xhtml-text.module: 7.3. Authoring Compound Documents with Modular XHTML
XInclude: 1.4. The Evolution of XML
XLink (Extensible Linking Language): 0.. What This Book Covers
1.4. The Evolution of XML
xlink:actuate attribute: 10.2.2. xlink:actuate
xlink:arcrole attribute: 10.4.2.3. Arc roles
rddl:resource elements and: 14.2. RDDL Syntax
xlink:from attribute: 10.4.2. Arcs
xlink:href attribute: 10.1. Simple Links
locator elements and: 10.4.1. Locators
rddl:resource elements and: 14.2. RDDL Syntax
xlink:label attribute: 10.4.1. Locators
local resources and: 10.4.3. Local Resources
xlink:role attribute: 10.3. Link Semantics
local resources and: 10.4.3. Local Resources
locator elements and: 10.4.1. Locators
rddl:resource elements and: 14.2. RDDL Syntax
XLinks: 10. XLinks
DTDs for: 10.6. DTDs for XLinks
extended links: 10.4. Extended Links
link behavior: 10.2. Link Behavior
simple links: 10.1. Simple Links
third-party links: 10.5. Linkbases
XPointers and: 11.2. XPointers in Links
xlink:show attribute: 10.2.1. xlink:show
xlink:title attribute: 10.3. Link Semantics
arc elements and: 10.4.2.2. Arc titles
local resources and: 10.4.3. Local Resources
locator elements and: 10.4.1. Locators
xlink:to attribute: 10.4.2. Arcs
xlink:type attribute: 10.1. Simple Links
arc elements and: 10.4.2. Arcs
extended links and: 10.4. Extended Links
local resources and: 10.4.3. Local Resources
locator elements and: 10.4.1. Locators
values of: 10.1. Simple Links
XML
benefits of: 1.1. The Benefits of XML
comments: 2.7. Comments
communications protocols and: 15.1.2. Communications
data format: 1.2. Portable Data
15. XML as a Data Format
displaying in web browsers: 7.2. Direct Display of XML in Browsers
documents: 2.1. XML Documents and XML Files
associating stylesheets with: 12.3. Associating Stylesheets with XML Documents
counting elements/attributes: 19.1. The ContentHandler Interface
default character set for: 5.9. The Default Character Set for XML Documents
grammar for: 20.5. XML Document Grammar
narrative-oriented: 6.2. Narrative Document Structures
nodes in: 8.7. The Built-in Template Rules
parsing with DOM: 18.6. Parsing a Document with DOM
permanence of: 6.5. Document Permanence
syntax structures: 20.3.1. Global Syntax Structures
transforming and presenting: 6.6. Transformation and Presentation
tree structure of: 9.1. The Tree Structure of an XML Document
validating: 1.1. The Benefits of XML
3.1. Validation
16.1. Overview
enterprise applications and: 15.1.1. Mixed Environments
evolution of: 1.4. The Evolution of XML
files: 2.1. XML Documents and XML Files
filters, implementing: 25.2. The org.xml.sax.helpers Package
how it works: 1.3. How XML Works
name tokens: 3.3.1.2. NMTOKEN
names: 2.4. XML Names
3.3.1.2. NMTOKEN
20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
namespaces (see namespaces)
object serialization and: 15.1.3. Object Serialization
parsers: 1.3. How XML Works
checking documents for well-formedness: 2.10. Checking Documents for Well-Formedness
checking the character set: 5.9. The Default Character Set for XML Documents
comparing namespace URIs: 4.2.3. Namespace URIs
determining character encoding: 5.1. Character-Set Metadata
event-based: 17.1.2. Treating XML as Events
handling namespaces: 4.3. How Parsers Handle Namespaces
text-processing tools and: 17.1.1. Treating XML as Text
XMLReader interface and: 25.1. The org.xml.sax Package
processing instructions (see processing instructions)
processing issues: 17.2. Common XML Processing Issues
comments: 17.2.2. Comments
links: 17.2.6. Links and References
notations: 17.2.4. Notations
references: 17.2.6. Links and References
unparsed entities: 17.2.5. Unparsed Entities
processing models: 17.1. Common XML Processing Models
abstracting XML: 17.1.5. Abstracting XML Away
event-based: 17.1.2. Treating XML as Events
extensions/standards: 17.1.6. Standards and Extensions
text-based: 17.1.1. Treating XML as Text
tree-based: 17.1.3. Treating XML as Tree Models
resources: 17. Programming Models
schema languages: 1.1. The Benefits of XML
1.4. The Evolution of XML
SGML and: 1.4. The Evolution of XML
stylesheets: 7.2. Direct Display of XML in Browsers
trees: 2.2.2. XML Trees
XML Canonicalization: 1.4. The Evolution of XML
17.2.1. What You Get Is Not What You Saw
XML declaration: 2.9. The XML Declaration
web browser problems with: 7.1.3.1. The XML declaration and processing instructions
XML specification for: 20.2. Annotated Sample Documents
20.3.1. Global Syntax Structures
XML-defined character sets: 5.4. XML-Defined Character Sets
XML Encryption: 1.4. The Evolution of XML
XML Information Set: 1.4. The Evolution of XML
XML Query Language: 1.4. The Evolution of XML
XML Schema Language (see entries under schemas)
XML Signatures: 1.4. The Evolution of XML
xml-stylesheet processing instruction: 2.8. Processing Instructions
associating stylesheets with XML documents: 12.3. Associating Stylesheets with XML Documents
pseudoattributes used in: 7.2.1. The xml-stylesheet Processing Instruction
xml-stylesheet processing instructions
XSLT stylesheets and: 8.3.2. The xml-stylesheet Processing Instruction
XML Validation Form (Brown University): 3.1.4. Validating a Document
XMLFilter interface (SAX): 19.3. Filters
25.1. The org.xml.sax Package
XMLFilterImpl class (SAX): 19.3. Filters
25.2. The org.xml.sax.helpers Package
xml:lang attribute: 5.11. xml:lang
20.3.2. DTD (Document Type Definition)
xmlns attribute
SAX core feature: 25.3. SAX Features and Properties
setting default namespaces with: 4.2.4. Setting a Default Namespace with the xmlns Attribute
20.3.4. Namespaces
in XPath data model: 9.1. The Tree Structure of an XML Document
xml.org (DTD repository): 3.10. Locating Standard DTDs
XMLReader interface (SAX): 19. Simple API for XML (SAX)
25.1. The org.xml.sax Package
ContentHandler interface and: 19.1. The ContentHandler Interface
counting elements/attributes in a document: 19.1. The ContentHandler Interface
SAX filters and: 19.3. Filters
validating parsers: 19.2. SAX Features and Properties
XMLReaderAdapter class (SAX): 25.2. The org.xml.sax.helpers Package
XMLReaderFactory class (SAX): 19. Simple API for XML (SAX)
25.2. The org.xml.sax.helpers Package
xml:space attribute: 20.3.2. DTD (Document Type Definition)
XPath: 0.. What This Book Covers
1.4. The Evolution of XML
9. XPath
arithmetic operators in: 9.6.1. Numbers
Boolean expressions: 9.6.3. Booleans
22.2. Data Types
Boolean functions: 9.7.3. Boolean Functions
comparison operators: 9.6.2. Strings
data model: 9.1. The Tree Structure of an XML Document
22.1. The XPath Data Model
expressions: 9.6. General XPath Expressions
22.2. Data Types
XSLT elements and: 8.5. Calculating the Value of an Element with xsl:value-of
functions: 9.7. XPath Functions
22.5. XPath Functions
location paths: 9.2. Location Paths
22.3. Location Paths
axes: 9.5. Unabbreviated Location Paths
node tests: 9.5. Unabbreviated Location Paths
node-set functions: 9.7.1. Node-Set Functions
node sets in: 22.2. Data Types
numbers in: 9.6.1. Numbers
22.2. Data Types
numeric functions: 9.7.4. Number Functions
predicates in location paths: 9.4. Predicates
22.3. Location Paths
22.4. Predicates
reference: 22. XPath Reference
relational operators: 9.4. Predicates
9.6.2. Strings
string functions: 9.7.2. String Functions
strings in: 9.6.2. Strings
22.2. Data Types
unabbreviated location paths: 9.5. Unabbreviated Location Paths
xpointer( ): 11.1. XPointers on URLs
XPointers: 0.. What This Book Covers
1.4. The Evolution of XML
11. XPointers
bare names: 11.3. Bare Names
changing context nodes for: 11.7.5. Relative XPointers
in links: 11.2. XPointers in Links
namespaces and: 11.5. Namespaces
points in: 11.6. Points
ranges in: 11.6. Points
11.7. Ranges
on URLs: 11.1. XPointers on URLs
xs:all element: 16.7.2. Controlling Element Placement
21.2. Schema Elements
xs:annotation element: 16.2.2. Annotations
21.2. Schema Elements
xs:any element: 16.8. Allowing Any Content
21.2. Schema Elements
xs:anyAttribute element: 16.8. Allowing Any Content
21.2. Schema Elements
xs:anySimpleType type: 21.3. Primitive Types
xs:anyType type: 16.5.1. complexContent
21.3. Primitive Types
xs:anyURI type: 16.2.4. Simple Types
21.3. Primitive Types
xs:appinfo element: 16.2.2.2. The xs:appinfo element
21.2. Schema Elements
xs:attribute element: 16.2.5. Attribute Declarations
21.2. Schema Elements
xs:attributeGroup element: 16.2.5.1. Attribute groups
21.2. Schema Elements
xs:base64Binary type: 16.2.4. Simple Types
21.3. Primitive Types
xs:boolean type: 16.2.4. Simple Types
21.3. Primitive Types
xs:byte type: 16.2.4. Simple Types
21.3. Primitive Types
xs:choice element: 16.7.2. Controlling Element Placement
21.2. Schema Elements
xs:complexContent element: 16.5.1. complexContent
21.2. Schema Elements
xs:complexType element: 16.2.5. Attribute Declarations
16.4. Complex Types
16.5.1. complexContent
21.2. Schema Elements
xs:date type: 21.3. Primitive Types
xs:dateTime type: 16.2.4. Simple Types
21.3. Primitive Types
xs:decimal type: 21.3. Primitive Types
xs:documentation element: 16.2.2.1. The xs:documentation element
21.2. Schema Elements
xs:double type: 21.3. Primitive Types
xs:duration type: 16.2.4. Simple Types
21.3. Primitive Types
xs:element element: 16.2.3. Element Declarations
21.2. Schema Elements
xs:ENTITIES type: 16.2.4. Simple Types
21.3. Primitive Types
xs:ENTITY type: 16.2.4. Simple Types
21.3. Primitive Types
xs:enumeration facet element: 16.6.2.3. Enumerations
21.2. Schema Elements
xs:extension element: 16.2.5. Attribute Declarations
16.8.2.1. Deriving by extension
21.2. Schema Elements
xs:field element: 16.9.4.1. Forcing uniqueness
21.2. Schema Elements
xs:float type: 21.3. Primitive Types
xs:fractionDigits facet element: 16.6.2.4.2. Length and precision
21.2. Schema Elements
xs:gDay type: 21.3. Primitive Types
xs:gMonth type: 21.3. Primitive Types
xs:gMonthDay type: 21.3. Primitive Types
xs:group element: 16.7.3. Using Groups
21.2. Schema Elements
xs:gYear type: 21.3. Primitive Types
xs:gYearMonth type: 21.3. Primitive Types
xs:hexBinary type: 21.3. Primitive Types
xs:ID type: 16.2.4. Simple Types
21.3. Primitive Types
xs:IDREF type: 16.2.4. Simple Types
21.3. Primitive Types
xs:IDREFS type: 16.2.4. Simple Types
21.3. Primitive Types
xs:import element: 16.8.1.3. Importing schemas for other namespaces
21.2. Schema Elements
xs:include element: 16.8.1.1. Including external declarations
21.2. Schema Elements
xsi:nil attribute: 21.4. Instance Document Attributes
xsi:noNamespaceSchemaLocation attribute: 16.1. Overview
21.4. Instance Document Attributes
xs:int type: 21.3. Primitive Types
xs:integer type: 16.2.4. Simple Types
21.3. Primitive Types
xsi:schemaLocation attribute: 16.1. Overview
16.3.1. Target Namespaces
21.4. Instance Document Attributes
xsi:type attribute: 16.8.2.3. Using derived types
21.4. Instance Document Attributes
xs:key element: 16.9.4.2. Keys and references
21.2. Schema Elements
xs:keyref element: 16.9.4.2. Keys and references
21.2. Schema Elements
XSL (Extensible Stylesheet Language): 1.4. The Evolution of XML
XSL-FO (XSL Formatting Objects): 1.1. The Benefits of XML
1.4. The Evolution of XML
12. Cascading Style Sheets (CSS)
13. XSL Formatting Objects (XSL-FO)
documents
boxes in: 13.1. XSL Formatting Objects
generating: 13.3.2. Generating the Finished Document
programs for working with: 13.3.2. Generating the Finished Document
structure of: 13.2. The Structure of an XSL-FO Document
laying out master pages: 13.3. Laying Out the Master Pages
properties: 13.4. XSL-FO Properties
vs. CSS: 13.5. Choosing Between CSS and XSL-FO
xs:language type: 16.2.4. Simple Types
21.3. Primitive Types
xsl:apply-imports element: 23.2. XSLT Elements
xsl:apply-templates element: 8.6. Applying Templates with xsl:apply-templates
23.2. XSLT Elements
xsl:attribute element: 23.2. XSLT Elements
xsl:attribute-set element: 23.2. XSLT Elements
xsl:call-template element: 23.2. XSLT Elements
xsl:choose element: 23.2. XSLT Elements
xsl:comment element: 23.2. XSLT Elements
xsl:copy element: 23.2. XSLT Elements
xsl:copy-of element: 23.2. XSLT Elements
xsl:decimal-format element: 9.7.2. String Functions
23.2. XSLT Elements
23.3. XSLT Functions
xsl:element element: 23.2. XSLT Elements
xs:length facet element: 16.6.2.2. Restricting length
21.2. Schema Elements
xsl:fallback element: 23.2. XSLT Elements
xsl:for-each element: 23.2. XSLT Elements
xsl:if element: 23.2. XSLT Elements
xsl:import element: 23.2. XSLT Elements
xsl:include element: 23.2. XSLT Elements
xs:list element: 16.6.2.6. Lists
21.2. Schema Elements
xsl:key element: 23.2. XSLT Elements
xsl:message element: 23.2. XSLT Elements
xsl:namespace-alias element: 23.2. XSLT Elements
xsl:number element: 23.2. XSLT Elements
xs:long type: 21.3. Primitive Types
xsl:otherwise element: 23.2. XSLT Elements
xsl:output element: 23.2. XSLT Elements
xsl:param element: 23.2. XSLT Elements
xsl:preserve-space element: 23.2. XSLT Elements
xsl:processing-instruction element: 23.2. XSLT Elements
xsl:sort element: 23.2. XSLT Elements
xsl:strip-space element: 23.2. XSLT Elements
xsl:stylesheet element: 8.2. xsl:stylesheet and xsl:transform
23.2. XSLT Elements
XSLT: 0.. What This Book Covers
1.4. The Evolution of XML
8. XSL Transformations (XSLT)
functions: 23.3. XSLT Functions
Internet Explorer and: 7.2.2. Internet Explorer
Mozilla and: 7.2.3. Netscape and Mozilla
Netscape and: 7.2.3. Netscape and Mozilla
processors: 8. XSL Transformations (XSLT)
8.3. Stylesheet Processors
problems with incorrect namespace URIs: 8.2. xsl:stylesheet and xsl:transform
transforming XML documents: 6.6. Transformation and Presentation
17.1.4. Transformations
XSLT stylesheets: 8. XSL Transformations (XSLT)
adding templates to: 8.4. Templates and Template Rules
attribute value templates: 8.9. Attribute Value Templates
built-in template rules and: 8.7. The Built-in Template Rules
elements, categories of: 23.2. XSLT Elements
flowing content into pages: 13.3.1. Flowing Content into the Pages
MIME media type for: 7.2.1.1. The required href and type pseudoattributes
modes and: 8.8. Modes
namespaces and: 8.10. XSLT and Namespaces
23.1. The XSLT Namespace
rddl:resource elements and: 14.2. RDDL Syntax
transforming documents into XSL-FO documents: 13.3.2. Generating the Finished Document
13.4. XSL-FO Properties
XSL-FO and: 13. XSL Formatting Objects (XSL-FO)
xsl:stylesheet element and: 8.2. xsl:stylesheet and xsl:transform
23.2. XSLT Elements
xsl:template element: 8.4. Templates and Template Rules
23.2. XSLT Elements
xsl:text element: 23.2. XSLT Elements
xsl:transform element: 23.2. XSLT Elements
xsl:value-of element: 8.5. Calculating the Value of an Element with xsl:value-of
23.2. XSLT Elements
string( ) and: 9.7.2. String Functions
xsl:variable element: 23.2. XSLT Elements
xsl:vendor property: 23.3. XSLT Functions
xsl:vendor-url property: 23.3. XSLT Functions
xsl:version property: 23.3. XSLT Functions
xsl:when element: 23.2. XSLT Elements
xsl:with-param element: 23.2. XSLT Elements
xs:maxExclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
xs:maxInclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
xs:maxLength facet element: 16.6.2.2. Restricting length
21.2. Schema Elements
xs:minExclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
xs:minInclusive facet element: 16.6.2.4.1. Minimum and maximum values
21.2. Schema Elements
xs:minLength facet element: 16.6.2.2. Restricting length
21.2. Schema Elements
xs:Name type: 16.2.4. Simple Types
21.3. Primitive Types
xs:NCName type: 21.3. Primitive Types
xs:negativeInteger type: 21.3. Primitive Types
xs:NMTOKEN type: 16.2.4. Simple Types
21.3. Primitive Types
xs:NMTOKENS type: 16.2.4. Simple Types
21.3. Primitive Types
xs:nonNegativeInteger type: 21.3. Primitive Types
xs:nonPositiveInteger type: 21.3. Primitive Types
xs:normalizedString type: 16.2.4. Simple Types
21.3. Primitive Types
xs:notation element: 21.2. Schema Elements
xs:NOTATION type: 16.2.4. Simple Types
21.3. Primitive Types
xs:pattern facet element: 16.6.2.5. Enforcing format
21.2. Schema Elements
xs:positiveInteger type: 21.3. Primitive Types
xs:QName type: 21.3. Primitive Types
xs:redefine element: 16.8.1.2. Modifying external declarations
21.2. Schema Elements
xs:restriction element: 16.6.2. Facets
16.8.2.2. Deriving by restriction
21.2. Schema Elements
xs:schema element: 16.2.1. Document Organization
21.2. Schema Elements
controlling namespace qualification: 16.3.2. Controlling Qualification
target namespaces and: 16.3.1. Target Namespaces
xs:selector element: 16.9.4.1. Forcing uniqueness
21.2. Schema Elements
xs:sequence element: 16.4. Complex Types
16.7.2. Controlling Element Placement
21.2. Schema Elements
xs:short type: 21.3. Primitive Types
xs:simpleContent element: 16.2.5. Attribute Declarations
16.6. Simple Content
21.2. Schema Elements
xs:simpleType element: 16.4. Complex Types
16.6. Simple Content
21.2. Schema Elements
xs:string type: 16.2.4. Simple Types
16.6. Simple Content
21.3. Primitive Types
xs:time type: 21.3. Primitive Types
xs:token type: 16.2.4. Simple Types
21.3. Primitive Types
xs:totalDigits facet element: 16.6.2.4.2. Length and precision
21.2. Schema Elements
xs:union element: 16.6.2.7. Unions
21.2. Schema Elements
xs:unique element: 16.9.4.1. Forcing uniqueness
21.2. Schema Elements
xs:unsignedByte type: 21.3. Primitive Types
xs:unsignedInt type: 21.3. Primitive Types
xs:unsignedLong type: 21.3. Primitive Types
xs:unsignedShort type: 21.3. Primitive Types
xs:whiteSpace facet element: 16.6.2.1. Handling whitespace
21.2. Schema Elements

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Y

Yiddish language, Unicode block for: 26.3.9. Hebrew

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Z

Zapf Dingbats font, Unicode block for: 26.3.26. Dingbats

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Colophon (XML in a Nutshell, 2nd Edition)

 XML in a NutshellSearch this book
Previous

Colophon

Our look is the result of reader comments, our own experimentation, and feedback from distribution channels. Distinctive covers complement our distinctive approach to technical topics, breathing personality and life into potentially dry subjects.

The bird on the cover of XML in a Nutshell, Second Edition, is a peafowl, the largest bird in the Phasianinae family, which also includes pheasants and turkeys. People often incorrectly call peafowl peacocks. Peacocks are actually male peafowl; the females are called peahens. Two wild peafowl species exist today: the Indian peafowl (Pavo cristatus) and the Green peafowl of Southeast Asia (Pavo muticus), which may be endangered. These wild peafowl live in musters of 8 to 12 birds in dense forest near water. Though they do not fly very well, and do so only for short distances, they do manage to escape most predators and roost peacefully at night, high up in treetops.

The peafowl's most famous characteristic, of course, is its beautiful fan of feathers, known as a "train." Each blue-green train feather has a dark spot on its tip that looks much like an eye. Peacocks develop especially brilliant plumage, an indicator of sexual maturity, by age three. A healthy peacock has a full and vibrant train each year during the spring mating season. During this period, peacocks strut their stuff-display their "breeding plumage," as it is called-to attract peahens. Scientists theorize that the peacock's performance plays upon the peahen's instinctive drives to find healthy mates in the hope of producing hardy offspring. Each summer after the mating season, peafowl shed their train feathers, which are often collected by humans as eye-catching souvenirs.

Jeffrey Holcomb was the production editor and copyeditor for XML in a Nutshell, Second Edition. Jane Ellin and Sarah Sherman were the proofreaders. Matt Hutchinson, Tatiana Apandi Diaz, and Claire Cloutier provided quality control. Linley Dolby and Mary Brady provided production assistance. Judy Hoer wrote the index.

Ellie Volckhausen designed the cover of this book, based on a series design by Edie Freedman. The cover image is an original illustration created by Susan Hart. Emma Colby produced the cover layout with QuarkXPress 4.1 using Adobe's ITC Garamond font.

Melanie Wang designed the interior layout, based on a series design by David Futato. This book was converted to FrameMaker 5.5.6 with a format conversion tool created by Erik Ray, Jason McIntosh, Neil Walls, and Mike Sierra that uses Perl and XML technologies. The text font is Linotype Birka; the heading font is Adobe Myriad Condensed; and the code font is LucasFont's TheSans Mono Condensed. Additional fonts used in this book include TibetanMachine, Code2000, Adobe MathematicalPi2, and Adobe MathematicalPi6. The illustrations that appear in the book were produced by Robert Romano and Jessamyn Read using Macromedia FreeHand 9 and Adobe Photoshop 6. The tip and warning icons were drawn by Christopher Bing. This colophon was written by Sarah Jane Shangraw and Molly Shangraw.

Previous | Home
Index | Book Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT

by Doug Tidwell
ISBN 0-596-00053-7
First Edition, published August 2001.
(See the catalog page for this book.)

Table of Contents

Copyright Page
Dedication
Preface
Chapter 1: Getting Started
Chapter 2: The Obligatory Hello World Example
Chapter 3: XPath: A Syntax for Describing Needles and Haystacks
Chapter 4: Branching and Control Elements
Chapter 5: Creating Links and Cross-References
Chapter 6: Sorting and Grouping Elements
Chapter 7: Combining XML Documents
Chapter 8: Extending XSLT
Chapter 9: Case Study: The Toot-O-Matic
Appendix A: XSLT Reference
Appendix B: XPath Reference
Appendix C: XSLT and XPath Function Reference
Appendix D: XSLT Guide
Glossary
Index
Colophon
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Copyright (XSLT)

 XSLTSearch this book

Copyright © 2001 O'Reilly & Associates, Inc. All rights reserved.

Printed in the United States of America.

Published by O'Reilly & Associates, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

O'Reilly & Associates books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://safari.oreilly.com). For more information contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Nutshell Handbook, the Nutshell Handbook logo, and the O'Reilly logo are registered trademarks of O'Reilly & Associates, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O'Reilly & Associates, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps. The association between the image of a jabiru and XSLT is a trademark of O'Reilly & Associates, Inc.

While every precaution has been taken in the preparation of this book, the publisher assumes no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Previous | Home | Next
Table of Contents | Book Index | 0. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Dedication (XSLT)

 XSLTSearch this book

Dedication

To my family--my wonderful wife, Sheri Castle, and our amazing daughter, Lily--for their support and understanding. Nothing I do would be possible without their help.

Previous | Home | Next
Copyright Page | Book Index | 0.1. Preface
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Preface (XSLT)

 XSLTSearch this book

Preface

0.1. Organization of This Book

The heart of this book is designed to take you through the basics of XSLT and help you solve problems with this exciting new technology. It contains the following chapters:

Chapter 1, "Getting Started", covers the basics of XML and discusses how to install the Xalan stylesheet engine.

Chapter 2, "The Obligatory Hello World Example", takes a look at an XML-tagged "Hello World" document, then examines stylesheets that transform it into other things.

Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks", covers the basics of XPath, the language used to describe parts of an XML document.

Chapter 4, "Branching and Control Elements", discusses the logic elements of XSLT (<xsl:if> and <xsl:choose>) and how they work.

Chapter 5, "Creating Links and Cross-References", Covers the different ways to build links between elements in XML documents. Using XPath to describe relationships between related elements is also covered.

Chapter 6, "Sorting and Grouping Elements", Goes over the <xsl:sort> element and discusses various ways to sort elements in an XML document. It also talks about how to do grouping with various XSLT elements and functions.

Chapter 7, "Combining XML Documents", discusses the document() function, which allows you to combine several XML documents, then write a stylesheet that works against the collection of documents.

Chapter 8, "Extending XSLT", > explains how you can write extension elements and extension functions. Although XSLT and XPath are extremely powerful and flexible, there are still times when you need to do something that isn't provided by the language itself.

Chapter 9, "Case Study: The Toot-O-Matic", > goes through a complicated case study that puts XSLT through its paces. The chapter reviews the Toot-O-Matic, an XSLT tool that generates a wide range of output documents from a single file.

The last section of the book contains reference information:

Appendix A, "XSLT Reference", an alphabetical listing of all the elements defined by XSLT, with a discussion of all attributes of those elements and how they were designed to be used.

Appendix B, "XPath Reference", a listing of various aspects of XPath, including datatypes, axes, node types, and operators.

Appendix C, "XSLT and XPath Function Reference", > an alphabetical listing of all the functions defined by XPath and XSLT.

Appendix D, "XSLT Guide", a series of "How do I...?" questions, with brief examples and/or references to sections of the book that explain how to do common tasks.

Glossary, a glossary of terms used in XSLT, XPath, and XML in general.

Previous | Home | Next
Copyright Page | Book Index | 0.2. Where I'm Coming From
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Where I'm Coming From (XSLT)

 XSLSearch this book

0.2. Where I'm Coming From

Before we begin, it's only fair that I tell you my biases.

0.2.1. I Believe in Open, Platform-Neutral, Standards-Based Computing

If any part of your business life ties you down to anything closed, proprietary, or platform-specific, I encourage you to make some changes. This book shows you how to take charge of your data and move it from one place to another on your terms, not your software vendor's. XML is shifting the balance of power from software vendors to software users. If your tools force you to work in unnatural ways or refuse to let you have your data when and where you want it, you don't have to take it anymore.

0.2.2. I Assume You're Busy

This book is written for developers who want to learn how to use XSLT to solve problems. Throughout the book, we'll transform XML-tagged data into a variety of useful things. If a particular bit of arcana from the specifications doesn't relate to any practical problem I can think of, it's probably mentioned in the reference section only.

0.2.3. I Don't Care Which Standards-Compliant Tools You Use

Most examples in this book are done with Apache's Xalan XSLT engine, which is free, open source, cross-platform, and standards compliant. I use Xalan for two reasons: I've been using it longer than the others out there, and it has more developers working on it than any other XSLT engine I'm aware of. Unless otherwise stated, all examples in this book also work with Michael Kay's Saxon, Microsoft's XSLT tools, James Clark's XT, and Oracle's XML parser.

My job as an author and a teacher is to show you how to use free, standards-compliant tools to simplify your life. I'm not here to sell you a parser, an XSLT processor, a toaster, or anything else, so please use whatever tools you like. I encourage you to take a look at all of the tools out there and find your own preferences.

The XSLT processors mentioned in this book are:

Xalan
Xalan is the Apache XML Project's XSLT engine. It was originally built on the LotusXSL code base. It's available at http://xml.apache.org/xalan-j/. Every example in this book was developed and tested using Xalan. Except where noted, examples should work on all other standards-compliant XSLT processors as well, although I haven't tested other tools as thoroughly.

Saxon
Saxon was written by Michael Kay, an XSLT author and a frequent contributor to the XSLT mailing list. You can find it at http://saxon.sourceforge.net.

Microsoft's XSLT tools
As of this writing, Microsoft's XSLT processor is part of the MSXML parser, available at http://www.microsoft.com/xml. (This site seems to be redesigned frequently, so be prepared to spend some time looking. Also, there are rumors that the packaging and names of the tools may change.) The most exciting thing about this processor is that it can be integrated with the Internet Explorer browser, allowing you to transform XML documents on a client machine. By merely pointing your browser at an XML document, you can have the document transformed and rendered as HTML automatically. (For this to work, you must associate a stylesheet with the XML document. See Section 1.2.4.8, "Associating stylesheets with XML documents" in Chapter 1, "Getting Started" for more information.)

XT
XT was written by James Clark, the editor of the XSLT specification and the co-editor of the XPath specification. You can find it at http://www.jclark.com/xml/xt.html. Much of the code in XT was written to test the XSLT and XPath specs as they were developed. (Even though it is still a very popular XSLT processor, XT is no longer actively developed.)

Oracle
Oracle's XML parser is different from most because it includes the XSLT processor and the XML parser in a single package. It's free at Oracle's TechNet site (http://technet.oracle.com/tech/xml), although you do have to register before you can download it.

0.2.4. XSLT Is a Tool, Not a Religion

An old adage says that to a person with a hammer, everything looks like a nail. I don't claim that XSLT is the solution to every business problem you'll encounter. The next chapter discusses reasons why XML and XSLT were created, the design decisions behind XSLT, and tries to identify the kinds of problems XSLT is designed to solve. All chapters in this book illustrate common scenarios in which XSLT is extremely powerful and useful.

That being said, if a particular tool does something better than XSLT does, by all means, use that other tool. XSLT is a powerful addition to your tool box, but that doesn't mean you should throw out all your other tools.

Previous | Home | Next
0. Preface | Book Index | 0.3. Conventions Used in This Book
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Conventions Used in This Book (XSLT)

 XSLSearch this book

0.3. Conventions Used in This Book

Items appearing in this book are sometimes given a special appearance to set them apart from the regular text. Here's how they look:

Italic
Used for citations of books and articles, commands, email addresses, and URLs

Constant width
Used for literals, constant values, code listings, and XML markup

Constant-width bold
Used to indicate user input

Constant-width italic
Used for replaceable parameter and variable names

NOTE:

This icon represents a tip, suggestion, or general note.

WARNING:
This icon represents a warning or caution.
Previous | Home | Next
0.2. Where I'm Coming From | Book Index | 0.4. How to Contact Us
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How to Contact Us (XSLT)

 XSLSearch this book

0.4. How to Contact Us

We have tested and verified the information in this book to the best of our ability, but you may find that features have changed (or even that we have made mistakes!). Please let us know about any errors you find, as well as your suggestions for future editions, by writing to:

O'Reilly & Associates, Inc.
101 Morris Street
Sebastopol, CA 95472
(800) 998-9938 (in the United States or Canada)
(707) 829-0515 (international/local)
(707) 829-0104 (fax)

To ask technical questions or comment on the book, send email to:

bookquestions@oreilly.com

The web site for this book lists examples, errata, and plans for future editions. You can access this page at:

http://www.oreilly.com/catalog/xslt

For more information about our books, conferences, software, resource centers, and the O'Reilly Network, see our web site:

http://www.oreilly.com
Previous | Home | Next
0.3. Conventions Used in This Book | Book Index | 0.5. Acknowledgments
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Acknowledgments (XSLT)

 XSLSearch this book

0.5. Acknowledgments

First and foremost, I'd like to thank the reviewers of this book. David Marston of Lotus was the lead reviewer; David, thank you so much for your comments, wisdom, and knowledge. Along the way, I also got a lot of good feedback and encouragement from Tony Colle, Slavko Malesvic, Dr. Joe Molitoris, Shane O'Donnell, Andy Piper, Sreenivas Ramarao, Mike Riley, and Willie Wheeler. This book is significantly better because of your comments and other efforts.

I'd also like to thank my teammates at developerWorks for encouraging me to undertake this project. Taking on an additional full-time job hasn't been easy, but their advice, flexibility, and understanding as I've tried to balance my responsibilities has been invaluable. Even more valuable is the fact that I'm surrounded by some of the most interesting, creative, and remarkable people I've ever known. You guys rule.

For the times I've been at home (in Raleigh, North Carolina), I've depended on my nutritional advisors at Schiano's Pizza: "Hey, you want your usual?" (Slight pause.) "Yeah, that'd be great, thanks." Nothing's as comforting as a couple of slices. If you're within a day's drive of Raleigh, I strongly encourage you to visit.

Finally, I'd like to thank the staff at O'Reilly, especially Laurie Petrycki and Simon St.Laurent. Laurie, thank you for convincing me to take on this project and for sticking with me when my ability to find the time to write was in doubt. Simon, I've enjoyed reading your books for years; it's been an honor to work with you. Your guidance, technical insight, patience, and suggestions were invaluable.

Thanks so much to all of you!

Previous | Home | Next
0.4. How to Contact Us | Book Index | 1. Getting Started
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Getting Started (XSLT)

 XSLTSearch this book

Chapter 1. Getting Started

Contents:

The Design of XSLT
XML Basics
Installing Xalan
Summary

In this chapter, we review the design rationale behind XSLT and XPath and discuss the basics of XML. We also talk about other web standards and how they relate to XSLT and XPath. We conclude the chapter with a brief discussion of how to set up an XSLT processor on your machine so you can work with the examples throughout the book.

1.1. The Design of XSLT

XML has gone from working group to entrenched buzzword in record time. Its flexibility as a language for presenting structured data has made it the lingua franca for data interchange. Early adopters used programming interfaces such as the Document Object Model (DOM) and the Simple API for XML (SAX) to parse and process XML documents. As XML becomes mainstream, however, it's clear that the average web citizen can't be expected to hack Java, Visual Basic, Perl, or Python code to work with documents. What's needed is a flexible, powerful, yet relatively simple, language capable of processing XML.

What's needed is XSLT.

XSLT, the Extensible Stylesheet Language for Transformations, is an official recommendation of the World Wide Web Consortium (W3C). It provides a flexible, powerful language for transforming XML documents into something else. That something else can be an HTML document, another XML document, a Portable Document Format (PDF) file, a Scalable Vector Graphics (SVG) file, a Virtual Reality Modeling Language (VRML) file, Java code, a flat text file, a JPEG file, or most anything you want. You write an XSLT stylesheet to define the rules for transforming an XML document, and the XSLT processor does the work.

The W3C has defined two families of standards for stylesheets. The oldest and simplest is Cascading Style Sheets (CSS), a mechanism used to define various properties of markup elements. Although CSS can be used with XML, it is most often used to style HTML documents. I can use CSS properties to define that certain elements be rendered in blue, or in 58-point type, or in boldface. That's all well and good, but there are many things that CSS can't do:

  • CSS can't change the order in which elements appear in a document. If you want to sort certain elements or filter elements based on a certain property, CSS won't do the job.

  • CSS can't do computations. If you want to calculate and output a value (maybe you want to add up the numeric value of all <price> elements in a document), CSS won't do the job.

  • CSS can't combine multiple documents. If you want to combine 53 purchase order documents and print a summary of all items ordered in those purchase orders, CSS won't do the job.

WARNING: Don't take this section as a criticism of CSS; XSLT and CSS were designed for different purposes. One fairly common use of XSLT is to generate an HTML document that contains CSS elements. See Section 3.5, "The XPath View of an XML Document" in Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks" for an example that uses XSLT to generate CSS properties.

XSLT was created to be a more powerful, flexible language for transforming documents. In this book, we go through all the features of XSLT and discuss each of them in terms of practical examples. Some of XSLT's design goals specify that:

  • An XSLT stylesheet should be an XML document. This means that you can write a stylesheet that transforms a second stylesheet into another stylesheet (we actually do this in Chapter 4, "Branching and Control Elements"). This kind of recursive thinking is common in XSLT.

  • The XSLT language should be based on pattern matching. Most of our stylesheets consist of rules (called templates in XSLT) used to transform a document. Each rule says, "When you see part of a document that looks like this, here's how you convert it into something else." This is probably different from any programming you've previously done.

  • XSLT should be designed to be free of side effects. In other words, XSLT is designed to be optimized so that many different stylesheet rules could be applied simultaneously. The biggest impact of this is that variables can't be modified. Once a variable is initialized, you can't change its value; if variables could be changed, then processing one stylesheet rule might have side effects that impact other stylesheet rules. This is almost certainly different from any programming you've previously done.

    XSLT is heavily influenced by the design of functional programming languages, such as Lisp, Scheme, and Haskell. These languages also feature immutable variables. Instead of defining the templates of XSLT, functional programming languages define programs as a series of functions, each of which generates a well-defined output (free from side effects, of course) in response to a well-defined input. The goal is to execute the instructions of a given XSLT template without affecting the execution of any other XSLT template.

  • Instead of looping, XSLT uses iteration and recursion. Given that variables can't be changed, how do you do something like a for or do-while loop? XSLT uses two equivalent techniques: iteration and recursion. Iteration means that you can write an XSLT template that says, "get all the things that look like this, and here's what I want you to do with each of them." Although that's different from a do-while loop, usually what you do in a procedural language is something like, "do this while there are any items left to process." In that case, iteration does exactly what you want.

    Recursion takes some getting used to. If you must implement something like a for statement (for i=1 to 10 do, for example), recursion is the way to go. There are a number of examples of recursion throughout the book; you can flip ahead to Section 4.7, "A Stylesheet That Emulates a for Loop" in Chapter 4, "Branching and Control Elements" for more information.

Given these design goals, what are XSLT's strengths? Here are some scenarios:

  • Your web site needs to deliver information to a variety of devices. You need to support ordinary desktop browsers, as well as pagers, mobile phones, and other low-resolution, low-function devices. It would be great if you could create your information in structured documents, then transform those documents into all the formats you need.

  • You need to exchange data with your partners, but all of you use different database systems. It would be great if you could define a common XML data format, then transform documents written in that format into the import files you need (SQL statements, comma-separated values, etc.).

  • To stay on the cutting edge, your web site gets a complete visual redesign every few months. Even though things such as server-side includes and CSS can help, they can't do everything. It would be great if your data were in a flexible format that could be transformed into any look and feel, simplifying the redesign process.

  • You have documents in several different formats. All the documents are machine-readable, but it's a hassle to write programs to parse and process all of them. It would be great if you could combine all of the documents into a single format, then generate summary documents and reports based on that collection of documents. It would be even better if the report could contain calculated values, automatically generated graphics, and formatting for high-quality printing.

Throughout the book, we'll demonstrate XSLT solutions for problems just like these. Most chapters focus on particular techniques, such as sorting, grouping, and generating links between pieces of data. We wrap up with a case study that discusses a real-world content-management scenario and illustrates how XSLT was used to solve a number of problems.

Previous | Home | Next
0.5. Acknowledgments | Book Index | 1.2. XML Basics
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Basics (XSLT)

 XSLSearch this book

1.2. XML Basics

Almost everything we do in this book deals with XML documents. XSLT stylesheets are XML documents themselves, and they're designed to transform an XML document into something else. If you don't have much experience with XML, we'll review the basics here. For more information on XML, check out Erik T. Ray's Learning XML (O'Reilly, 2001) and Elliotte Rusty Harold and W. Scott Means's XML in a Nutshell (O'Reilly, 2001).

1.2.1. XML's Heritage

XML's heritage is in the Standard Generalized Markup Language (SGML). Created by Dr. Charles Goldfarb in the 1970s, SGML is widely used in high-end publishing systems. Unfortunately, SGML's perceived complexity prevented its widespread adoption across the industry (SGML also stands for "sounds great, maybe later"). SGML got a boost when Tim Berners-Lee based HTML on SGML. Overnight, the whole computing industry was using a markup language to build documents and applications.

The problem with HTML is that its tags were designed for the interaction between humans and machines. When the Web was invented in the late 1980s, that was just fine. As the Web moved into all aspects of our lives, HTML was asked to do lots of strange things. We've all built HTML pages with awkward table structures, 1-pixel GIFs, and other nonsense just to get the page to look right in the browser. XML is designed to get us out of this rut and back into the world of structured documents.

Whatever its limitations, HTML is the most popular markup language ever created. Given its popularity, why do we need XML? Consider this extremely informative HTML element:

<td>12304</td>

What does this fascinating piece of content represent?

  • Is it the postal code for Schenectady, New York?

  • Is it the number of light bulbs replaced each month in Las Vegas?

  • Is it the number of Volkswagens sold in Hong Kong last year?

  • Is it the number of tons of steel in the Sydney Harbour Bridge?

The answer: maybe, maybe not. The point of this silly example is that there's no structure to this data. Even if we included the entire table, it takes intelligence (real, live intelligence, the kind between your ears) to make sense of this data. If you saw this cell in a table next to another cell that contained the text "Schenectady," and the heading above the table read "Postal Codes for the State of New York," as a human being, you could interpret the contents of this cell correctly. On the other hand, if you wanted to write a piece of code that took any HTML table and attempted to determine whether any of the cells in the table contained postal codes, you'd find that difficult, to say the least.

Most HTML pages have one goal in mind: the appearance of the document. Veterans of the markup industry know that this is definitely not the way to create content. The separation of content and presentation is a long-established tenet of the publishing industry; unfortunately, most HTML pages aren't even close to approaching this ideal. An XML document should contain information, marked up with tags that describe what all the pieces of information are, as well as the relationship between those items. Presenting the document (also known as rendering) involves rules and decisions separate from the document itself. As we work through dozens of sample documents and applications, you'll see how delaying the rendering decisions as long as possible has significant advantages.

Let's look at another marked-up document. Consider this:

<?xml version="1.0"?>
<postalcodes>
  <title>Most-used postal codes in November 2000</title>
  <item>
    <city>Schenectady</city>
    <postalcode>12304</postalcode>
    <usage-count>2039</usage-count>
  </item>
  <item>
    <city>Kuala Lumpur</city>
    <postalcode>57000</postalcode>
    <usage-count>1983</usage-count>
  </item>
  <item>
    <city>London</city>
    <postalcode>SW1P 4RG</postalcode>
    <usage-count>1722</usage-count>
  </item>
  ...
</postalcodes>

Although we're still in the realm of contrived examples, it would be fairly easy to write a piece of code to find the postal codes in any document that used this set of tags (as opposed to HTML's <table>, <tr>, <td>, etc.). Our code would look for the contents of any <postalcode> elements in the document. (Not to get ahead of ourselves here, but writing an XSLT stylesheet to do this might take all of 30 minutes, including a 25-minute nap.) A well-designed XML document identifies each piece of data in the document and models the relationships between those pieces of data. This means we can be confident that we're processing an XML document correctly.

Again, the key idea here is that we're separating content from presentation. Our XML document clearly delineates the pieces of data and puts them into a format we can parse easily. In this book, we illustrate a number of techniques for transforming this XML document into a variety of formats. Among other things, we can transform the item <postalcode>12304</postalcode> into <td>12304</td>.

1.2.2. XML Document Rules

Continuing our trip through the basics of XML, there are several rules you need to keep in mind when creating XML documents. All stylesheets we develop in this book are themselves XML documents, so all the rules of XML documents apply to everything we do. The rules are pretty simple, even though the vast majority of HTML documents don't follow them.

One important point: The XML 1.0 specification makes it clear that when an XML parser finds an XML document that breaks the rules, the parser is supposed to throw an exception and stop. The parser is not allowed to guess what the document structure should actually be. This specification avoids recreating the HTML world, where lots of ugly documents are still rendered by the average browser.

1.2.2.1. An XML document must be contained in a single element

The first element in your XML document must contain the entire document. That first element is called the document element or the root element. If more than one document element is in the document, the XML parser throws an exception. This XML document is perfectly legal:

<?xml version="1.0"?>
<greeting>
  Hello, World!
</greeting>

(To be precise, this document is well-formed. XML documents are described as well-formed and valid; we'll define those terms in a minute.) This XML document isn't legal at all:

<?xml version="1.0"?>
<greeting>
  Hello, World!
</greeting>
<greeting>
  Hey, Y'all!
</greeting>

There are two root elements in this document, so an XML parser refuses to process it. Also, be aware that the XML declaration (the <?xml version="1.0"?> part, more on this in a minute) isn't an element at all.

1.2.2.2. All elements must be nested

If you start one element inside another, you have to end it there, too. An HTML browser is happy to render this document:

<b>I really, <i>really</b> like XML.</i>

But an XML parser will throw an exception when it sees this document. If you want the same effect, you would need to code this:

<b>I really, <i>really</i></b><i> like XML.</i>

1.2.2.3. All attributes must be quoted

You can quote the attributes with either single quotes or double quotes. These two XML tags are equivalent:

<a href="http://www.oreilly.com">
<a href='http://www.oreilly.com'>

If you need to define an attribute with the value , you can use single quotes inside double quotes, as we just did. If you need both single and double quotes in an attribute, use the predefined entities &quot; for double quotes and &apos; for single quotes.

One more note: XML doesn't allow attributes without values. In other words, HTML elements like <ol compact> aren't valid in XML. To code this element in XML, you'd have to give the attribute a value, as in <ol compact="yes">.

1.2.2.4. XML tags are case-sensitive

In HTML, <h1> and <H1> are the same. In XML, they're not. If you try to end an <h1> element with </H1>, the parser will throw an exception.

1.2.2.5. All end tags are required

This is another area where most HTML documents break. Your browser doesn't care whether you don't have a </p> or </br> tag, but your XML parser does.

1.2.2.6. Empty tags can contain the end marker

In other words, these two XML fragments are identical:

<lily age="6"></lily>

<lily age="6"/>

Notice that there is nothing, not even whitespace, between the start tag and the end tag in the first example; that's what makes this an empty tag.

1.2.2.7. XML declarations

Some XML documents begin with an XML declaration. An XML declaration is a line similar to this:

<?xml version="1.0" encoding="ISO-8859-1"?>

If no encoding is specified, the XML parser assumes you're using UTF-8, a Unicode standard that uses different numbers of bytes to represent virtually every character and ideograph from the world's languages. Be aware that each parser supports a different set of encodings, so you need to check your parser's documentation to find out what your options are.

1.2.2.8. Document Type Definitions (DTDs) and XML Schemas

All of the rules we've discussed so far apply to all XML documents. In addition, you can use DTDs and Schemas to define other constraints for your XML documents. DTDs and Schemas are metalanguages that let you define the characteristics of an XML vocabulary. For example, you might want to specify that any XML document describing a purchase order must begin with a <po> element, and the <po> element in turn contains a <customer-id> element, one or more <item-ordered> elements, and an <order-date> element. In addition, each <item-ordered> element must contain a part-number attribute and a quantity attribute.

Here's a sample DTD that defines the constraints we just mentioned:

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT po (customer-id , item-ordered+ , order-date)>

<!ELEMENT customer-id (#PCDATA)>

<!ELEMENT item-ordered EMPTY>

<!ATTLIST item-ordered  part-number CDATA  #REQUIRED
                        quantity    CDATA  #REQUIRED >
<!ELEMENT order-date EMPTY>

<!ATTLIST order-date  day   CDATA  #REQUIRED
                      month CDATA  #REQUIRED
                      year  CDATA  #REQUIRED >

And here's an XML Schema that defines the same document type:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema">

  <xsd:element name="po">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="customer-id"/>
        <xsd:element ref="item-ordered" maxOccurs="unbounded"/>
        <xsd:element ref="order-date"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>

  <xsd:element name="customer-id" type="xsd:string"/>

  <xsd:element name="item-ordered">
    <xsd:complexType>
      <xsd:attribute name="part-number" use="required">
        <xsd:simpleType>
          <xsd:restriction base="xsd:string">
            <xsd:pattern value="[0-9]{5}-[0-9]{4}-[0-9]{5}"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:attribute>
      <xsd:attribute name="quantity" use="required" type="xsd:integer"/>
    </xsd:complexType>
  </xsd:element>

  <xsd:element name="order-date">
    <xsd:complexType>
      <xsd:attribute name="day" use="required">
        <xsd:simpleType>
          <xsd:restriction base="xsd:integer">
            <xsd:maxInclusive value="31"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:attribute>
      <xsd:attribute name="month" use="required">
        <xsd:simpleType>
          <xsd:restriction base="xsd:integer">
            <xsd:maxInclusive value="12"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:attribute>
      <xsd:attribute name="year" use="required">
        <xsd:simpleType>
          <xsd:restriction base="xsd:integer">
            <xsd:maxInclusive value="2100"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:attribute>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Schemas have two significant advantages over DTDs:

  • They can define datatypes and other complex structures that are difficult or impossible to do in a DTD. In the previous example, we defined various constraints for the data in our XML documents. We defined that the day attribute must be an integer between 1 and 31, and the month attribute must be an integer between 1 and 12. We also used a regular expression to define a part-number attribute as a five-digit number, a dash, a four-digit number, a dash, and another five-digit number. None of those things are possible in a DTD.

  • Schemas are themselves XML documents. Since they are XML documents, we can write XSLT stylesheets to manipulate them. There are products in the marketplace today that take a schema and generate documentation from it. Figure 1-1 shows some sample output generated from our schema.

    Figure 1-1. Automatically generated XML Schema documentation

    Generating this output is relatively straightforward because the tool transforms an XML document. The schema defines a variety of elements and attributes and constraints on valid data, all of which can be easily converted into other formats, such as the HTML shown in Figure 1-1.

1.2.2.9. Well-formed versus valid documents

Any XML document that follows the rules described here is said to be well-formed. In addition, if an XML document references a set of rules that define how the document is structured (either a DTD or an XML Schema), and it follows all those rules, it is said to be a valid document.

All valid documents are well-formed; on the other hand, not all well-formed documents are valid.

1.2.2.10. Tags versus elements

Although many people use the two terms interchangably, a tag is different from an element. A tag is the text between the angle brackets (< and >). There are start tags, end tags, and empty tags. A tag consists of a tag name and, if it is a start tag or an empty tag, some optional attributes. (Unlike other markup languages, end tags in XML cannot contain attributes.) An element consists of the start tag for the element, the end tag for the element, and everything in between. This might include text, other elements, and comments, as well as other things such as entity references and processing instructions.

1.2.2.11. Namespaces

A final XML topic we'll mention here is namespaces. Namespaces are designed to distinguish between two tags that have the same name. For example, if I design an XML vocabulary for books and you design an XML vocabulary for paintings, it's likely that both of us will define a <title> element. My <title> element refers to the title of a book, while yours refers to the title of a painting. If someone needs to create an XML document that refers to both books and paintings, they can use a namespace to distinguish between the two <title> elements. Namespaces are defined and used as follows:

<xyz xmlns:books="http://www.myco.com/books.dtd"
     xmlns:paintings="http://www.yourco.com/paintings.xsd">

In this example, the xmlns:books attribute associates a string with the books DTD, and the xmlns:paintings attribute associates a string with the paintings schema. This means that a title element from the books DTD would be coded as <books:title>, while a title element from the paintings schema would be referred to as <paintings:title>.

I mention namespaces here primarily because all XSLT elements we use in this book are prefixed with the xsl namespace prefix. All stylesheets we write begin like this:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

This opening associates the xsl namespace prefix with the string http://www.w3.org/1999/XSL/Transform. The value of the namespace prefix doesn't matter; we could start our stylesheets like this:

<?xml version="1.0"?>
<pdq:stylesheet xmlns:pdq="http://www.w3.org/1999/XSL/Transform" version="1.0">

What matters is the string to which the namespace prefix is mapped. Also keep in mind that all XSLT stylesheets use namespace prefixes to process the XML elements they contain. By default, anything that doesn't use the xsl namespace prefix is not processed -- it's written to the result tree. We'll discuss these topics in more detail as we go through the book.

1.2.3. DOM and SAX

The two most popular APIs used to parse XML documents are the Document Object Model (DOM) and the Simple API for XML (SAX). DOM is an official recommendation of the W3C (available at http://www.w3.org/TR/REC-DOM-Level-1), while SAX is a de facto standard created by David Megginson and others on the XML-DEV mailing list (http://lists.xml.org/archives). We'll discuss these two APIs briefly here. We won't use them much in this book, but discussing them will give you some insight into how most XSLT processors work.

TIP: See http://www.megginson.com/SAX/ for the SAX standard. (Make sure the letters SAX are in uppercase.) If you'd like to learn more about the XML-DEV mailing list, send email with "subscribe xml-dev" in the body of the message to majordomo@xml.org. You can also check out http://www.lists.ic.ac.uk/hypermail/xml-dev to see the XML-DEV mailing list archives.

1.2.3.1. DOM

DOM is designed to build a tree view of your document. Remember that all XML documents must be contained in a single element; that single element becomes the root of the tree. The DOM specification defines several language-neutral interfaces, described here:

Node
This interface is the base datatype of the DOM. Element, document, text, comment, and attr all extend the Node interface.

Document
This object contains the DOM representation of the XML document. Given a Document object, you can get the root of the tree (the Document element); from the root, you can move through the tree to find all elements, attributes, text, comments, processing instructions, etc., in the XML document.

Element
This interface represents an element in an XML document.

Attr
This interface represents an attribute of an element in an XML document.

Text
This interface represents a piece of text from the XML document. Any text in your XML document becomes a Text node. This means that the text of a DOM object is a child of the object, not a property of it. The text of an Element is represented as a Text child of an Element object; the text of an Attr is also represented that way.

Comment
This interface represents a comment in the XML document. A comment begins with <!-- and ends with -->. The only restriction on its contents is that two consecutive hyphens (--) can appear only at the start or end of the comment. Other than that, a comment can include angle brackets (< >), ampersands (&), single or double quotation marks (' "), and anything else.

ProcessingInstruction
This interface represents a processing instruction in the XML document. Processing instructions look like this:

<?xml-stylesheet href="case-study.xsl" type="text/xsl"?>
<?cocoon-process type="xslt"?>

Processing instructions contain processor-specific information. The first of the two PIs (PI is XML jargon -- feel free to drop this into casual conversations to impress your friends) is the standard way to associate an XSLT stylesheet with an XML document (more on this in a minute). The second PI is used by Cocoon, an XML publishing framework from the Apache Software Foundation. (If you're not familiar with Cocoon, look at the Cocoon home page at http://xml.apache.org/cocoon.)

When you parse an XML document with a DOM parser, it:

  • Creates objects (Elements, Attr, Text, Comments) representing the contents of the document. These objects implement the interfaces defined in the DOM specification.

  • Arranges these objects in a tree. Each Element in the XML document has some properties (such as the element's name), and may also have some children.

  • Parses the entire document before control returns to your code. This means that for large documents, there is a significant delay while the document is parsed.

The most significant thing about the DOM is that it is based on a tree view of your document. An XSLT processor uses a very similar tree view (with some slight differences, such as the fact that not everything we deal with in XPath and XSLT has the same root element). Understanding how a DOM parser works makes it easier to understand how an XSLT processor views your document.

1.2.3.1.1. A sample DOM tree

DOM, XSLT, and XPath all use tree structures to represent data from an XML document. For this reason, it's important to have at least a casual knowledge of how DOM builds a tree structure. Our earlier <postalcodes> document is shown as a DOM tree in Figure 1-2.

Figure 1-2. DOM tree representation of an XML document
NOTE: The image in Figure 1-2 was produced by the DOMit servlet, an XML validation service available at http://www-106.ibm.com/developerworks/features/xmlvalidatorform.html.

If we want to find different parts of our XML document, sort the subtrees based on the first character of the text of the <postalcode> element, or select only the subtrees in which the text of the <usage-count> element has a numeric value greater than 500, we have to start at the top of the DOM tree and work our way down through the root element's descendants. When we write XSLT stylesheets, we also start at the root of the tree and work our way down.

WARNING: To be honest, the DOM tree built for our document is more complicated than our beautiful picture indicates. The whitespace characters in our document (carriage return/line feed, tabs, spaces, etc.) become Text nodes. Normally it's a good idea to remove this whitespace so the DOM tree won't be littered with these useless Text nodes, but I included them here to give you a sense of the XML document's structure.

1.2.3.2. SAX

The Simple API for XML was developed by David Megginson and others on the XML-DEV mailing list. It has several important differences from DOM:

  • The SAX API is interactive. As a SAX parser processes your document, it sends events to your code. You don't have to wait for the parser to finish the entire document as you do with the DOM; you get events from the parser immediately. These events let you know when the parser finds the start of the document, the start of an element, some text, the end of an element, a processing instruction, the end of the document, etc.

  • SAX is designed to avoid the large memory footprint of DOM. In the SAX world, you're told when the parser finds things in the XML document; it's up to you to save those things. If you don't do anything to store the data found by the parser, it goes into the bit bucket.

  • SAX doesn't provide the hierarchical view of the document that DOM does. If you need to know a lot about the structure of an XML document and the context of a given element, SAX isn't much help. Each SAX event is stateless; that is, a SAX event won't tell you, "Here's some text for the <postalcode> element I mentioned earlier." A SAX parser only tells you, "Here's some text." If you need to know about an XML document's structure, you have to keep track of that information yourself.

The best thing about SAX is that it is interactive. Most of the transformations currently done with XSLT take place on the server. As of this writing, most XSLT processors are based on DOM parsers. In the near future, however, we'll see XSLT processors based on SAX parsers. This means that the processor can start generating results almost as soon as the parse of the source document begins, resulting in better throughput and creating the perception of faster service. Because DOM, XPath, and XSLT all use trees to represent XML documents, DOM is more relevant to our discussions here. Nevertheless, it's useful to know how SAX parsers work, especially as SAX-based XSLT processors begin to rear their speedy little heads.

1.2.4. XML Standards

When we talk about writing stylesheets, we'll work with two standards: XSLT and XPath. XSLT defines a set of primitives used to describe a document transformation, while XPath defines a syntax for describing locations in XML documents. When we write stylesheets, we'll use XSLT to tell the processor what to do, and we'll use XPath to tell the processor what to do it to. Both standards are available at the W3C's web site; see http://www.w3.org/TR/xslt and http://www.w3.org/TR/xpath for more information.

There are other XML-related standards, of course. We'll discuss them here briefly, with a short mention of how (or whether) they relate to our work with XSLT and XPath.

1.2.4.1. XML 1.0

XML 1.0 is the foundation upon which everything else is built. See http://www.w3.org/TR/REC-xml.

1.2.4.2. The Extensible Stylesheet Language (XSL)

Also called the Formatting Objects specification or XSL-FO, this standard deals with rendering XML elements. Although most people think of rendering as formatting for a browser or a printed page, researchers use the specification to render XML elements as Braille or as audio files. (That being said, the main market for this technology is in producing high-quality printed output.) As of this writing, the XSL-FO specification is a Candidate Recommendation at the W3C. A couple of our examples in this book use formatting objects and the Apache XML Project's Formatting Object to PDF translator (FOP) tool; see http://xml.apache.org/fop for more information on FOP. For more information on XSL, see http://www.w3.org/TR/xsl.

1.2.4.3. XML Schemas

In our earlier examples, we had a brief example of an XML Schema. Part 1 of the specification deals with XML document structures; it contains XML elements that define what can appear in an XML document. You use these elements to specify which elements can be nested inside others, how many times each element can appear, the attributes of those elements, and other features. Part 2 of the specification defines basic datatypes used in XML Schemas and rules for deriving new datatypes from existing ones.

The two specifications are available at http://www.w3.org/TR/xmlschema-1 and http://www.w3.org/TR/xmlschema-2. For a good introduction to XML Schemas, see the XML Schema Primer, available at http://www.w3.org/TR/xmlschema-0.

1.2.4.4. The Simple API for XML (SAX)

The SAX API defines the events and interfaces used to interact with a SAX parser. SAX and DOM are the most common APIs used to work with XML documents. See http://www.megginson.com/SAX/ for the complete specification. (Note that the letters "SAX" must be in uppercase.)

1.2.4.5. Document Object Model (DOM) Level 1

The DOM, as we discussed earlier, is a programming API for documents. It defines a set of interfaces and methods used to view an XML document as a tree structure. XSLT and XPath use a similar tree view of XML documents. See http://www.w3.org/TR/REC-DOM-Level-1 for more information.

1.2.4.6. Document Object Model (DOM) Level 2

The DOM Level 2 standard builds on DOM Level 1. It adds several new features:

  • HTML support, providing a DOM view of HTML documents

  • CSS and stylesheet interfaces

  • Document events

  • Tree traversal methods

  • Range selection methods

  • Views of DOM trees, in which the view is separate from the DOM itself

The new features of DOM Level 2 don't affect our work directly. If you read the specification, you'll see that certain features, such as views, stylesheet interfaces, and tree traversal, provide features useful to an XSLT processor. Although future XSLT processors will be built on XML parsers that provide these functions, that won't change any of the techniques we cover in this book. See http://www.w3.org/TR/DOM-Level-2 for the complete specification.

1.2.4.7. Namespaces in XML

As we mentioned earlier, namespaces provide a way to avoid name collisions when two XML elements have the same name. See http://www.w3.org/TR/REC-xml-names for more information.

1.2.4.8. Associating stylesheets with XML documents

It's possible to reference an XSLT stylesheet within an XML document. This specification uses processing instructions to define one or more stylesheets that should be used to transform an XML document. You can define different stylesheets to be used for different browsers. See http://www.w3.org/TR/xml-stylesheet for complete information. Here's the start of an XML document, with two associated stylesheets:

<?xml version="1.0"?>
<?xml-stylesheet href="docbook/html/docbook.xsl" type="text/xsl"?>
<?xml-stylesheet href="docbook/wap/docbook.xsl"  type="text/xsl" media="wap"?>

In this example, the first stylesheet is the default because it doesn't have a media attribute. The second stylesheet will be used when the User-Agent field from the HTTP header contains the string wap, identifying the requester of a document as a WAP browser. The advantage of this technique is that you can define several different stylesheets within a particular document and have each stylesheet generate useful results for different browser or client types. The disadvantage of this technique is that we're effectively putting rendering instructions into our XML document, something we prefer to avoid.

If you use Microsoft Internet Explorer Version 5.0 or higher, you can install the Microsoft's XSLT processor so that opening an XML document in your browser will cause it to be transformed and rendered automatically. For more details on how to install and configure the XML tools to work with the brower, see http://www.microsoft.com/xml. In the previous example, if we opened an XML document that began this way, the browser would transform the XML document according to the rules defined in docbook/html/docbook.xsl and render the results as if it were any HTML page.

1.2.4.9. Scalable Vector Graphics (SVG)

The SVG specification defines an XML vocabulary for vector graphics. Described by some as "PostScript with angle brackets," it allows you to define images that can be scaled to any size or resolution. See http://www.w3.org/TR/SVG/ for details.

1.2.4.10. Canonical XML Version 1.0

Sometimes comparing two XML documents is necessary (when digitally signing an XML document, for example). The Canonical XML specification defines a canonical form of XML that makes comparing two documents easy. See http://www.w3.org/TR/xml-c14n for the complete specification.

1.2.4.11. XML digital signatures

A joint effort of the W3C and the Internet Engineering Task Force (IETF), XML digital signatures provide a mechanism for storing digital signatures in an XML document. The XML document then provides an envelope used to store, send, and retrieve digital signatures for any kind of digital resource. The latest draft of the specification can be found at http://www.w3.org/TR/xmldsig-core.

1.2.4.12. XML Pointer Language (XPointer) Version 1.0

XPointer provides a way to identify a fragment of a web resource. It uses XPath to identify fragments. For details, see http://www.w3.org/TR/xptr.

1.2.4.13. XML Linking Language (XLink) Version 1.0

XLink defines an XML vocabulary for linking to other web resources within an XML document. It supports the unidirectional links we're all familiar with in HTML, as well as more sophisticated links. See http://www.w3.org/TR/xlink/.

Previous | Home | Next
1. Getting Started | Book Index | 1.3. Installing Xalan
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Installing Xalan (XSLT)

 XSLSearch this book

1.3. Installing Xalan

In this section, I'll show you how to install the Xalan XSLT processor. In the next chapter, we'll create our first stylesheet and use it to transform an XML document.

The installation process is pretty simple, assuming you already have a Java Runtime Environment (JRE) installed on your machine. Although very little of the code we look at in this book uses Java, the Xalan XSLT processor itself is written in Java. Once you've installed the JRE, go to http://xml.apache.org/xalan-j/ and download the latest stable build of the code. (If you're feeling brave, feel free to download last night's build instead.)

Once the Xalan .zip or .gzip file is downloaded, unpack it and add three files to your CLASSPATH. The three files include the .jar file for the Xerces parser, the .jar file for the Xalan stylesheet engine itself, and the .jar file for the Bean Scripting Framework. As of this writing, the .jar files are named xerces.jar, xalan.jar, and bsf.jar.

To make sure Xalan is installed correctly, go to a command prompt and type the following command:

java org.apache.xalan.xslt.Process

This is a Java class, so everything is case sensitive. You should see an error message like this:

java org.apache.xalan.xslt.Process
=xslproc options:
    -IN inputXMLURL
   [-XSL XSLTransformationURL]
   [-OUT outputURL]
   [-LXCIN compiledStylesheetFileNameIn]
   [-LXCOUT compiledStylesheetFileNameOutOut]

If you got this error message, you're all set! You're ready for the next chapter, in which we'll build our very first XSLT stylesheet.

Previous | Home | Next
1.2. XML Basics | Book Index | 1.4. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

1.4. Summary

In this chapter, we've gone over the basics of XML and talked about DOM and SAX, two standards that are commonly used by XSLT processors. We also talked about other technology standards and how to install the Xalan stylesheet processor. At this point, you've got everything you need to build and use your first stylesheets, something we'll do in the next chapter.

Previous | Home | Next
1.3. Installing Xalan | Book Index | 2. The Obligatory Hello World Example
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The Obligatory Hello World Example (XSLT)

 XSLTSearch this book

Chapter 2. The Obligatory Hello World Example

Contents:

Goals of This Chapter
Transforming Hello World
How a Stylesheet Is Processed
Stylesheet Structure
Sample Gallery
Summary

In future chapters, we'll spend much time talking about XSLT, XPath, and various advanced functions used to transform XML documents. First, though, we'll go through a short example to illustrate how stylesheets work.

2.1. Goals of This Chapter

By the end of this chapter, you should know:

  • How to create a basic stylesheet

  • How to use a stylesheet to transform an XML document

  • How a stylesheet processor uses a stylesheet to transform an XML document

  • The structure of an XSLT stylesheet

Previous | Home | Next
1.4. Summary | Book Index | 2.2. Transforming Hello World
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Transforming Hello World (XSLT)

 XSLSearch this book

2.2. Transforming Hello World

Continuing the tradition of Hello World examples begun by Brian Kernighan and Dennis Ritchie in The C Programming Language (Prentice Hall, 1988), we'll transform a Hello World XML document.

2.2.1. Our Sample Document

First, we'll look at our sample document. This simple XML document, courtesy of the XML 1.0 specification, contains the famous friendly greeting to the world:

<?xml version="1.0"?>
<greeting>
  Hello, World!
</greeting>

What we'd like to do is transform this fascinating document into something we can view in an ordinary household browser.

2.2.2. A Sample Stylesheet

Here's an XSLT stylesheet that defines how to transform the XML document:

<xsl:stylesheet
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     version="1.0">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:apply-templates select="greeting"/>
  </xsl:template>

  <xsl:template match="greeting">
    <html>
      <body>
        <h1>
          <xsl:value-of select="."/>
        </h1>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

We'll talk about these elements and what they do in just a minute. Keep in mind that the stylesheet is itself an XML document, so we have to follow all of the document rules we discussed in the previous chapter.

2.2.3. Transforming the XML Document

To transform the XML document using the XSLT stylesheet, run this command:

java org.apache.xalan.xslt.Process -in greeting.xml -xsl greeting.xsl
  -out greeting.html

This command transforms the document greeting.xml, using the templates found in the stylesheet greeting.xsl. The results of the transformation are written to the file greeting.html. Check the output file in your favorite browser to make sure the transform worked correctly.

2.2.4. Stylesheet Results

The XSLT processor generates these results:

<html>
<body>
<h1>
  Hello, World!
</h1>
</body>
</html>

When rendered in a browser, our output document looks like Figure 2-1.

Figure 2-1. HTML version of our Hello World file

Congratulations! You've now used XSLT to transform an XML document.

Previous | Home | Next
2. The Obligatory Hello World Example | Book Index | 2.3. How a Stylesheet Is Processed
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How a Stylesheet Is Processed (XSLT)

 XSLSearch this book

2.3. How a Stylesheet Is Processed

Now that we're giddy with the excitement of having transformed an XML document, let's discuss the stylesheet and how it works. A big part of the XSLT learning curve is figuring out how stylesheets are processed. To make this clear, we'll go through the steps taken by the stylesheet processor to create the HTML document we want.

2.3.1. Parsing the Stylesheet

Before the XSLT processor can process your stylesheet, it has to read it. Conceptually, it doesn't matter how the XSLT processor stores the information from your stylesheet. For our purposes, we'll just assume that the XSLT processor can magically find anything it needs in our stylesheet. (If you really must know, Xalan uses an optimized table structure to represent the stylesheet; other processors may use that approach or something else.)

Our stylesheet contains three items: an <xsl:output> element that specifies HTML as the output format and two <xsl:template> elements that specify how parts of our XML document should be transformed.

2.3.2. Parsing the Transformee

Now that the XSLT processor has processed the stylesheet, it needs to read the document it's supposed to transform. The XSLT processor builds a tree view from the XML source. This tree view is what we'll keep in mind when we build our stylesheets.

2.3.3. Lather, Rinse, Repeat

Finally, we're ready to begin the actual work of transforming the XML document. The XSLT processor may set some properties based on your stylesheet (in the previous example, it would set its output method to HTML), then it begins processing as follows:

  • Do I have any nodes to process? The nodes to process are represented by something called the context. Initially the context is the root of the XML document, but it changes throughout the stylesheet. We'll talk about the context extensively in the next chapter. (Note: all XSLT processors enjoy being anthropomorphized, so I'll often refer to them this way.)

While any nodes are in the context, do the following:

  • Get the next node from the context. Do I have any <xsl:template>s that match it? (In our example, the next node is the root node, represented in XPath syntax by /.) There is a template that matches this node -- it's the one that begins <xsl:template match="/">.

  • If one or more <xsl:template>s match, pick the right one and process it. (The right one is the most specific template. For example, <xsl:template match="/html/body/h1/p"> is more specific than <xsl:template match="p">. See the discussion of the <xsl:template> element for more information.) If no <xsl:template>s match, the XSLT processor uses some built-in rules. See Section 2.4.5, "Built-in Template Rules" later in this chapter for more information.

Notice that this is a recursive processing model. We process the current node by finding the right xsl:template for it. That xsl:template may in turn invoke other xsl:templates, which invoke xsl:templates as well. This model takes some getting used to, but it is actually quite elegant once you're accustomed to it.

NOTE: If it helps, you can think of the root template (<xsl:template match="/">) as the main method in a C, C++, or Java program. No matter how much code you've written, everything starts in main. Similarly, no matter how many <xsl:template>s you've defined in your stylesheet, everything starts in <xsl:template match="/">.

2.3.4. Walking Through Our Example

Let's revisit our example and see how the XSLT processor transforms our document:

  1. The XSLT stylesheet is parsed and converted into a tree structure.

  2. The XML document is also parsed and converted into a tree structure. (Don't worry too much about what that tree looks like or how it works; for now, just assume that the XSLT processor knows everything that's in the XML document and the XSLT stylesheet. After the first two steps are done, when we describe various things using XSLT and XPath, the processor knows what we're talking about.)

  3. The XSLT processor is now at the root of the XML document. This is the original context.

  4. There is an xsl:template that matches the document root:

    <xsl:template match="/">
  <xsl:apply-templates select="greeting"/>
</xsl:template>

    A single forward slash (/) is an XPath expression that means "the root of the document."

  5. Now the process begins again inside the xsl:template. Our only instruction here is to apply whatever xsl:templates might apply to any greeting elements in the current context. The current context inside this template is defined by the match attribute of the xsl:template element. This means the XSLT processor is looking for any greeting elements at the document root.

    Because one greeting element is at the document root, the XSLT processor must deal with it. (If more than one element matches in the current context, the XSLT processor deals with each one in the order in which they appear in the document; this is known as document order.) Looking at the greeting element, the xsl:template that applies to it is the second xsl:template in our stylesheet:

    <xsl:template match="greeting">
  <html>
    <body>
      <h1>
        <xsl:value-of select="."/>
      </h1>
    </body>
  </html>
</xsl:template>

  6. Now we're in the xsl:template for the greeting element. The first three elements in this xsl:template (<html>, <body>, and <h1>) are HTML elements. Because they're not defined with a namespace declaration, the XSLT processor passes those HTML elements through to the output stream unaltered.

    The middle of our xsl:template is an xsl:value-of element. This element writes the value of something to the output stream. In this case, we're using the XPath expression . (a single period) to indicate the current node. The XSLT processor looks at the current node (the greeting element we're currently processing) and outputs its text.

    Because our stylesheet is an XML document (we're really harping on that, aren't we?), we have to end the <h1>, <body>, and <html> elements here. At this point, we're done with this template, so control returns to the template that invoked us.

  7. Now we're back in the template for the root element. We've processed all the <greeting> elements, so we're finished with this template.

  8. No more elements are in the current context (there is only one root element), so the XSLT processor is done.

Previous | Home | Next
2.2. Transforming Hello World | Book Index | 2.4. Stylesheet Structure
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Stylesheet Structure (XSLT)

 XSLSearch this book

2.4. Stylesheet Structure

As the final part of our introduction to XSLT, we'll look at the contents of the stylesheet itself. We'll explain all the things in our stylesheet and discuss other approaches we could have taken.

2.4.1. The <xsl:stylesheet> Element

The <xsl:stylesheet> element is typically the root element of an XSLT stylesheet.

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

First of all, the <xsl:stylesheet> element defines the version of XSLT we're using, along with a definition of the xsl namespace. To be compliant with the XSLT specification, your stylesheet should always begin with this element, coded exactly as shown here. Some stylesheet processors, notably Xalan, issue a warning message if your <xsl:stylesheet> element doesn't have these two attributes with these two values. For all examples in this book, we'll start the stylesheet with this exact element, defining other namespaces as needed.

2.4.2. The <xsl:output> Element

Next, we specify the output method. The XSLT specification defines three output methods: xml, html, and text. We're creating an HTML document, so HTML is the output method we want to use. In addition to these three methods, an XSLT processor is free to define its own output methods, so check your XSLT processor's documentation to see if you have any other options.

<xsl:output method="html"/>

A variety of attributes are used with the different output methods. For example, if you're using method="xml", you can use doctype-public and doctype-system to define the public and system identifiers to be used in the the document type declaration. If you're using method="xml" or method="html", you can use the indent attribute to control whether or not the output document is indented. The discussion of the <xsl:output> element in Appendix A, "XSLT Reference" has all the details.

2.4.3. Our First <xsl:template>

Our first template matches "/", the XPath expression for the document's root element.

<xsl:template match="/">
  <xsl:apply-templates select="greeting"/>
</xsl:template>

2.4.4. The <xsl:template> for <greeting> Elements

The second <xsl:template> element processes any <greeting> elements in our XML source document.

<xsl:template match="greeting">
  <html>
    <body>
      <h1>
        <xsl:value-of select="."/>
      </h1>
    </body>
  </html>
</xsl:template>

2.4.5. Built-in Template Rules

Although most stylesheets we'll develop in this book explicitly define how various XML elements should be transformed, XSLT does define several built-in template rules that apply in the absence of any specific rules. These rules have a lower priority than any other templates, so they're always overridden when you define your own templates. The built-in templates are listed here.

2.4.5.1. Built-in template rule for element and root nodes

This template processes the root node and any of its children. This processing ensures that recursive processing will continue, even if no template is declared for a given element.

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

This means that if the structure of a document looks like this:

<?xml version="1.0"?>
<x>
  <y>
    <z/>
  </y>
</z>

The built-in template rule for element and root nodes means that we could write a template with match="z" and the <z> element will still be processed, even if there are no template rules for the <x> and <y> elements.

2.4.5.2. Built-in template rule for modes

This template ensures that element and root nodes are processed, regardless of any mode that might be in effect. (See Section 4.3.2, "Templates à la Mode" in Chapter 4, "Branching and Control Elements" for more information on the mode attribute.)

<xsl:template match="*|/" mode="x">
  <xsl:apply-templates mode="x"/>
</xsl:template>

2.4.5.3. Built-in template rule for text and attribute nodes

This template copies the text of all text and attribute nodes to the output tree. Be aware that you have to actually select the text and attribute nodes for this rule to be invoked.

<xsl:template match="text()|@*">
  <xsl:value-of select="."/>
</xsl:template>

2.4.5.4. Built-in template rule for comment and processing instruction nodes

This template does nothing.

<xsl:template match="comment()|processing-instruction()"/>

2.4.5.5. Built-in template rule for namespace nodes

This template also does nothing.

<xsl:template match="namespace()"/>

2.4.6. Top-Level Elements

To this point, we haven't actually talked about our source document or how we're going to transform it. We're simply setting up some properties for the transform. There are other elements we can put at the start of our stylesheet. Any element whose parent is the <xsl:stylesheet> element is called a top-level element. Here is a brief discussion of the other top-level elements:

<xsl:include> and <xsl:import>
These elements refer to another stylesheet. The other stylesheet and all of its contents are included in the current stylesheet. The main difference between <xsl:import> and <xsl:include> is that a template, variable, or anything else imported with <xsl:import> has a lower priority than the things in the current stylesheet. This gives you a mechanism to subclass stylesheets, if you want to think about this from an object-oriented point of view. You can import another stylesheet that contains common templates, but any templates in the importing stylesheet will be used instead of any templates in the imported stylesheet. Another difference is that <xsl:import> can only appear at the beginning of a stylesheet, while <xsl:include> can appear anywhere.

<xsl:strip-space> and <xsl:preserve-space>

These elements contain a space-separated list of elements from which whitespace should be removed or preserved in the output. To define these elements globally, use <xsl:strip-space elements="*"/> or <xsl:preserve-space elements="*"/>. If we want to specify that whitespace be removed for all elements except for <greeting> elements and <salutation> elements, we would add this markup to our stylesheet:

<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="greeting
            salutation"/>
<xsl:key>
This element defines a key, which is similar to defining an index on a database. We'll talk more about the <xsl:key> element and the key() function in Section 5.2, "Generating Links with the key() Function" in Chapter 5, "Creating Links and Cross-References".

<xsl:variable>
This element defines a variable. Any <xsl:variable> that appears as a top-level element is global to the entire stylesheet. Variables are discussed extensively in Section 4.5, "Variables" in Chapter 4, "Branching and Control Elements".

<xsl:param>
This element defines a parameter. As with <xsl:variable>, any <xsl:param> that is a top-level element is global to the entire stylesheet. Parameters are discussed extensively in Section 4.4, "Parameters" in Chapter 4, "Branching and Control Elements".

Other stuff
More obscure elements that can appear as top-level elements are <xsl:decimal-format>, <xsl:namespace-alias>, and <xsl:attribute-set>. All are discussed in Appendix A, "XSLT Reference".

2.4.7. Other Approaches

One mantra of the Perl community is, "There's more than one way to do it." That's true with XSLT stylesheets, as well. We could have written our stylesheet like this:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:apply-templates select="greeting"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="greeting">
    <h1>
      <xsl:value-of select="."/>
    </h1>
  </xsl:template>
</xsl:stylesheet>

In this version, we put the wrapper elements for the HTML document in the template for the root element. One of the things you should think about as you build your stylesheets is where to put elements like <html> and <body>. Let's say our XML document looked like this instead:

<?xml version="1.0"?>
<greetings>
  <greeting>Hello, World!</greeting>
  <greeting>Hey, Y'all!</greeting>
</greetings>

In this case, we would have to put the <html> and <body> elements in the <xsl:template> for the root element. If they were in the <xsl:template> for the <greeting> element, the output document would have multiple <html> elements, something that isn't valid in an HTML document. Our updated stylesheet would look like this:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:apply-templates select="greetings/greeting"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="greeting">
    <h1>
      <xsl:value-of select="."/>
    </h1>
  </xsl:template>
</xsl:stylesheet>

Notice that we had to modify our XPath expression; what was originally greeting is now greetings/greeting. As we develop stylesheets, we'll have to make sure our XPath expressions match the document structure. When you get unexpected results, or no results, an incorrect XPath expression is usually the cause.

As a final example, we could also write our stylesheet with only one xsl:template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <body>
        <h1>
          <xsl:value-of select="greeting"/>
        </h1>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Although this is the shortest of our sample stylesheets, our examples will tend to feature a number of short templates, each of which defines a simple transform for a few elements. This approach makes your stylesheets much easier to understand, maintain, and reuse. The more transformations you cram into each xsl:template, the more difficult it is to debug your stylesheets, and the more difficult it is to reuse the templates elsewhere.

Previous | Home | Next
2.3. How a Stylesheet Is Processed | Book Index | 2.5. Sample Gallery
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Sample Gallery (XSLT)

 XSLSearch this book

2.5. Sample Gallery

Before we get into more advanced topics, we'll transform our Hello World document in other ways. We'll look through simple stylesheets that convert our small XML document into the following things:

  • A Scalable Vector Graphics (SVG) File

  • A PDF file

  • A Java program

  • A Virtual Reality Modeling Language (VRML) file

2.5.1. The Hello World SVG File

Our first example will convert our Hello World document into an SVG file:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml"
    doctype-public="-//W3C//DTD SVG 20001102//EN"
    doctype-system=
      "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd"/>

  <xsl:template match="/">
    <svg width="8cm" height="4cm">
      <g>
        <defs>
          <radialGradient id="MyGradient"
            cx="4cm" cy="2cm" r="3cm" fx="4cm" fy="2cm">
            <stop offset="0%" style="stop-color:red"/>
            <stop offset="50%" style="stop-color:blue"/>
            <stop offset="100%" style="stop-color:red"/>
          </radialGradient>
        </defs>
        <rect style="fill:url(#MyGradient); stroke:black"
          x="1cm" y="1cm" width="6cm" height="2cm"/>
        <text x="4cm" y="2.2cm" text-anchor="middle"
          style="font-family:Verdana; font-size:24;
          font-weight:bold; fill:black">
          <xsl:apply-templates select="greeting"/>
        </text>
      </g>
    </svg>
  </xsl:template>

  <xsl:template match="greeting">
    <xsl:value-of select="."/>
  </xsl:template>

</xsl:stylesheet>

As you can see from this stylesheet, most of the code here simply sets up the structure of the SVG document. This is typical of many stylesheets; once you learn what the output format should be, you merely extract content from the XML source document and insert it into the output document at the correct spot. When we transform the Hello World document with this stylesheet, here are the results:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN"
        "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
<svg height="4cm" width="8cm">
  <g>
    <defs>
      <radialGradient fy="2cm" fx="4cm" r="3cm" cy="2cm"
        cx="4cm" id="MyGradient">
        <stop style="stop-color:red" offset="0%"/>
        <stop style="stop-color:blue" offset="50%"/>
        <stop style="stop-color:red" offset="100%"/>
      </radialGradient>
    </defs>
    <rect height="2cm" width="6cm" y="1cm" x="1cm"
        style="fill:url(#MyGradient); stroke:black"/>
    <text style="font-family:Verdana; font-size:24;
        font-weight:bold; fill:black"
        text-anchor="middle" y="2.2cm" x="4cm">
  Hello, World!
    </text>
  </g>
</svg>

When rendered in an SVG viewer, our Hello World document looks like Figure 2-2.

Figure 2-2. SVG version of our Hello World file

This screen capture was made using the Adobe SVG plug-in inside the Internet Explorer browser. You can find the plug-in at http://www.adobe.com/svg/.

We used Xalan to generate the SVG file:

java org.apache.xalan.xslt.Process -in greeting.xml -xsl svg-greeting.xsl
  -out greeting.svg

(This command should all be on a single line.)

2.5.2. The Hello World PDF File

To convert the Hello World file into a PDF file, we'll first convert our XML file into formatting objects. The Extensible Stylesheet Language for Formatting Objects (XSL-FO) is an XML vocabulary that describes how content should be rendered. Here is our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <xsl:output method="xml"/>

  <xsl:template match="/">
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
      <fo:layout-master-set>
        <fo:simple-page-master margin-right="75pt" margin-left="75pt"
          page-height="11in" page-width="8.5in"
          margin-bottom="25pt" margin-top="25pt" master-name="main">
          <fo:region-before extent="25pt"/>
          <fo:region-body margin-top="50pt" margin-bottom="50pt"/>
          <fo:region-after extent="25pt"/>
        </fo:simple-page-master>
        <fo:page-sequence-master master-name="standard">
          <fo:repeatable-page-master-alternatives>
            <fo:conditional-page-master-reference
              master-name="main" odd-or-even="any"/>
          </fo:repeatable-page-master-alternatives>
        </fo:page-sequence-master>
      </fo:layout-master-set>

      <fo:page-sequence master-name="standard">
        <fo:flow flow-name="xsl-region-body">
          <xsl:apply-templates select="greeting"/>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>

  <xsl:template match="greeting">
    <fo:block line-height="40pt" font-size="36pt" text-align="center">
      <xsl:value-of select="."/>
    </fo:block>
  </xsl:template>

</xsl:stylesheet>

This stylesheet converts our Hello World document into the following XML file:

<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <fo:layout-master-set>
    <fo:simple-page-master master-name="main" margin-top="25pt"
        margin-bottom="25pt" page-width="8.5in" page-height="11in"
        margin-left="75pt" margin-right="75pt">
      <fo:region-before extent="25pt"/>
      <fo:region-body margin-bottom="50pt" margin-top="50pt"/>
      <fo:region-after extent="25pt"/>
    </fo:simple-page-master>
    <fo:page-sequence-master master-name="standard">
      <fo:repeatable-page-master-alternatives>
        <fo:conditional-page-master-reference odd-or-even="any"
        master-name="main"/>
      </fo:repeatable-page-master-alternatives>
    </fo:page-sequence-master>
  </fo:layout-master-set>
  <fo:page-sequence master-name="standard">
    <fo:flow flow-name="xsl-region-body">
      <fo:block text-align="center" font-size="36pt" line-height="40pt">
  Hello, World!
      </fo:block>
    </fo:flow>
  </fo:page-sequence>
</fo:root>

We generated this file of formatting objects with this command:

java org.apache.xalan.xslt.Process -in greeting.xml -xsl fo-greeting.xsl
  -out greeting.fo

This lengthy set of tags uses formatting objects to describe the size of the page, the margins, font sizes, line heights, etc., along with the text extracted from our XML source document. Now that we have the formatting objects, we can use the Apache XML Project's FOP tool. After converting the formatting objects to PDF, the PDF file looks like Figure 2-3.

Figure 2-3. PDF version of our Hello World file

Here's the command used to convert our file of formatting objects into a PDF file:

java org.apache.fop.apps.CommandLine greeting.fo greeting.pdf

2.5.3. The Hello World Java Program

Our last two transformations don't involve XML vocabularies at all; they use XSLT to convert the Hello World document into other formats. Next, we'll transform our XML source document into the source code for a Java program. When the program is compiled and executed, it prints the message from the XML document to the console. Here's our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:text>
public class Greeting
{
  public static void main(String[] argv)
  {
    </xsl:text>
    <xsl:apply-templates select="greeting"/>
    <xsl:text>
  }
}
    </xsl:text>
  </xsl:template>

  <xsl:template match="greeting">
    <xsl:text>System.out.println("</xsl:text>
    <xsl:value-of select="normalize-space()"/>
    <xsl:text>");</xsl:text>
  </xsl:template>

</xsl:stylesheet>

(Notice that we used <xsl:output method="text"> to generate text, not markup.) Our stylesheet produces these results:

public class Greeting
{
  public static void main(String[] argv)
  {
    System.out.println("Hello, World!");
  }
}

We generated this Java code with the following command:

java org.apache.xalan.xslt.Process -in greeting.xml -xsl java-greeting.xsl
  -out Greeting.java

(Notice that the name of the generated file must start with an uppercase letter; Java requires that the name of the file must match the name of the class it contains. Also, this command should be entered on a single line.) When executed, our generated Java program looks like this:

C:\> java Greeting
Hello, World!

Although generating Java code from an XML document may seem strange, it is actually a common technique. The FOP tool from the Apache XML Project does this; it defines a number of properties in XML, then generates the Java source code to create class definitions and get and set methods for each of those properties.

2.5.4. The Hello World VRML File

For our final transformation, we'll create a VRML file from our XML source document. Here's the stylesheet that does the trick:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:text>#VRML V2.0 utf8

Shape
{
  geometry ElevationGrid
  {
    xDimension 9
    zDimension 9
    xSpacing 1
    zSpacing 1
    height
    [
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
      0 0 0 0 0 0 0 0 0
    ]
    colorPerVertex FALSE
    color Color
    {
      color
      [
        0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1,
        1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0,
        0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1,
        1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0,
        0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1,
        1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0,
        0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1,
        1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0, 1 1 1, 0 0 0,
      ]
    }
  }
}

Transform
{
  translation 4.5 1 4
  children
  [
    Shape
    {
      geometry Text
      {
    </xsl:text>
    <xsl:apply-templates select="greeting"/>
    <xsl:text>
        fontStyle FontStyle

        {
          justify "MIDDLE"
          style "BOLD"
        }
      }
    }
  ]
}

NavigationInfo
{
  type ["EXAMINE","ANY"]
}

Viewpoint
{
  position 4 1 10
}
    </xsl:text>
  </xsl:template>

  <xsl:template match="greeting">
    <xsl:text>string"</xsl:text>
    <xsl:value-of select="normalize-space()"/>
    <xsl:text>"</xsl:text>
  </xsl:template>

</xsl:stylesheet>

We generate our VRML document with the following command:

java org.apache.xalan.xslt.Process -in greeting.xml -xsl vrml-greeting.xsl
  -out greeting.wrl

As with our earlier stylesheet, our VRML-generating template is mostly boilerplate, with content from the XML source document added at the appropriate point. The <xsl:apply-templates> element is replaced with the value of the <greeting> element. The VRML code here draws a checkerboard, then draws the text from the XML document above it, floating in midair in the center of the document. A couple of views of the VRML version of our XML document are shown in Figure 2-4.

Figure 2-4. Two views of the VRML version of our Hello World document

Although we haven't discussed any of the specific vocabularies or file formats we've used here, hopefully you understand that you can transform your XML documents into any useful format you can think of. Through the rest of the book, we'll cover several common tasks you can solve with XSLT, all of which build on the basics we've discussed here.

Previous | Home | Next
2.4. Stylesheet Structure | Book Index | 2.6. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

2.6. Summary

Although our stylesheets here are trivial, they are much simpler than the corresponding procedural code (written in Visual Basic, C++, Java, etc.) to transform any <greeting> elements similarly. We've gone over the basics of what stylesheets are and how they work.

As we go through this book, we'll demonstrate the incredible range of things you can do in XSLT stylesheets, including:

  • Using logic, branching, and control statements

  • Sorting and grouping elements

  • Linking and cross-referencing elements

  • Creating master documents that embed other XML documents, then sort, filter, group, and format the combined documents.

  • Adding new functions to the XSLT stylesheet processor with XSLT's extension mechanism

XSLT has an extremely active user community. To see just how active, visit the XSL-List site at http://www.mulberrytech.com/xsl/xsl-list/index.html.

Before we dive in to those topics, we need to talk about XPath, the syntax that describes what parts of an XML document we want to transform into all of these different things.

Previous | Home | Next
2.5. Sample Gallery | Book Index | 3. XPath: A Syntax for Describing Needles and Haystacks
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath: A Syntax for Describing Needles and Haystacks (XSLT)

 XSLTSearch this book

Chapter 3. XPath: A Syntax for Describing Needles and Haystacks

Contents:

The XPath Data Model
Location Paths
Attribute Value Templates
XPath Datatypes
The XPath View of an XML Document
Summary

XPath is a syntax used to describe parts of an XML document. With XPath, you can refer to the first <para> element, the quantity attribute of the <part-number> element, all <first-name> elements that contain the text "Joe", and many other variations. An XSLT stylesheet uses XPath expressions in the match and select attributes of various elements to indicate how a document should be transformed. In this chapter, we'll discuss XPath in all its glory.

XPath is designed to be used inside an attribute in an XML document. The syntax is a mix of basic programming language expressions (such as $x*6) and Unix-like path expressions (such as /sonnet/author/last-name). In addition to the basic syntax, XPath provides a set of useful functions that allow you to find out various things about the document.

One important point, though: XPath works with the parsed version of your XML document. That means that some details of the original document aren't accessible to you from XPath. For example, entity references are resolved beforehand by the XSLT processor before instructions in our stylesheet are evaluated. CDATA sections are converted to text, as well. That means we have no way of knowing if a text node in an XPath tree was in the original XML document as text, as an entity reference, or as part of a CDATA section. As you get used to thinking about your XML documents in terms of XPath expressions, this situation won't be a problem, but it may confuse you at first.

3.1. The XPath Data Model

XPath views an XML document as a tree of nodes. This tree is very similar to a Document Object Model (DOM) tree, so if you're familiar with the DOM, you should have some understanding of how to build basic XPath expressions. (To be precise, this is a conceptual tree; an XSLT processor or anything else that implements the XPath standard doesn't have to build an actual tree.) There are seven kinds of nodes in XPath:

  • The root node (one per document)

  • Element nodes

  • Attribute nodes

  • Text nodes

  • Comment nodes

  • Processing instruction nodes

  • Namespace nodes

We'll talk about all the different node types in terms of the following document:

<?xml version="1.0"?>
<?xml-stylesheet href="sonnet.xsl" type="text/xsl"?>
<?cocoon-process type="xslt"?>

<!DOCTYPE sonnet [
  <!ELEMENT sonnet (auth:author, title, lines)>
  <!ATTLIST sonnet public-domain CDATA "yes"
            type (Shakespearean | Petrarchan) "Shakespearean">
<!ELEMENT auth:author  (last-name,first-name,nationality,
                        year-of-birth?,year-of-death?)>
<!ELEMENT last-name (#PCDATA)>
<!ELEMENT first-name (#PCDATA)>
<!ELEMENT nationality (#PCDATA)>
<!ELEMENT year-of-birth (#PCDATA)>
<!ELEMENT year-of-death (#PCDATA)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT lines (line,line,line,line,
                 line,line,line,line,
                 line,line,line,line,
                 line,line)>
<!ELEMENT line (#PCDATA)>
]>

<!-- Default sonnet type is Shakespearean, the other allowable  -->
<!-- type is "Petrarchan."                                      -->
<sonnet type="Shakespearean">
  <auth:author xmlns:auth="http://www.authors.com/">
    <last-name>Shakespeare</last-name>
    <first-name>William</first-name>
    <nationality>British</nationality>
    <year-of-birth>1564</year-of-birth>
    <year-of-death>1616</year-of-death>
  </auth:author>
  <!-- Is there an official title for this sonnet?  They're
       sometimes named after the first line.                   -->
  <title>Sonnet 130</title>
  <lines>
    <line>My mistress' eyes are nothing like the sun,</line>
    <line>Coral is far more red than her lips red.</line>
    <line>If snow be white, why then her breasts are dun,</line>
    <line>If hairs be wires, black wires grow on her head.</line>
    <line>I have seen roses damasked, red and white,</line>
    <line>But no such roses see I in her cheeks.</line>
    <line>And in some perfumes is there more delight</line>
    <line>Than in the breath that from my mistress reeks.</line>
    <line>I love to hear her speak, yet well I know</line>
    <line>That music hath a far more pleasing sound.</line>
    <line>I grant I never saw a goddess go,</line>
    <line>My mistress when she walks, treads on the ground.</line>
    <line>And yet, by Heaven, I think my love as rare</line>
    <line>As any she belied with false compare.</line>
  </lines>
</sonnet>
<!-- The title of Sting's 1987 album "Nothing like the sun" is  -->
<!-- from line 1 of this sonnet.                                -->

3.1.1. The Root Node

The root node is the XPath node that contains the entire document. In our example, the root node contains the <sonnet> element; it's not the <sonnet> element itself. In an XPath expression, the root node is specified with a single slash (/).

Unlike other nodes, the root node has no parent. It always has at least one child, the document element. The root node also contains comments or processing instructions that are outside the document element. In our sample document, the two processing instructions named xml-stylesheet and cocoon-process are both children of the root node, as are the comment that appears before the <sonnet> tag and the comment that appears after the </sonnet> tag. The string value of the root node (returned by <xsl:value-of select="/" />, for example), is the concatenation of all text nodes of the root node's descendants.

3.1.2. Element Nodes

Every element in the original XML document is represented by an XPath element node. In the previous document, an element node exists for the <sonnet> element, the <auth:author> element, the <last-name> element, etc. An element node's children include text nodes, element nodes, comment nodes, and processing instruction nodes that occur within that element in the original document.

An element node's string value (returned by <xsl:value-of select="sonnet">, for example) is the concatenation of the text of this node and all of its children, in document order (the order in which they appear in the original document). All entity references (such as &lt;) and character references (such as &#052;) in the text are resolved automatically; you can't access the entity or character references from XPath.

The name of an element node (returned by the XPath name() function) is the element name and any namespace in effect. In the previous example, the name() of the <sonnet> element is sonnet. The name() of the <auth:author> element is auth:author, and the name() of the <last-name> element is auth:last-name (any element contained in the <author> element is from the auth namespace unless specifically declared otherwise). Other XPath functions, such as local-name() and namespace-uri(), return other information about the name of the element node.

3.1.3. Attribute Nodes

At a minimum, an element node is the parent of one attribute node for each attribute in the XML source document. In our sample document, the element node corresponding to the <sonnet> element is the parent of an attribute node with a name of type and a value of Shakespearean. A couple of complications for attribute nodes exist, however:

  • Although an element node is the parent of its attribute nodes, those attribute nodes are not children of their parent. The children of an element are the text, element, comment, and processing instruction nodes contained in the original element. If you want a document's attributes, you must ask for them specifically. That relationship seems odd at first, but you'll find that treating an element's attributes separately is usually what you want to do.

  • If a DTD or schema defines default values for certain attributes, those attributes don't have to appear in the XML document. For example, we could have declared that a Shakespearean sonnet is the default type, so that the tag <sonnet type="Shakespearean"> is functionally equivalent to <sonnet>. Under normal circumstances, XPath creates an attribute node for all attributes with default values, whether they actually appear in the document or not. If the type is defined as #IMPLIED, both of the <sonnet> elements we just mentioned will have an attribute node with a name of type and a value of Shakespearean. Of course, if the document codes a value other than the default (<sonnet type="Petrarchan">, for example), the attribute node's value will be whatever was coded in the document.

    To make this situation even worse, an XML parser isn't required to read an external DTD. If it doesn't, then any attribute nodes that represent default values not coded in the document won't exist. Fortunately, XSLT has some branching elements (<xsl:if> and <xsl:choose>) that can help you deal with these ambiguities; we'll discuss those in Chapter 4, "Branching and Control Elements".

  • The XML 1.0 specification defines two attributes (xml:lang and xml:space) that work like default namespaces. In other words, if the <auth:author> element in our sample document contains the attribute xml:lang="en_us", that attribute applies to all elements contained inside <auth:author>. Even though that attribute might apply to the <last-name> element, <last-name> won't have an attribute node named xml:lang. Similarly, the xml:space defines whether whitespace in an element should be preserved; valid values for this attribute are preserve and default. Whether these attributes are in effect for a given element or not, the only attribute nodes an element node contains are those tagged in the document and those defined with a default value in the DTD.

    For more information on language codes and whitespace handling, see the discussions of the XPath lang() function and the XSLT <xsl:preserve-space> and <xsl:strip-space> elements.

3.1.4. Text Nodes

Text nodes are refreshingly simple; they contain text from an element. If the original text in the XML document contained entity or character references, they are resolved before the XPath text node is created. The text node is text, pure and simple. A text node is required to contain as much text as possible; the next or previous node can't be a text node.

You might have noticed that there are no CDATA nodes in this list. If your XML document contains text in a CDATA section, you can access the contents of the CDATA section as a text node. You have no way of knowing if a given text node was originally a CDATA section. Similarly, all entity references are resolved before anything in your stylesheet is evaluated, so you have no way of knowing if a given piece of text originally contained entity references.

3.1.5. Comment Nodes

A comment node is also very simple -- it contains some text. Every comment in the source document (except for comments in the DTD) becomes a comment node. The text of the comment node (returned by the text() node test) contains everything inside the comment, except the opening <!-- and the closing -->.

3.1.6. Processing Instruction Nodes

A processing instruction node has two parts, a name (returned by the name() function) and a string value. The string value is everything after the name, including whitespace, but not including the ?> that closes the processing instruction.

3.1.7. Namespace Nodes

Namespace nodes are almost never used in XSLT stylesheets; they exist primarily for the XSLT processor's benefit. Remember that the declaration of a namespace (such as xmlns:auth="http://www.authors.net"), even though it is technically an attribute in the XML source, becomes a namespace node, not an attribute node.

Previous | Home | Next
2.6. Summary | Book Index | 3.2. Location Paths
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Location Paths (XSLT)

 XSLSearch this book

3.2. Location Paths

One of the most common uses of XPath is to create location paths. A location path describes the location of something in an XML document. In our examples in the previous chapter, we used location paths on the match and select attributes of various XSLT elements. Those location paths described the parts of the XML document we wanted to work with. Most of the XPath expressions you'll use are location paths, and most of them are pretty simple. Before we dive in to the wonders of XPath, we need to discuss the context.

3.2.1. The Context

One of the most important concepts in XPath is the context. Everything we do in XPath is interpreted with respect to the context. You can think of an XML document as a hierarchy of directories in a filesystem. In our sonnet example, we could imagine that sonnet is a directory at the root level of the filesystem. The sonnet directory would, in turn, contain directories named auth:author, title, and lines. In this example, the context would be the current directory. If I go to a command line and execute a particular command (such as dir *.js), the results I get vary depending on the current directory. Similarly, the results of evaluating an XPath expression will probably vary based on the context.

Most of the time, we can think of the context as the node in the tree from which any expression is evaluated. To be completely accurate, the context consists of five things:

  • The context node (the "current directory"). The XPath expression is evaluated from this node.

  • Two integers, the context position and the context size. These integers are important when we're processing a group of nodes. For example, we could write an XPath expression that selects all of the <li> elements in a given document. The context size refers to the number of <li> items selected by that expression, and the context position refers to the position of the <li> we're currently processing.

  • A set of variables. This set includes names and values of all variables that are currently in scope.

  • A set of all the functions available to XPath expressions. Some of these functions are defined by the XPath and XSLT standards themselves; others might be extension functions defined by whomever created the stylesheet. (You'll read more about extension functions in Chapter 8, "Extending XSLT".)

  • A set of all the namespace declarations currently in scope.

Having said all that, most of the time you can ignore everything but the context node. To use our command line analogy one more time, if you're at a command line, you have a current directory; you also have (depending on your operating system) a number of environment variables defined. For most commands, you can focus on the current directory and ignore the environment variables.

3.2.2. Simple Location Paths

Now that we've talked about what a context is and why it matters, we'll look at some location paths. We'll start with a variety of simple location paths; as we go along, we'll look at more complex location paths that use all the various features of XPath. We already looked at one of the simplest XPath expressions:

<xsl:template match="/">

This template selects the root node of the document. We saw another simple XPath expression in the <xsl:value-of> element:

<xsl:value-of select="."/>

This template selects the context node, represented by a period. To complete our tour of very simple location paths, we can use the double period (..) to select the parent of the context node:

<xsl:value-of select=".."/>

All these XPath expressions have one thing in common: they don't use element names. As you might have noticed in our Hello World example, you can use element names to select elements that have a particular name:

<xsl:apply-templates select="greeting"/>

In this example, we select all of the <greeting> elements in the current context and apply the appropriate template to each of them. Turning to our XML sonnet, we can create location paths that specify more than one level in the document hierarchy:

<xsl:apply-templates select="lines/line/">

This example selects all <line> elements that are contained in any <lines> elements in the current context. If the current context doesn't have any <lines> elements, then this expression returns an empty node-set. If the current context has plenty of <lines> elements, but none of them contain any <line> elements, this expression also returns an empty node-set.

3.2.3. Relative and Absolute Expressions

The XPath specification talks about two kinds of XPath expressions, relative and absolute. Our previous example is a relative XPath expression because the nodes it specifies depend on the current context. An absolute XPath expression begins with a slash (/), which tells the XSLT processor to start at the root of the document, regardless of the current context. In other words, you can evaluate an absolute XPath expression from any context node you want, and the results will be the same. Here's an absolute XPath expression:

<xsl:apply-templates select="/sonnet/lines/line"/>

The good thing about an absolute expression is that you don't have to worry about the context node. Another benefit is that it makes it easy for the XSLT processor to find all nodes that match this expression: what we've said in this expression is that there must be a <sonnet> element at the root of the document, that element must contain at least one <lines> element, and that at least one of those <lines> elements must contain at least one <line> element. If any of those conditions fail, the XSLT processor can stop looking through the tree and return an empty node-set.

A possible disadvantage of using absolute XPath expressions is that it could make your templates more difficult to reuse. Both of these templates process <line> elements, but the second one is more difficult to reuse:

<xsl:template match="line">
  ...
</xsl:template>

<xsl:template match="/sonnet/lines/line">
  ...
</xsl:template>

If the second template has wonderful code for processing <line> elements, but your document contains <line> elements that don't match the absolute XPath expression, you can't reuse that template. You should keep that in mind as you design your templates.

3.2.4. Selecting Things Besides Elements with Location Paths

Up until now, we've discussed XPath expressions that used either element names (/sonnet/lines/line) or special characters (/ or ..) to select elements from an XML document. Obviously, XML documents contain things other than elements; we'll talk about how to select those other things here.

3.2.4.1. Selecting attributes

To select an attribute, use the at-sign (@) along with the attribute name. In our sample sonnet, you can select the type attribute of the <sonnet> element with the XPath expression /sonnet/@type. If the context node is the <sonnet> element itself, then the relative XPath expression @type does the same thing.

3.2.4.2. Selecting the text of an element

To select the text of an element, use the XPath node test text(). The XPath expression /sonnet/auth:author/last-name/text() selects the text of the last-name element in our example document. Be aware that the text of an element is the concatenation of all of its text nodes. Thus, the XPath expression /sonnet/auth:author/text() returns the following text:

ShakespeareWilliamBritish15641616

That's probably not the output you want; if you want to provide spacing, line breaks, or other formatting, you need to use the text() node test against all the child nodes individually.

3.2.4.3. Selecting comments, processing instructions, and namespace nodes

By this point, we've covered most of the things you're ever likely to do with an XPath expression. You can use a couple of other XPath node tests to describe parts of an XML document. The comment() and processing-instruction() node tests allow you to select comments and processing instructions from the XML document. Going back to our sample sonnet, the XPath expression /processing-instruction() returns the two processing instructions (named xml-stylesheet and cocoon-process). The expression /sonnet/comment() returns the comment node that begins, "Is there an official title for this sonnet?"

Processing comment nodes in this way can actually be useful. If you've entered comments into an XML document, you can use the comment() node test to display your comments only when you want. Here's an XSLT template you could use:

<xsl:template match="comment()">
  <span class="comment">
    <p><xsl:value-of select="."/></p>
  </span>
</xsl:template>

Elsewhere in your stylesheet, you could define CSS attributes to print comments in a large, bold, purple font. To remove all comments from your output document, simply go to your stylesheet and comment out any <xsl:apply-templates select="comment()"/> statements.

XPath has one other kind of node, the rarely used namespace node. To retrieve namespace nodes, you have to use something called the namespace axis; we'll discuss axes soon. One note about namespace nodes, if you ever have to use them: When matching namespace nodes, the namespace prefix isn't important. As an example, our sample sonnet used the auth namespace prefix, which maps to the value http://www.authors.com/. If a stylesheet uses the namespace prefix writers to refer to the same URL, then the XPath expression /sonnet/writers::* would return the <auth:author> element. Even though the namespace prefixes are different, the URLs they refer to are the same.

Having said all that, the chances that you'll ever need to use namespace nodes are pretty slim.

3.2.5. Using Wildcards

XPath features three wildcards:

  • The asterisk (*), which selects all element nodes in the current context. Be aware that the asterisk wildcard selects element nodes only; attributes, text nodes, comments, or processing instructions aren't included. You can also use a namespace prefix with an asterisk: in our sample sonnet, the XPath expression auth:* returns all element nodes in the current context that are associated with the namespace URL http://www.authors.com.

  • The at-sign and asterisk (@*), which selects all attribute nodes in the current context. You can use a namespace prefix with the attribute wildcard. In our sample sonnet, @auth:* returns all attribute nodes in the current context that are associated with the namespace URL http://www.authors.com.

  • The node() node test, which selects all nodes in the current context, regardless of type. This includes elements, text, comments, processing instructions, attributes, and namespace nodes.

In addition to these wildcards, XPath includes the double slash (//), which indicates that zero or more elements may occur between the slashes. For example, the XPath expression //line selects all <line> elements, regardless of where they appear in the document. This is an absolute XPath expression because it begins with a slash. You can also use the double slash at any point in an XPath expression; the expression /sonnet//line selects all <line> elements that are descendants of the <sonnet> element at the root of the XML document. The expressions /sonnet//line and /sonnet/descendant-or-self::line are equivalent.

WARNING: The double slash (//) is a very powerful operator, but be aware that it can make your stylesheets incredibly inefficient. If we use the XPath expression //line, the XSLT processor has to check every node in the document to see if there are any <line> elements. The more specific you can be in your XPath expressions, the less work the XSLT processor has to do, and the faster your stylesheets will execute. Thinking back to our filesystem metaphor, if I go to a Windows command prompt and type dir/s c:\*.xml, the operating system has to look in every subdirectory for any *.xml files that might be there. However, if I type dir /s c:\doug\projects\xml-docs\*.xml, the operating system has far fewer places to look, and the command will execute much faster.

3.2.6. Axes

To this point, we've been able to select child elements, attributes, text, comments, and processing instructions with some fairly simple XPath expressions. Obviously, we might want to select many other things, such as:

  • All ancestors of the context node

  • All descendants of the context node

  • All previous siblings or following siblings of the context node (siblings are nodes that have the same parent)

To select these things, XPath provides a number of axes that let you specify various collections of nodes. There are thirteen axes in all; we'll discuss all of them here, even though most of them won't be particularly useful to you. To use an axis in an XPath expression, type the name of the axis, a double colon (::), and the name of the element you want to select, if any.

Before we define all of the axes, though, we need to talk about XPath's unabbreviated syntax.

3.2.6.1. Unabbreviated syntax

To this point, all the XPath expressions we've looked at used the XPath abbreviated syntax. Most of the time, that's what you'll use; however, most of the lesser-used axes can only be specified with the unabbreviated syntax. For example, when we wrote an XPath expression to select all of the <line> elements in the current context, we used the abbreviated syntax:

<xsl:apply-templates select="line"/>

If you really enjoy typing, you can use the unabbreviated syntax to specify that you want all of the <line> children of the current context:

<xsl:apply-templates select="child::line"/>

We'll go through all of the axes now, pointing out which ones have an abbreviated syntax.

3.2.6.2. Axis roll call

The following list contains all of the axes defined by the XPath standard, with a brief description of each one.

child axis
Contains the children of the context node. As we've already mentioned, the XPath expression child::lines/child::line is equivalent to lines/line. If an XPath expression (such as /sonnet) doesn't have an axis specifier, the child axis is used by default. The children of the context node include all comment, element, processing instruction, and text nodes. Attribute and namespace nodes are not considered children of the context node.

parent axis
Contains the parent of the context node, if there is one. (If the context node is the root node, the parent axis returns an empty node-set.) This axis can be abbreviated with a double period (..). The expressions parent::sonnet and ../sonnet are equivalent. If the context node does not have a <sonnet> element as its parent, these XPath expressions return an empty node-set.

self axis
Contains the context node itself. The self axis can be abbreviated with a single period (.). The expressions self::* and . are equivalent.

attribute axis
Contains the attributes of the context node. If the context node is not an element node, this axis is empty. The attribute axis can be abbreviated with the at-sign (@). The expressions attribute::type and @type are equivalent.

ancestor axis
Contains the parent of the context node, the parent's parent, etc. The ancestor axis always contains the root node unless the context node is the root node.

ancestor-or-self axis
Contains the context node, its parent, its parent's parent, and so on. This axis always includes the root node.

descendant axis
Contains all children of the context node, all children of all the children of the context node, and so on. The children are all of the comment, element, processing instruction, and text nodes beneath the context node. In other words, the descendant axis does not include attribute or namespace nodes. (As we discussed earlier, although an attribute node has an element node as a parent, an attribute node is not considered a child of that element.)

descendant-or-self axis
Contains the context node and all the children of the context node, all the children of all the children of the context node, all the children of the children of all the children of the context node, and so on. As always, the children of the context node include all comment, element, processing instruction, and text nodes; attribute and namespace nodes are not included.

preceding-sibling axis
Contains all preceding siblings of the context node; in other words, all nodes that have the same parent as the context node and appear before the context node in the XML document. If the context node is an attribute node or a namespace node, the preceding-sibling axis is empty.

following-sibling axis
Contains all the following siblings of the context node; in other words, all nodes that have the same parent as the context node and appear after the context node in the XML document. If the context node is an attribute node or a namespace node, the following-sibling axis is empty.

preceding axis
Contains all nodes that appear before the context node in the document, except ancestors, attribute nodes, and namespace nodes.

following axis
Contains all nodes that appear after the context node in the document, except descendants, attribute nodes, and namespace nodes.

namespace axis
Contains the namespace nodes of the context node. If the context node is not an element node, this axis is empty.

3.2.7. Predicates

There's one more aspect of XPath expressions that we haven't discussed: predicates. Predicates are filters that restrict the nodes selected by an XPath expression. Each predicate is evaluated and converted to a Boolean value (either true or false). If the predicate is true for a given node, that node will be selected; otherwise, the node is not selected. Predicates always appear inside square brackets ([]). Here's an example:

<xsl:apply-templates select="line[3]"/>

This expression selects the third <line> element in the current context. If there are two or fewer <line> elements in the current context, this XPath expression returns an empty node-set. Several things can be part of a predicate; we'll go through them here.

3.2.7.1. Numbers in predicates

A number inside square brackets selects nodes that have a particular position. For example, the XPath expression line[7] selects the seventh <line> element in the context node. XPath also provides the boolean and and or operators as well as the union operator (|) to combine predicates. The expression line[position()=3 and @style] matches all <line> elements that occur third and have a style attribute, while line[position()=3 or @style] matches all <line> elements that either occur third or have a style attribute. With the union operator, the expression line[3|7] matches all third and seventh <line> elements in the current context, as does the more verbose line[3] | line[7].

3.2.7.2. Functions in predicates

In addition to numbers, we can use XPath and XSLT functions inside predicates. Here are some examples:

line[last()]
Selects the last <line> element in the current context.

line[position() mod 2 = 0]
Selects all even-numbered <line> elements. (The mod operator returns the remainder after a division; the position of any even-numbered element divided by 2 has a remainder of 0.)

sonnet[@type="Shakespearean"]
Selects all <sonnet> elements that have a type attribute with the value Shakespearean.

ancestor::table[@border="1"]
Selects all <table> ancestors of the current context that have a border attribute with the value 1.

count(/body/table[@border="1"])
Returns the number of <table> elements with a border attribute equal to 1 that are children of <body> elements that are children of the root node. Notice that in this case we're using an XPath predicate expression as an argument to a function.

Previous | Home | Next
3. XPath: A Syntax for Describing Needles and Haystacks | Book Index | 3.3. Attribute Value Templates
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Attribute Value Templates (XSLT)

 XSLSearch this book

3.3. Attribute Value Templates

Although they're technically defined in the XSLT specification (in section 7.6.2, to be exact), we'll discuss attribute value templates here. An attribute value template is an XPath expression that is evaluated, and the result of that evaluation replaces the attribute value template. For example, we could create an HTML <table> element like this:

<table border="{@size}"/>

In this example, the XPath expression @size is evaluated, and its value, whatever that happens to be, is inserted into the output tree as the value of the border attribute. Attribute value templates can be used in any literal result elements in your stylesheet (for HTML elements and other things that aren't part of the XSLT namespace, for example). You can also use attribute value templates in the following XSLT attributes:

  • The name and namespace attributes of the <xsl:attribute> element

  • The name and namespace attributes of the <xsl:element> element

  • The format, lang, letter-value, grouping-separator, and grouping-size attributes of the <xsl:number> element

  • The name attribute of the <xsl:processing-instruction> element

  • The lang, data-type, order, and case-order attributes of the <xsl:sort> element

Previous | Home | Next
3.2. Location Paths | Book Index | 3.4. XPath Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Datatypes (XSLT)

 XSLSearch this book

3.4. XPath Datatypes

An XPath expression returns one of four datatypes:

node-set
Represents a set of nodes. The set can be empty, or it can contain any number of nodes.

boolean
Represents the value true or false. Be aware that the true or false strings have no special meaning or value in XPath; see Section 4.2.1.2, "Boolean examples" in Chapter 4, "Branching and Control Elements" for a more detailed discussion of boolean values.

number
Represents a floating-point number. All numbers in XPath and XSLT are implemented as floating-point numbers; the integer (or int) datatype does not exist in XPath and XSLT. Specifically, all numbers are implemented as IEEE 754 floating-point numbers, the same standard used by the Java float and double primitive types. In addition to ordinary numbers, there are five special values for numbers: positive and negative infinity, positive and negative zero, and NaN, the special symbol for anything that is not a number.

string
Represents zero or more characters, as defined in the XML specification.

These datatypes are usually simple, and with the exception of node-sets, converting between types is usually straightforward. We won't discuss these datatypes in any more detail here; instead, we'll discuss datatypes and conversions as we need them to do specific tasks.

Previous | Home | Next
3.3. Attribute Value Templates | Book Index | 3.5. The XPath View of an XML Document
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The XPath View of an XML Document (XSLT)

 XSLSearch this book

3.5. The XPath View of an XML Document

Before we leave the subject of XPath, we'll look at a stylesheet that generates a pictorial view of a document. The stylesheet has to distinguish between all of the different XPath node types, including any rarely used namespace nodes.

3.5.1. Output View

Figure 3-1 shows the output of our stylesheet. In this graphical view of the document, the nested HTML tables illustrate which nodes are contained inside of others, as well as the sequence in which these nodes occur in the original document. In the section of the document visible in Figure 3-1, the root of the document contains, in order, two processing instructions and two comments, followed by the <sonnet> element. The <sonnet> element, in turn, contains two attributes and an <auth:author> element. The <auth:author> element contains a namespace node and an element. Be aware that this stylesheet has its limitations; if you throw a very large XML document at it, it will generate an HTML file with many levels of nested tables -- probably more levels than your browser can handle.

Figure 3-1. XPath tree view of an XML document

3.5.2. The Stylesheet

Now we'll take a look at the stylesheet and how it works. The stylesheet creates a number of nested tables to illustrate the XPath view of the document. We begin by writing the basic HTML elements to the output stream and creating a legend for our nested tree view:

  <xsl:template match="/">
    <html>
      <head>
        <title>XPath view of your document</title>
        <style type="text/css">
          <xsl:comment>
            span.literal         { font-family: Courier, monospace; }
          </xsl:comment>
        </style>
      </head>
      <body>
        <h1>XPath view of your document</h1>
        <p>The structure of your document (as defined by
           the XPath standard) is outlined below.</p>
        <table cellspacing="5" cellpadding="2" border="0">
          <tr>
            <td colspan="7">
              <b>Node types:</b>
            </td>
          </tr>
          <tr>
            <td bgcolor="#99CCCC"><b>root</b></td>
            <td bgcolor="#CCCC99"><b>element</b></td>
            <td bgcolor="#FFFF99"><b>attribute</b></td>
            <td bgcolor="#FFCC99"><b>text</b></td>
            <td bgcolor="#CCCCFF"><b>comment</b></td>
            <td bgcolor="#99FF99"><b>processing instruction</b></td>
            <td bgcolor="#CC99CC"><b>namespace</b></td>
          </tr>
        </table>
        <br />

Having created the legend for our document, we select all the different types of nodes and represent them:

<xsl:for-each select="namespace::*">
  ...
</xsl:for-each>
<xsl:for-each select="*|comment()|processing-instruction()|text()">
            ...
          </xsl:for-each>

The only difficult thing here was remembering to get all of the namespace nodes. These nodes are rarely used (with the exception of this example, I've never needed them), and they can only be selected with the namespace:: axis. Also, we process the attribute nodes when we process their element node parents; that's why the select attribute just shown doesn't have @* in it.

Here's the complete stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>XPath view of your document</title>
        <style type="text/css">
          <xsl:comment>
            span.literal         { font-family: Courier, monospace; }
          </xsl:comment>
        </style>
      </head>
      <body>
        <h1>XPath view of your document</h1>
        <p>The structure of your document (as defined by
          the XPath standard) is outlined below.</p>
        <table cellspacing="5" cellpadding="2" border="0">
          <tr>
            <td colspan="7">
              <b>Node types:</b>
            </td>
          </tr>
          <tr>
            <td bgcolor="#99CCCC"><b>root</b></td>
            <td bgcolor="#CCCC99"><b>element</b></td>
            <td bgcolor="#FFFF99"><b>attribute</b></td>
            <td bgcolor="#FFCC99"><b>text</b></td>
            <td bgcolor="#CCCCFF"><b>comment</b></td>
            <td bgcolor="#99FF99"><b>processing instruction</b></td>
            <td bgcolor="#CC99CC"><b>namespace</b></td>
          </tr>
        </table>
        <br />
        <table width="100%" border="1" bgcolor="#99CCCC" cellspacing="2">
          <tr bgcolor="#99CCCC">
            <td colspan="2">
              <b>root:</b>
            </td>
          </tr>
          <xsl:for-each select="namespace::*">
            <tr bgcolor="#CC99CC">
              <td width="15">   </td>
              <td>
                <xsl:call-template name="namespace-node"/>
              </td>
            </tr>
          </xsl:for-each>
          <xsl:for-each select="*|comment()|processing-instruction()|text()">
            <tr bgcolor="#99CCCC">
              <td width="15">   </td>
              <td>
                <xsl:apply-templates select="."/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="comment()">
    <table width="100%" cellspacing="2">
      <tr>
        <td bgcolor="#CCCCFF">
          <b>comment: </b>
          <span class="literal">
            <xsl:value-of select="."/>
          </span>
        </td>
      </tr>
    </table>
  </xsl:template>

  <xsl:template match="processing-instruction()">
    <table border="0" width="100%" cellspacing="2">
      <tr>
        <td bgcolor="#99FF99">
          <b>processing instruction: </b>
          <span class="literal">
            <xsl:text>&lt;?</xsl:text>
            <xsl:value-of select="name()"/>
            <xsl:text>?&gt;</xsl:text>
            <br />
            <xsl:value-of select="."/>
          </span>
        </td>
      </tr>
    </table>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:if test="string-length(normalize-space(.))">
      <tr>
        <td bgcolor="#CCCC99" width="15">    </td>
        <td bgcolor="#FFCC99" width="100%">
          <b>text: </b>
          <span class="literal">
            <xsl:value-of select="."/>
          </span>
        </td>
      </tr>
    </xsl:if>
  </xsl:template>

  <xsl:template name="namespace-node">
    <table border="0" width="100%" cellspacing="2">
      <tr>
        <td bgcolor="#CC99CC">
          <b>namespace: </b>
          <span class="literal">
            <xsl:value-of select="name()"/>
          </span>
          <br />
          <span class="literal">
            <xsl:value-of select="."/>
          </span>
        </td>
      </tr>
    </table>
  </xsl:template>

  <xsl:template match="*">
    <table border="1" width="100%" cellspacing="2">
      <xsl:choose>
        <xsl:when test="count(@*) &gt; 0">
          <tr>
            <td bgcolor="#CCCC99" colspan="2">
              <b>element: </b>
              <span class="literal">
                <xsl:text>&lt;</xsl:text>
                <xsl:value-of select="name()"/>
                <xsl:text>&gt;</xsl:text>
              </span>
              <table border="0" width="100%" cellspacing="2">
                <tr>
                  <td bgcolor="#CCCC99" width="15">   </td>
                  <td bgcolor="#FFFF99" width="20%">
                    <b>attribute name</b>
                  </td>
                  <td bgcolor="#FFFF99">
                    <b>value</b>
                  </td>
                </tr>
                <xsl:for-each select="@*">
                  <tr>
                    <td bgcolor="#CCCC99" width="15">   </td>
                    <td bgcolor="#FFFF99" width="20%">
                      <span class="literal">
                        <xsl:value-of select="name()"/>
                      </span>
                    </td>
                    <td bgcolor="#FFFF99">
                      <span class="literal">
                        <xsl:value-of select="."/>
                      </span>
                    </td>
                  </tr>
                </xsl:for-each>
              </table>
            </td>
          </tr>
        </xsl:when>
        <xsl:otherwise>
          <tr>
            <td bgcolor="#CCCC99" colspan="2">
              <b>element: </b>
              <span class="literal">
                <xsl:text>&lt;</xsl:text>
                <xsl:value-of select="name()"/>
                <xsl:text>&gt;</xsl:text>
              </span>
            </td>
          </tr>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:for-each select="namespace::*">
        <tr>
          <td bgcolor="#CCCC99" width="15">    </td>
          <td bgcolor="#CC99CC">
            <xsl:call-template name="namespace-node"/>
          </td>
        </tr>
      </xsl:for-each>
      <xsl:for-each select="*|comment()|processing-instruction()|text()">
        <tr bgcolor="#CCCC99">
          <td width="15">   </td>
          <td>
            <xsl:apply-templates select="."/>
          </td>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>

</xsl:stylesheet>

Before we leave this example, there are a couple of other techniques worth mentioning here. First, notice that we used CSS to format some of the output. XSLT and CSS aren't mutually exclusive; you can use XSLT to generate CSS as part of an HTML page, as we demonstrated here. Second, we used wildcard expressions like * and @* to process all the elements and attributes in our document. Use of these expressions allows us to apply this stylesheet to any XML document, regardless of the tags it uses. Because we use these wildcard expressions, we have to use the name() function to get the name of the element or attribute we're currently working with. Third, notice that we used conditional logic and the expression count(@*) &gt; 0 to determine whether a given element has attributes. We'll talk more about conditional logic in the next chapter.

Previous | Home | Next
3.4. XPath Datatypes | Book Index | 3.6. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

3.6. Summary

We've covered the basics of XPath. Hopefully, at this point you're comfortable with the idea of writing XPath expressions to describe parts of an XML document. As we go through the following chapters, you'll see XPath expressions used in a variety of ways, all of which build on the basics we've discussed here. You'll probably spend most of your debugging time working on the XPath expressions in your stylesheets. Very few of the things we'll do in the rest of the book are possible without precise XPath expressions.

Previous | Home | Next
3.5. The XPath View of an XML Document | Book Index | 4. Branching and Control Elements
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Branching and Control Elements (XSLT)

 XSLTSearch this book

Chapter 4. Branching and Control Elements

Contents:

Goals of This Chapter
Branching Elements of XSLT
Invoking Templates by Name
Parameters
Variables
Using Recursion to Do Most Anything
A Stylesheet That Emulates a for Loop
A Stylesheet That Generates a Stylesheet That Emulates a for Loop
Summary

So far, we've done some straightforward transformations and we've been able to do some reasonably sophisticated things. To do truly useful work, though, we'll need to use logic in our stylesheets. In this chapter, we'll discuss the XSLT elements that allow you to do just that. Although you'll see several XML elements that look like constructs from other programming languages, they're not exactly the same. As we go along, we'll discuss what makes XSLT different and how to do common tasks with your stylesheets.

4.1. Goals of This Chapter

By the end of this chapter, you should:

  • Know the XSLT elements used for branching and control

  • Understand the differences between XSLT's branching elements and similar constructs in other programming languages

  • Know how to invoke XSLT templates by name and how to pass parameters to them, if you want

  • Know how to use XSLT variables

  • Understand how to use recursion to get around the "limitations" of XSLT's branching and control elements

Previous | Home | Next
3.6. Summary | Book Index | 4.2. Branching Elements of XSLT
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Branching Elements of XSLT (XSLT)

 XSLSearch this book

4.2. Branching Elements of XSLT

Three XSLT elements are used for branching: <xsl:if>, <xsl:choose>, and <xsl:for-each>. The first two are much like the if and case statements you may be familiar with from other languages, while the for-each element is significantly different from the for or do-while structures in other languages. We'll discuss all of them here.

4.2.1. The <xsl:if> Element

The <xsl:if> element looks like this:

<xsl:if test="count(zone) &gt; 2">
  <xsl:text>Applicable zones: </xsl:text>
  <xsl:apply-templates select="zone"/>
</xsl:if>

The <xsl:if> element, surprisingly enough, implements an if statement. The element has only one attribute, test. If the value of test evaluates to the boolean value true, then all elements inside the <xsl:if> are processed. If test evaluates to false, then the contents of the <xsl:if> element are ignored. (If you want to implement an if-then-else statement, check out the <xsl:choose> element described in the next section.)

Notice that we used &gt; instead of > in the attribute value. You're always safe using &gt; here, although some XSLT processors process the greater-than sign correctly if you use > instead. If you need to use the less-than operator (<), you'll have to use the &lt; entity. The same holds true for the less-than-or-equal operator (<=) and the greater-than-or-equal (>=) operators. See Section B.4.2, "Boolean Operators" for more information on this topic.

4.2.1.1. Converting to boolean values

The <xsl:if> element is pretty simple, but it's the first time we've had to deal with boolean values. These values will come up later, so we might as well discuss them here. Attributes like the test attribute of the <xsl:if> element convert whatever their values happen to be into a boolean value. If that boolean value is true, the <xsl:if> element is processed. (The <xsl:when> element, which we'll discuss in just a minute, has a test attribute as well.)

Here's the rundown of how various datatypes are converted to boolean values:

number
If a number is positive or negative zero, it is false. If a numeric value is NaN (not a number; if I try to use the string "blue" as a number, the result is NaN), it is false. If a number has any other value, it is true.

node-set
An empty node-set is false, a non-empty node-set is true.

string
A zero-length string is false; a string whose length is not zero is true.

These rules are defined in Section 4.3 of the XPath specification.

4.2.1.2. Boolean examples

Here are some examples that illustrate how boolean values evaluate the test attribute:

<xsl:if test="count(zone) &gt;= 2">
This is a boolean expression because it uses the greater-than-or-equal boolean operator. If the count() function returns a value greater than or equal to 2, the test attribute is true. Otherwise, the test attribute is false.

<xsl:if test="$x">
The variable x is evaluated. If it is a string, then the test attribute is true only if the string has a length greater than zero. If it is a node-set, then the test attribute is true only if the node-set has at least one member. If it is a number, then the test attribute is true only if the number is anything other than positive zero, negative zero, or NaN. (Of course, if x is a boolean value, true is true and false is false.)

<xsl:if test="true()">
The boolean function true() always returns the boolean value true. Therefore, this test attribute is always true.

<xsl:if test="true">
This example is a trick. This test attribute is true only if there is at least one <true> element in the current context. The XSLT processor interprets the value true as an XPath expression that specifies all <true> elements in the current context. The strings true and false don't have any special significance in XSLT.

<xsl:if test="'true'">
This test attribute is always true. Notice that in this case we used single quotes inside double quotes to specify that this is a literal string, not an element name. This test attribute is always true because the string has a length greater than zero, not because its value happens to be the word "true."

<xsl:if test="'false'">
Another trick example; this test attribute is always true. As before, we used single quotes inside double quotes to specify that this is a literal string. Because the string has a length greater than zero, the test attribute is always true. The value of the nonempty string, confusing as it is, doesn't matter.

<xsl:if test="not(3)">
This test attribute is always false. The literal 3 evaluates to true, so its negation is false. On the other hand, the expressions not(0) and not(-0) are always true.

<xsl:if test="false()">
This test attribute is always false. The boolean function false() always returns the boolean value false.

<xsl:if test="section/section">
The XPath expression section/section returns a node-set. If the current context contains one or more <section> elements that contain a <section> element in turn, the test attribute is true. If no such elements exist in the current context, the test attribute is false.

4.2.2. The <xsl:choose> Element

The <xsl:choose> element is the equivalent of a case or switch statement in other programming languages. You can also use it to implement an if-then-else statement. An <xsl:choose> contains at least one <xsl:when> element (logically equivalent to an <xsl:if> element), with an optional <xsl:otherwise> element. The test attribute of each <xsl:when> element is evaluated until the XSLT processor finds one that evaluates to true. When that happens, the contents of that <xsl:when> element are evaluated. If none of the <xsl:when> elements have a test that is true, the contents of the <xsl:otherwise> element (if there is one) are processed.

Here's how these XSLT elements compare to the switch or select/case statements you might know from other languages:

  • The C, C++, and Java switch statement is roughly equivalent to the <xsl:choose> element. The one exception is that procedural languages tend to use fallthrough processing. In other words, if a branch of the switch statement evaluates to true, the runtime executes everything until it encounters a break statement, even if some of that code is part of other branches. The <xsl:choose> element doesn't work that way. If a given <xsl:when> evaluates to true, only the statements inside that <xsl:when> are evaluated.

  • The Java case statement is equivalent to the <xsl:when> element. In Java, if a given case statement does not end with a break statement, the following case is executed as well. Again, this is not the case with XSLT; only the contents of the first <xsl:when> element that is true are processed.

  • The Java and C++ default statement is equivalent to the <xsl:otherwise> element.

4.2.2.1. <xsl:choose> example

Here's a sample <xsl:choose> element that sets the background color of the table's rows. If the bgcolor attribute is coded on the <table-row> element, the value of that attribute is used as the color; otherwise, the sample uses the position() function and the mod operator to cycle the colors between papayawhip, mintcream, lavender, and whitesmoke.

<xsl:template match="table-row">
  <tr>
    <xsl:attribute name="bgcolor">
     <xsl:choose>
        <xsl:when test="@bgcolor">
          <xsl:value-of select="@bgcolor"/>
        </xsl:when>
        <xsl:when test="position() mod 4 = 0">
          <xsl:text>papayawhip</xsl:text>
        </xsl:when>
        <xsl:when test="position() mod 4 = 1">
          <xsl:text>mintcream</xsl:text>
        </xsl:when>
        <xsl:when test="position() mod 4 = 2">
          <xsl:text>lavender</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>whitesmoke</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
    <xsl:apply-templates select="*"/>
  </tr>
</xsl:template>

In this sample, we use <xsl:choose> to generate the value of the bgcolor attribute of the <tr> element. Our first test is to see if the bgcolor attribute of the <table-row> element exists; if it does, we use that value for the background color and the <xsl:otherwise> and other <xsl:when> elements are ignored. (If the bgcolor attribute is coded, the XPath expression @bgcolor returns a node-set containing a single attribute node.)

The next three <xsl:when> elements check the position of the current <table-row> element. The use of the mod operator here is the most efficient way to cycle between the various options. Finally, we use an <xsl:otherwise> element to specify whitesmoke as the default case. If position() mod 4 = 3, the background color will be whitesmoke.

A couple of minor details: in this example, we could replace the <xsl:otherwise> element with <xsl:when test="position() mod 4 = 3">; that is logically equivalent to the example as coded previously. For obfuscation bonus points, we could code the second <xsl:when> element as <xsl:when test="not(position() mod 4)">. (Remember that the boolean negation of zero is true.)

4.2.3. The <xsl:for-each> Element

If you want to process all the nodes that match a certain criteria, you can use the <xsl:for-each> element. Be aware that this isn't a traditional for loop; you can't ask the XSLT processor to do something like this:

for i = 1 to 10 do

The <xsl:for-each> element lets you select a set of nodes, then do something with each of them. Let me mention again that this is not the same as a traditional for loop. Another important point is that the current node changes with each iteration through the <xsl:for-each> element. We'll go through some examples to illustrate this.

4.2.3.1. <xsl:for-each> example

Here's a sample that selects all <section> elements inside a <tutorial> element and then uses a second <xsl:for-each> element to select all the <panel> elements inside each <section> element:

<xsl:template match="tutorial">
  <xsl:for-each select="section">
    <h1>
      <xsl:text>Section </xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text>. </xsl:text>
      <xsl:value-of select="title"/>
    </h1>
    <ul>
      <xsl:for-each select="panel">
        <li>
          <xsl:value-of select="position()"/>
          <xsl:text>. </xsl:text>
          <xsl:value-of select="title"/>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:for-each>
</xsl:template>

Given this XML document:

<tutorial>
  <section>
    <title>Gene Splicing for Young People</title>
    <panel>
      <title>Introduction</title>
      <!-- ... -->
    </panel>
    <panel>
      <title>Discovering the secrets of life and creation</title>
      <!-- ... -->
    </panel>
    <panel>
      <title>"I created him for good, but he's turned out evil!"</title>
      <!-- ... -->
    </panel>
    <panel>
      <title>When angry mobs storm your castle</title>
      <!-- ... -->
    </panel>
  </section>
</tutorial>

The previous template produces these results:

<h1>Section 1. Gene Splicing for Young People</h1>
<ul>
  <li>1. Introduction</li>
  <li>2. Discovering the secrets of life and creation</li>
  <li>3. "I created him for good, but he's turned out evil!"</li>
  <li>4. When angry mobs storm your castle</li>
</ul>

Each time a select attribute is processed, it is evaluated in terms of the current node. As the XSLT processor cycles through all the <xsl:section> and <xsl:panel> elements, each of them in turn becomes the current node. By using iteration, we've generated a table of contents with a very simple template.

Previous | Home | Next
4. Branching and Control Elements | Book Index | 4.3. Invoking Templates by Name
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Invoking Templates by Name (XSLT)

 XSLSearch this book

4.3. Invoking Templates by Name

Up to this point, we've always used XSLT's <xsl:apply-templates> element to invoke other templates. You can think of this as a limited form of polymorphism; a single instruction is invoked a number of times, and the XSLT processor uses each node in the node-set to determine which <xsl:template> to invoke. Most of the time, this is what we want. However, sometimes we want to invoke a particular template. XSLT allows us to do this with the <xsl:call-template> element.

4.3.1. How It Works

To invoke a template by name, two things have to happen:

  • The template you want to invoke has to have a name.

  • You use the <xsl:call-template> element to invoke the named template.

Here's how to do this. Say we have a template named createMasthead that creates the masthead of a web page. Whenever we create an HTML page for our web site, we want to invoke the createMasthead template to create the masthead. Here's what our stylesheet would look like:

<xsl:template name="createMasthead">
  <!-- interesting stuff that generates the masthead goes here -->
</xsl:template>
...
<xsl:template match="/">
  <html>
    <head>
      <title><xsl:value-of select="title"/></title>
    </head>
    <body>
      <xsl:call-template name="createMasthead"/>
...

Named templates are extremely useful for defining commonly used markup. For example, say you're using an XSLT stylesheet to create web pages with a particular look and feel. You can write named templates that create the header, footer, navigation areas, or other items that define how your web page will look. Every time you need to create a web page, simply use <xsl:call-template> to invoke those templates and create the look and feel you want.

Even better, if you put those named templates in a separate stylesheet and import the stylesheet (with either <xsl:import> or <xsl:include>), you can create a set of stylesheets that generate the look and feel of the web site you want. If you decide to redesign your web site, redesign the stylesheets that define the common graphical and layout elements. Change those stylesheets, regenerate your web site, and voila! You will see an instantly updated web site. (See Chapter 9, "Case Study: The Toot-O-Matic" for an example.)

4.3.2. Templates à la Mode

The XSLT <xsl:template> element has a mode attribute that lets you process the same set of nodes several times. For example, we might want to process <h1> elements one way when we generate a table of contents, and another way when we process the document as a whole. We could use the mode attribute to define different templates for different purposes:

<xsl:template match="h1" mode="build-toc">
  <!-- Template to process the <h1> element for table of contents -->
</xsl:template>

<xsl:template match="h1" mode="process-text">
  <!-- Template to process the <h1> element along with the rest   -->
  <!-- of the document                                                  -->
</xsl:template>

We can then start applying templates with the mode attribute:

<xsl:template match="/">
  <html>
    <body>
      <h1>Table of Contents</h1>
      <ul>
        <xsl:apply-templates select="h1" mode="build-toc"/>
      </ul>
      <xsl:apply-templates select="*" mode="process-text"/>
    </body>
  </html>
</xsl:template>

This style of coding makes maintenance much easier; if the table of contents isn't generated correctly, the templates with mode="build-toc" are the obvious place to start debugging. See Chapter 9, "Case Study: The Toot-O-Matic" for a more detailed discussion of the mode attribute.

Previous | Home | Next
4.2. Branching Elements of XSLT | Book Index | 4.4. Parameters
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Parameters (XSLT)

 XSLSearch this book

4.4. Parameters

The XSLT <xsl:param> and <xsl:with-param> elements allow you to pass parameters to a template. You can pass templates with either the <call-template> element or the <apply-templates> element; we'll discuss the details in this section.

4.4.1. Defining a Parameter in a Template

To define a parameter in a template, use the <xsl:param> element. Here's an example of a template that defines two parameters:

<xsl:template name="calcuateArea">
  <xsl:param name="width"/>
  <xsl:param name="height"/>

  <xsl:value-of select="$width * $height"/>
</xsl:template>

Conceptually, this is a lot like writing code in a traditional programming language, isn't it? Our template here defines two parameters, width and height, and outputs their product.

If you want, you can define a default value for a parameter. There are two ways to define a default value; the simplest is to use a select attribute on the <xsl:param> element:

<template name="addTableCell">
  <xsl:param name="bgColor" select="'blue'"/>
  <xsl:param name="width" select="150"/>
  <xsl:param name="content"/>
  <td width="{$width}" bgcolor="{$bgColor}">
    <xsl:apply-templates select="$content"/>
  </td>
</template>

In this example, the default values of the parameters bgColor and width are 'blue' and 150, respectively. If we invoke this template without specifying values for these parameters, the default values are used. Also notice that we generated the values of the width and bgcolor attributes of the HTML <td> tag with attribute value templates, the values in curly braces. For more information, see Section 3.3, "Attribute Value Templates" in Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks".

WARNING: Notice that in the previous sample, we put single quotes around the value blue, but we didn't do it around the value 150. Without the single quotes around blue, the XSLT processor assumes we want to select all the <blue> elements in the current context, which is probably not what we want. The XSLT processor is clever enough to realize that the value 150 can't be an XML element name (the XML 1.0 Specification says element names can't begin with numbers), so we don't need the single quotes around a numeric value.

Try to keep this in mind when you're using parameters. You'll probably forget it at some point, and you'll probably go nuts trying to figure out the strange behavior you're getting from the XSLT processor.

The second way to define a default value for a parameter is to include content inside the <xsl:param> element:

<template name="addTableCell">
  <xsl:param name="bgColor">
    <xsl:text>blue</xsl:text>
  </xsl:param>
  <xsl:param name="width">
    <xsl:value-of select="7+8"/><xsl:text>0</xsl:text>
  </xsl:param>
  <xsl:param name="content"/>
  <td width="{$width}" bgcolor="{$bgColor}">
    <xsl:apply-templates select="$content"/>
  </td>
</template>

In this example, we used <xsl:text> and <xsl:value-of> elements to define the default values of the parameters. Out of sheer perverseness, we defined the value of width as the concatenation of the numeric expression 7+8, followed by the string "0". This example produces the same results as the previous one.

4.4.2. Passing Parameters

If we invoke a template by name, which is similar to calling a subroutine, we'll need to pass parameters to those templates. We do this with the <xsl:with-param> element. For example, let's say we want to call a template named draw-box, and pass the parameters startX, startY, endX, and endY to it. Here's what we'd do:

<xsl:call-template name="draw-box">
  <xsl:with-param name="startX" select="50"/>
  <xsl:with-param name="startY" select="50"/>
  <xsl:with-param name="endX" select="97"/>
  <xsl:with-param name="endY" select="144"/>
</xsl:call-template>

In this sample, we've called the template named draw-box with the four parameters we mentioned earlier. Notice that up until now, <xsl:call-template> has always been an empty tag; here, though, the parameters are the content of the <xsl:call-template> element. (If you want, you can do the same thing with <xsl:apply-templates>.) We used the <xsl:with-param> element with the <xsl:call-template> element here, but you can also use it with <xsl:apply-templates>.

If we're going to pass parameters to a template, we have to set up the template so that it expects the parameters we're passing. To do this, we'll use the <xsl:param> element inside the template. Here are some examples:

<xsl:template name="draw-box">
<xsl:param name="startX"/>
<xsl:param name="startY" select="'0'"/>
<xsl:param name="endX">
10
</xsl:param>
<xsl:param name="endY">
10
</xsl:param>
...
</xsl:template>

A couple of notes about the <xsl:param> element:

  • If you define any <xsl:param> elements in a template, they must be the first thing in the template.

  • The <xsl:param> element allows you to define a default value for the parameter. If the calling template doesn't supply a value, the default is used instead. The last three <xsl:param> elements in our previous example define default values.

  • The <xsl:param> element has the same content model as <xsl:variable>. With no content and no select attribute, the default value of the parameter is an empty string (""). With a select attribute, the default value of the parameter is the value of the select attribute. If the <xsl:param> element contains content, the default value of the parameter is the content of the <xsl:param> element.

4.4.3. Global Parameters

XSLT allows you to define parameters whose scope is the entire stylesheet. You can define default values for these parameters and you can pass values to those parameters externally to the stylesheet. Before we talk about how to pass in values for global parameters, we'll show you how to create them. Any parameters that are top-level elements (any <xsl:param> elements whose parent is <xsl:stylesheet>) are global parameters. Here's an example:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:param name="startX"/>
  <xsl:param name="baseColor"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Global parameters example</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>The value of startX is: </xsl:text>
    <xsl:value-of select="$startX"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>The value of baseColor is: </xsl:text>

    <xsl:value-of select="$baseColor"/>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

How you pass values for global parameters depends on the XSLT processor you're using. We'll go through some examples here for all the usual suspects. Let's say we want to pass the numeric value 50 as the value for startX, and the string value magenta as the default value for baseColor. Here are the commands you'd use to do that.

4.4.3.1. Xalan

To pass global parameters to Xalan, you can define them on the Xalan command line:

java org.apache.xalan.xslt.Process -in xyz.xml -xsl params.xsl
  -param startX 50 -param baseColor magenta

(This command should be on a single line.)

4.4.3.2. XT

If you're using James Clark's XSLT processor, you can pass parameters like this:

java com.jclark.xsl.sax.Driver xyz.xml params.xsl startX=50 baseColor=magenta

4.4.3.3. Microsoft's XSLT tools

Microsoft's XSLT tools support external parameters like this:

msxsl xyz.xml params.xsl startX=50 baseColor=magenta

4.4.3.4. Saxon

Saxon supports external parameters like this:

java com.icl.saxon.StyleSheet xyz.xml params.xsl startX=50 baseColor=magenta

4.4.3.5. Oracle

If you're using Oracle's parser, stylesheet parameters are passed like this:

java oracle.xml.parser.v2.oraxsl -p startX=50 -p baseColor='magenta' xyz.xml
params.xsl

(This command should be on a single line.) Notice that for the Oracle parser, we had to put single quotes around the text value magenta.

Using this stylesheet with any XML document and any of the XSLT processors listed here produces these results:

Global parameters example

The value of startX is: 50
The value of baseColor is: magenta

4.4.3.6. Setting global parameters in a Java program

If your XSLT engine supports the Transformation API for XML (TrAX), you can embed the XSLT processor and set global parameters in your code. Here's an example that uses TrAX support:

import java.io.File;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class GlobalParameters
{
  public static void parseAndProcess(String sourceID,
                                     String xslID,
                                     String outputID)
  {
    try
    {
      TransformerFactory tfactory = TransformerFactory.newInstance();

      Transformer transformer
        = tfactory.newTransformer(new StreamSource(xslID));

      // Use the setParameter method to set global parameters
      transformer.setParameter("startX", new Integer(50));
      transformer.setParameter("baseColor", "magenta");

      transformer.transform(new StreamSource(new File(sourceID)),
                            new StreamResult(new File(outputID)));
    }
    catch (TransformerConfigurationException tce)
    {
      System.err.println("Exception: " + tce);
    }
    catch (TransformerException te)
    {
      System.err.println("Exception: " + te);
    }
  }

  public static void main(String argv[])
    throws java.io.IOException,
           org.xml.sax.SAXException
  {
    GlobalParameters gp = new GlobalParameters();
    gp.parseAndProcess("xyz.xml", "params.xsl", "output.text");
  }
}

Notice that we used the setParameter method to set global parameters for the Transformer object before we invoke the transform method. This transformation generates the following results in output.text:

Global parameters example

The value of startX is: 50
The value of baseColor is: magenta
Previous | Home | Next
4.3. Invoking Templates by Name | Book Index | 4.5. Variables
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Variables (XSLT)

 XSLSearch this book

4.5. Variables

If we use logic to control the flow of our stylesheets, we'll probably want to store temporary results along the way. In other words, we'll need to use variables. XSLT provides the <xsl:variable> element, which allows you to store a value and associate it with a name.

The <xsl:variable> element can be used in three ways. The simplest form of the element creates a new variable whose value is an empty string (""). Here's how it looks:

<xsl:variable name="x"/>

This element creates a new variable named x, whose value is an empty string. (Please hold your applause until the end of the section.)

You can also create a variable by adding a select attribute to the <xsl:variable> element:

<xsl:variable name="favouriteColour" select="'blue'"/>

In this case, we've set the value of the variable to be the string "blue". Notice that we put single quotes around the value. These quotes ensure that the literal value blue is used as the value of the variable. If we had left out the single quotes, this would mean the value of the variable is that of all the <blue> elements in the current context, which definitely isn't what we want here.

WARNING: Some XSLT processors don't require you to put single quotes around a literal value if the literal value begins with a number. This is because the XML specification states that XML element names can't begin with a number. If I say the value should be 35, Xalan, XT, and Saxon all assume that I mean 35 as a literal value, not as an element name. Although this works with many XSLT processors, you're safer to put the single quotes around the numeric values anyway. A further aside: the value here is the string "35", although it can be converted to a number easily.

The third way to use the <xsl:variable> element is to put content inside it. Here's a brief example:

<xsl:variable name="y">
  <xsl:choose>
    <xsl:when test="$x &gt; 7">
      <xsl:text>13</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>15</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

In this more complicated example, the content of the variable y depends on the test attribute of the <xsl:when> element. This is the equivalent of this procedural programming construct:

int y;
if (x > 7)
  y = 13;
else
  y = 15;

4.5.1. Are These Things Really Variables?

Although these XSLT variables are called variables, they're not variables in the traditional sense of procedural programming languages like C++ or Java. Remember that earlier we said one goal behind the design of the stylesheet language is to avoid side effects in execution? Well, one of the most common side effects used in most procedural languages is changing the value of a variable. If we write our stylesheet so that the results depend on the varying values of different variables, the stylesheet engine would be forced to evaluate the templates in a certain order.

XSLT variables are more like variables in the traditional mathematical sense. In mathematics, we can define a function called square(x) that returns the value of a number (represented by x) multiplied by itself. In other words, square(2.5) returns 6.25. In this context, we understand that x can be any number; we also understand that the square function can't change the value of x.

It takes a while to get used to this concept, but you'll get there. Trust me on this.

4.5.2. Variable Scope

An <xsl:variable> element is scoped to the element that contains it. If an <xsl:variable> element is a top-level element (its parent is <xsl:stylesheet>), it is global, and its value is visible everywhere in the stylesheet. You can also use an <xsl:variable> element to override the value of a global variable locally.

Previous | Home | Next
4.4. Parameters | Book Index | 4.6. Using Recursion to Do Most Anything
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Using Recursion to Do Most Anything (XSLT)

 XSLSearch this book

4.6. Using Recursion to Do Most Anything

Writing an XSLT stylesheet is different from programming in other languages. If you didn't believe that before, you probably do now. We'll finish this chapter with a couple of examples that demonstrate how to use recursion to solve the kinds of problems that you're probably used to solving with procedural programming languages.

4.6.1. Implementing a String Replace Function

To demonstrate how to use recursion to solve problems, we'll write a string replace function. This is sometimes useful when you need to escape certain characters or substrings in your output. The stylesheet we'll develop here transforms an XML document into a set of SQL statements that will be executed at a Windows command prompt. We have to do several things:

Put a caret (^) in front of all ampersands (&)
On the Windows NT and Windows 2000 command prompt, the ampersand means that the current command has ended and another is beginning. For example, this command creates a new directory called xslt and changes the current directory to the newly created one:

mkdir xslt & chdir xslt

If we create a SQL statement that contains an ampersand, we'll need to escape the ampersand so it's processed as a literal character, not as an operator. If we insert the value Jones & Son as the value of the company field in a row of the database, we need to change it to Jones ^& Son before we try to run the SQL command.

Put a caret (^)) in front of all vertical bars (|)
The vertical bar is the pipe operator on Windows systems, so we need to escape it if we want it interpreted as literal text instead of an operator.

Replace any single quote (') with two single quotes ('')
This is a requirement of our database system.

4.6.1.1. Procedural design

Three functions we could use in our template are concat(), substring-before(), and substring-after(). To replace an ampersand with a caret and an ampersand, this would do the trick:

<xsl:value-of select="concat(substring-before(., '&amp;'), '^&amp;',
                             substring-after(., '&amp;'))"/>

The obvious problem with this step is that it only replaces the first occurrence of the ampersand. If there are two ampersands, or three, or three hundred, we need to call this method once for each ampersand in the original string. Because of the way variables work, we can't do what we'd do in a procedural language:

private static String strChange(String string, String from, String to)
{
  String before = "", after = "";
  int    index;

  index = string.indexOf(from);
  while (index >= 0)
  {
    before = string.substring(0, index);
    after = string.substring(index + from.length());
    string = before + to + after;

    index = string.indexOf(from, index + to.length());
  }

  return string;
}

4.6.1.2. Recursive design

To implement a string replace function with recursion, we take a modified version of the approach we used here. We build the replaced string in three pieces:

  • Everything up to the first occurrence of the substring we're replacing. If the substring doesn't exist in the main string, then this is the entire string.

  • The replacement substring. If the substring we're replacing doesn't exist in the main string, then this is blank.

  • Everything after the first occurrence of the substring. If the substring doesn't exist in the main string, then this is blank.

The third portion is where we use recursion. If the substring we're replacing occurs in that part of the main string, we call the substring replace function on the last of the string. The key here, as with all recursive functions, is that we have an exit case, a condition in which we don't recurse. If the substring doesn't occur in the last portion of the string, we're done.

Here's the design in pseudocode:

replaceSubstring(originalString, substring, replacementString)
{
  if (contains(originalString, substring))
    firstOfString = substring-before(originalString, substring)
  else
    firstOfString = originalString

  if (contains(originalString, substring))
    middleOfString = replacementString
  else
    middleOfString = ""

  if (contains(originalString, substring))
  {
    if (contains(substring-after(originalString, substring), substring))
     lastOfString = replaceString(substring-after(originalString, substring),
                                  substring, replacementString)
    else
      lastOfString = substring-after(originalString, substring)
  }
  concat(firstOfString, middleOfString, lastOfString)
}

In the recursive approach, the function calls itself whenever there's at least one more occurrence of the substring. Each time the function calls itself, the originalString parameter is a little smaller, until eventually we've processed the complete string. Here's the complete template:

<xsl:template name="replace-substring">
  <xsl:param name="original"/>
  <xsl:param name="substring"/>
  <xsl:param name="replacement" select="''"/>
  <xsl:variable name="first">
    <xsl:choose>
      <xsl:when test="contains($original, $substring)">
        <xsl:value-of select="substring-before($original, $substring)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$original"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="middle">
    <xsl:choose>
      <xsl:when test="contains($original, $substring)">
        <xsl:value-of select="$replacement"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text></xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="last">
    <xsl:choose>
      <xsl:when test="contains($original, $substring)">
        <xsl:choose>
          <xsl:when test="contains(substring-after($original, $substring),
                                   $substring)">
            <xsl:call-template name="replace-substring">
              <xsl:with-param name="original">
                <xsl:value-of select="substring-after($original, $substring)"/>
              </xsl:with-param>
              <xsl:with-param name="substring">
                <xsl:value-of select="$substring"/>
              </xsl:with-param>
              <xsl:with-param name="replacement">
                <xsl:value-of select="$replacement"/>
              </xsl:with-param>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="substring-after($original, $substring)"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text></xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:value-of select="concat($first, $middle, $last)"/>
</xsl:template>

This style of programming takes some getting used to, but whatever you want to do can usually be done. Our example here is a good illustration of the techniques we've discussed in this chapter, including branching statements, variables, invoking templates by name, and passing parameters.

Previous | Home | Next
4.5. Variables | Book Index | 4.7. A Stylesheet That Emulates a for Loop
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Stylesheet That Emulates a for Loop (XSLT)

 XSLSearch this book

4.7. A Stylesheet That Emulates a for Loop

We stressed earlier that the xsl:for-each element is not a for loop; it's merely an iterator across a group of nodes. However, if you simply must implement a for loop, there's a way to do it. (Get ready to use recursion, though.)

4.7.1. Template Design

Our design here is to create a named template that will take some arguments, then act as a for loop processor. If you think about a traditional for loop, it has several properties:

  • One or more initialization statements. These statements are processed before the for loop begins. Typically the initialization statements refer to an index variable that is used to determine whether the loop should continue.

  • An increment statement. This statement specifies how the index variable should be updated after each pass through the loop.

  • A boolean expression. If the expression is true, the loop continues; if it is ever false, the loop exits.

Let's take a sample from the world of Java and C++:

for (int i=0; i<length; i++)

In this scintillating example, the initialization statement is i=0, the index variable (the variable whose value determines whether we're done or not) is i, the boolean expression we use to test whether the loop should continue is i<length, and the increment statement is i++.

For our purposes here, we're going to make several simplifying assumptions. (Feel free, dear reader, to make the example as complicated as you wish.) Here are the shortcuts we'll take:

  • Rather than use an initialization statement, we'll require the caller to set the value of the local variable i when it invokes our for loop processor.

  • Rather than specify an increment statement such as i++, we'll require the caller to set the value of the local variable increment. The default value for this variable is 1; it can be any negative or positive integer, however. The value of this variable will be added to the current value of i after each iteration through our loop.

  • Rather than allow any conceivable boolean expression, we'll require the caller to pass in two parameters; operator and testValue. The allowable values for the operator variable are =, < (coded as &lt;), > (coded as &gt;), <> (coded as &lt;&gt;), <= (coded as &lt;=), and >= (coded as &gt;=). We're doing things this way because there isn't a way to ask the XSLT processor to evaluate a literal (such as i<length) as if it were part of the stylesheet.

4.7.2. Implementation

Let's look at the parameters for our for loop template:

<xsl:param name="i"         select="1"/>
<xsl:param name="increment" select="1"/>
<xsl:param name="operator"  select="="/>
<xsl:param name="testValue" select="1"/>

Our for template uses four parameters: the index variable, the increment, the comparison operator, and the test value. To emulate this C++ statement:

for (int i=1; i<=10; i++)

You'd use this markup:

<xsl:call-template name="for-loop">
  <xsl:with-param name="i"         select="1"/>
  <xsl:with-param name="increment" select="1"/>
  <xsl:with-param name="operator"  select="&lt;="/>
  <xsl:with-param name="testValue" select="10"/>
</xsl:call-template>

To demonstrate our stylesheet, our first version simply prints out the value of our index variable each time through the loop:

Transforming...
Iteration 1: i=1
Iteration 2: i=2
Iteration 3: i=3
Iteration 4: i=4
Iteration 5: i=5
Iteration 6: i=6
Iteration 7: i=7
Iteration 8: i=8
Iteration 9: i=9
Iteration 10: i=10
transform took 260 milliseconds
XSLProcessor: done

Here's the markup you'd use to emulate the Java statement for (int i=10; i>0; i-=2):

<xsl:call-template name="for-loop">
  <xsl:with-param name="i"         select="10"/>
  <xsl:with-param name="increment" select="-2"/>
  <xsl:with-param name="operator"  select="&gt;"/>
  <xsl:with-param name="testValue" select="0"/>
</xsl:call-template>

In this case, the values of i decrease from 10 to 0:

Transforming...
Iteration 1: i=10
Iteration 2: i=8
Iteration 3: i=6
Iteration 4: i=4
Iteration 5: i=2
transform took 110 milliseconds
XSLProcessor: done

4.7.3. The Complete Example

Here's our complete stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template name="for-loop">
    <xsl:param name="i"         select="1"/>
    <xsl:param name="increment" select="1"/>
    <xsl:param name="operator"  select="="/>
    <xsl:param name="testValue" select="1"/>
    <xsl:param name="iteration" select="1"/>

    <xsl:variable name="testPassed">
      <xsl:choose>
        <xsl:when test="starts-with($operator, '!=')">
          <xsl:if test="$i != $testValue">
            <xsl:text>true</xsl:text>
          </xsl:if>
        </xsl:when>
        <xsl:when test="starts-with($operator, '<=')">
          <xsl:if test="$i <= $testValue">
            <xsl:text>true</xsl:text>
          </xsl:if>
        </xsl:when>
        <xsl:when test="starts-with($operator, '>=')">
          <xsl:if test="$i >= $testValue">
            <xsl:text>true</xsl:text>
          </xsl:if>
        </xsl:when>
        <xsl:when test="starts-with($operator, '=')">
          <xsl:if test="$i = $testValue">
            <xsl:text>true</xsl:text>
          </xsl:if>
        </xsl:when>
        <xsl:when test="starts-with($operator, '<')">
          <xsl:if test="$i < $testValue">
            <xsl:text>true</xsl:text>
          </xsl:if>
        </xsl:when>
        <xsl:when test="starts-with($operator, '>')">
          <xsl:if test="$i > $testValue">
            <xsl:text>true</xsl:text>
          </xsl:if>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">
            <xsl:text>Sorry, the for-loop emulator only </xsl:text>
            <xsl:text>handles six operators </xsl:text>
            <xsl:value-of select="$newline"/>
            <xsl:text>(< | > | = | <= | >= | !=). </xsl:text>
            <xsl:text>The value </xsl:text>
            <xsl:value-of select="$operator"/>
            <xsl:text> is not allowed.</xsl:text>
            <xsl:value-of select="$newline"/>
          </xsl:message>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:if test="$testPassed='true'">
      <!-- Put your logic here, whatever it might be. For the purpose      -->
      <!-- of our example, we'll just write some text to the output stream. -->

      <xsl:text>Iteration </xsl:text><xsl:value-of select="$iteration"/>
      <xsl:text>: i=</xsl:text>
      <xsl:value-of select="$i"/><xsl:value-of select="$newline"/>

      <!-- Your logic should end here; don't change the rest of this        -->
      <!-- template!                                                        -->

      <!-- Now for the important part: we increment the index variable and  -->
      <!-- loop. Notice that we're passing the incremented value, not      -->
      <!-- changing the variable itself.                                   -->

      <xsl:call-template name="for-loop">
        <xsl:with-param name="i"         select="$i + $increment"/>
        <xsl:with-param name="increment" select="$increment"/>
        <xsl:with-param name="operator"  select="$operator"/>
        <xsl:with-param name="testValue" select="$testValue"/>
        <xsl:with-param name="iteration" select="$iteration + 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template match="/">
    <xsl:call-template name="for-loop">
      <xsl:with-param name="i"         select="'10'"/>
      <xsl:with-param name="increment" select="'-2'"/>
      <xsl:with-param name="operator"  select="'>'"/>
      <xsl:with-param name="testValue" select="'0'"/>
    </xsl:call-template>
  </xsl:template>

</xsl:stylesheet>

If you want to modify the for loop to do something useful, put your code between these comments:

<!-- Put your logic here, whatever it might be. For the purpose      -->
<!-- of our example, we'll just write some text to the output stream. -->

<xsl:text>Iteration </xsl:text><xsl:value-of select="$iteration"/>
<xsl:text>: i=</xsl:text>
<xsl:value-of select="$i"/><xsl:value-of select="$newline"/>

<!-- Your logic should end here; don't change the rest of this        -->
<!-- template!                                                        -->
Previous | Home | Next
4.6. Using Recursion to Do Most Anything | Book Index | 4.8. A Stylesheet That Generates a Stylesheet That Emulates a for Loop
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


A Stylesheet That Generates a Stylesheet That Emulates a for Loop (XSLT)

 XSLSearch this book

4.8. A Stylesheet That Generates a Stylesheet That Emulates a for Loop

We've emulated a for loop now, but what about a stylesheet that generates another stylesheet that emulates the for loop? As we beat this dead horse one more time, we'll create a stylesheet that generates the iteration for us, along with an XML syntax that automates the process.

4.8.1. XML Input

Here's the XML template we'll use to generate the stylesheet:

<?xml version="1.0"?>
<html xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <head>
    <title>Text generated by our for loop processor</title>
  </head>
  <body>
    <h1>Text generated by our for loop processor</h1>
    <table border="1">
      <tr>
        <th>Iteration #</th>
        <th>Value of <i>i</i></th>
      </tr>
      <for-loop index-variable="0" increment="1"
       operator="<=" test-value="10">
        <tr>
          <td align="center">
            <xsl:value-of select="$iteration"/>
          </td>
          <td align="center">
            <xsl:value-of select="$i"/>
          </td>
        </tr>
      </for-loop>
    </table>
  </body>
</html>

4.8.2. Template Design

The design of our stylesheet-generating stylesheet is as follows:

  1. Output the <xsl:stylesheet> element.

  2. Generate the for-loop template. This will be a named template that we'll invoke while processing the rest of the document.

  3. Generate the root element template. To do this, everything except the <for-loop> element is copied to the output document. The <for-loop> element will be converted into a call to the for-loop template we generated in the previous step.

  4. Close out the <xsl:stylesheet> element.

4.8.3. Complications

There are a couple of complications in producing our stylesheet-generating stylesheet. First, we need to have some way to distinguish among the XSLT elements in the stylesheet being processed and the XSLT elements we're generating. Here's one way to do it:

<xsl:element name="xsl:template"
  namespace="http://www.w3.org/1999/XSL/Transform">
  <xsl:attribute name="name">for-loop</xsl:attribute>
  <xsl:element name="xsl:param"
  namespace="http://www.w3.org/1999/XSL/Transform">
    <xsl:attribute name="name">i</xsl:attribute>
    <xsl:attribute name="select">
      <xsl:value-of select="@index-variable"/>
    </xsl:attribute>
  </xsl:element>
  <xsl:element name="xsl:param" namespace="http://www.w3.org/1999/XSL/Transform">
    <xsl:attribute name="name">increment</xsl:attribute>
    <xsl:attribute name="select">
      <xsl:value-of select="@increment"/>
    </xsl:attribute>
  </xsl:element>
  <xsl:element name="xsl:param" namespace="http://www.w3.org/1999/XSL/Transform">
    <xsl:attribute name="name">operator</xsl:attribute>
    <xsl:attribute name="select">
      <xsl:text>'</xsl:text>
      <xsl:value-of select="@operator"/>
      <xsl:text>'</xsl:text>
    </xsl:attribute>
  </xsl:element>
  <xsl:element name="xsl:param" namespace="http://www.w3.org/1999/XSL/Transform">
    <xsl:attribute name="name">testValue</xsl:attribute>
    <xsl:attribute name="select">
      <xsl:value-of select="@test-value"/>
    </xsl:attribute>
  </xsl:element>
  <xsl:element name="xsl:param" namespace="http://www.w3.org/1999/XSL/Transform">
    <xsl:attribute name="name">iteration</xsl:attribute>
    <xsl:attribute name="select">1</xsl:attribute>
  </xsl:element>
  ...

This lengthy listing generates this simple XML fragment:

<ns1:template name="for-loop">
  <ns1:param name="i" select="0"/>
  <ns1:param name="increment" select="1"/>
  <ns1:param name="operator" select="'<='"/>
  <ns1:param name="testValue" select="10"/>
  <ns1:param name="iteration" select="1"/>
  ...

This approach works, but we're doing an awful lot of work to create some fairly simple elements. For all the XSLT elements we're generating with <xsl:element> elements, we have to declare the namespace for each one. The obvious way of handling this would be to generate a namespace declaration on the <xsl:stylesheet> element:

<xsl:attribute name="xmlns:xsl">
  http://www.w3.org/1999/XSL/Transform
</xsl:attribute>

Unfortunately, the XSLT specification states (in section 7.1.3) that this isn't legal. What we did in our previous example was add the namespace attribute to all XSLT elements we need to generate. (The XSLT processor is not required to use the namespace prefix we specified in the <xsl:element>, by the way.) To help us get around this awkward problem, the XSLT specification provides the <xsl:namespace-alias> element. This provision allows us to define an alias for the XSLT namespace (or any other namespace we want to use); we'll use the normal XSLT namespace for the stylesheet elements we use, and we'll use the alias for the stylesheet elements we generating. Here's how our new stylesheet looks:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xslout="can be anything, doesn't matter">

  <xsl:output method="xml" indent="yes"/>

  <xsl:namespace-alias stylesheet-prefix="xslout" result-prefix="xsl"/>

  <xsl:template match="*|@*|text()|comment()|processing-instruction()">
    <xsl:copy>
      <xsl:apply-templates
        select="*|@*|text()|comment()|processing-instruction()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="for-loop">
    <xslout:call-template name="for-loop">
      <xslout:with-param name="i" select="{@index-variable}"/>
      <xslout:with-param name="increment" select="{@increment}"/>
      <xslout:with-param name="operator">
        <xsl:attribute name="select">
          <xsl:text>'</xsl:text>
          <xsl:value-of select="@operator"/>
          <xsl:text>'</xsl:text>
        </xsl:attribute>
      </xslout:with-param>
      <xslout:with-param name="testValue" select="{@test-value}"/>
    </xslout:call-template>
  </xsl:template>

  <xsl:template match="for-loop" mode="generate-template">
    <xslout:variable name="newline">
<xslout:text>
</xslout:text>
    </xslout:variable>

    <xslout:template name="for-loop">
      <xslout:param name="i" select="@index-variable"/>
      <xslout:param name="increment" select="@increment"/>
      <xslout:param name="operator" select="@operator"/>
      <xslout:param name="testValue" select="@test-value"/>
      <xslout:param name="iteration" select="1"/>

      <xslout:variable name="testPassed">
        <xslout:choose>
          <xslout:when test="starts-with($operator, '!=')">
            <xslout:if test="$i != $testValue">
              <xslout:text>true</xslout:text>
            </xslout:if>
          </xslout:when>
          <xslout:when test="starts-with($operator, '<=')">
            <xslout:if test="$i <= $testValue">
              <xslout:text>true</xslout:text>
            </xslout:if>
          </xslout:when>
          <xslout:when test="starts-with($operator, '>=')">
            <xslout:if test="$i >= $testValue">
              <xslout:text>true</xslout:text>
            </xslout:if>
          </xslout:when>
          <xslout:when test="starts-with($operator, '=')">
            <xslout:if test="$i = $testValue">
              <xslout:text>true</xslout:text>
            </xslout:if>
          </xslout:when>
          <xslout:when test="starts-with($operator, '<')">
            <xslout:if test="$i < $testValue">
              <xslout:text>true</xslout:text>
            </xslout:if>
          </xslout:when>
          <xslout:when test="starts-with($operator, '>')">
            <xslout:if test="$i > $testValue">
              <xslout:text>true</xslout:text>
            </xslout:if>
          </xslout:when>
          <xslout:otherwise>
            <xslout:message terminate="yes">
              <xslout:text>Sorry, the for-loop emulator only </xslout:text>
              <xslout:text>handles six operators </xslout:text>
              <xslout:value-of select="$newline"/>
              <xslout:text>(< | > | = | <= | >= | !=).  </xslout:text>
              <xslout:text>The value </xslout:text>
              <xslout:value-of select="$operator"/>
              <xslout:text> is not allowed.</xslout:text>
              <xslout:value-of select="$newline"/>
            </xslout:message>
          </xslout:otherwise>
        </xslout:choose>
      </xslout:variable>

      <xslout:if test="$testPassed='true'">
        <xslout:comment>From your stylesheet:</xslout:comment>

        <xsl:apply-templates select="*"/>

        <xslout:comment>End of text from your stylesheet</xslout:comment>

        <xslout:call-template name="for-loop">
          <xslout:with-param name="i" select="$i + $increment"/>
          <xslout:with-param name="increment" select="$increment"/>
          <xslout:with-param name="operator" select="$operator"/>
          <xslout:with-param name="testValue" select="$testValue"/>
          <xslout:with-param name="iteration" select="$iteration + 1"/>
        </xslout:call-template>
      </xslout:if>
    </xslout:template>
  </xsl:template>

  <xsl:template match="/">
    <xslout:stylesheet version="1.0">
      <xsl:apply-templates select="//for-loop" mode="generate-template"/>
      <xslout:template match="/">
        <xsl:apply-templates select="*"/>
      </xslout:template>
    </xslout:stylesheet>
  </xsl:template>

</xsl:stylesheet>

Throughout our stylesheet, we used the usual xsl namespace for the stylesheet elements we use, and the xslout namespace for the stylesheet elements we generate. Notice that though we define the xslout namespace on the <xsl:namespace-alias> element, we still have to declare it on the <xsl:stylesheet> element. Also note that the value we define for the xslout namespace doesn't matter; the value referred to by the <xsl:namespace-alias> is used instead.

Here is the stylesheet generated by our stylesheet-generating stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xslout:stylesheet xmlns:xslout="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
<xslout:variable name="newline">
<xslout:text/>
</xslout:variable>
<xslout:template name="for-loop">
<xslout:param select="@index-variable" name="i"/>
<xslout:param select="@increment" name="increment"/>
<xslout:param select="@operator" name="operator"/>
<xslout:param select="@test-value" name="testValue"/>
<xslout:param select="1" name="iteration"/>
<xslout:variable name="testPassed">
<xslout:choose>
<xslout:when test="starts-with($operator, '!=')">
<xslout:if test="$i != $testValue">
<xslout:text>true</xslout:text>
</xslout:if>
</xslout:when>
<xslout:when test="starts-with($operator, '<=')">
<xslout:if test="$i <= $testValue">
<xslout:text>true</xslout:text>
</xslout:if>
</xslout:when>
<xslout:when test="starts-with($operator, '>=')">
<xslout:if test="$i >= $testValue">
<xslout:text>true</xslout:text>
</xslout:if>
</xslout:when>
<xslout:when test="starts-with($operator, '=')">
<xslout:if test="$i = $testValue">
<xslout:text>true</xslout:text>
</xslout:if>
</xslout:when>
<xslout:when test="starts-with($operator, '<')">
<xslout:if test="$i < $testValue">
<xslout:text>true</xslout:text>
</xslout:if>
</xslout:when>
<xslout:when test="starts-with($operator, '>')">
<xslout:if test="$i > $testValue">
<xslout:text>true</xslout:text>
</xslout:if>
</xslout:when>
<xslout:otherwise>
<xslout:message terminate="yes">
<xslout:text>Sorry, the for-loop emulator only </xslout:text>
<xslout:text>handles six operators </xslout:text>
<xslout:value-of select="$newline"/>
<xslout:text>(< | > | = | <= | >= | !=).  </xslout:text>
<xslout:text>The value </xslout:text>
<xslout:value-of select="$operator"/>
<xslout:text> is not allowed.</xslout:text>
<xslout:value-of select="$newline"/>
</xslout:message>
</xslout:otherwise>
</xslout:choose>
</xslout:variable>
<xslout:if test="$testPassed='true'">
<xslout:comment>From your stylesheet:</xslout:comment>
<tr xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          <td align="center">
            <xsl:value-of select="$iteration"/>
          </td>
          <td align="center">
            <xsl:value-of select="$i"/>
          </td>
        </tr>
<xslout:comment>End of text from your stylesheet</xslout:comment>
<xslout:call-template name="for-loop">
<xslout:with-param select="$i + $increment" name="i"/>
<xslout:with-param select="$increment" name="increment"/>
<xslout:with-param select="$operator" name="operator"/>
<xslout:with-param select="$testValue" name="testValue"/>
<xslout:with-param select="$iteration + 1" name="iteration"/>
</xslout:call-template>
</xslout:if>
</xslout:template>
<xslout:template match="/">
<html xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <head>
    <title>Text generated by our for loop processor</title>
  </head>
  <body>
    <h1>Text generated by our for loop processor</h1>
    <table border="1">
      <tr>
        <th>Iteration #</th>
        <th>Value of <i>i</i>
</th>
      </tr>
      <xslout:call-template name="for-loop">
<xslout:with-param select="0" name="i"/>
<xslout:with-param select="1" name="increment"/>
<xslout:with-param name="operator" select="'<='"/>
<xslout:with-param select="10" name="testValue"/>
</xslout:call-template>
    </table>
  </body>
</html>
</xslout:template>
</xslout:stylesheet>

When we execute the generated stylesheet, it produces the following HTML document. (When rendered in a browser, the document generated by the stylesheet generated by our other stylesheet looks like Figure 4-1.)

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Text generated by our for loop processor</title>
</head>
<body>
<h1>Text generated by our for loop processor</h1>
<table border="1">
<tr>
<th>Iteration #</th><th>Value of <i>i</i></th>
</tr>
<tr>
<td align="center">1</td><td align="center">0</td>
</tr>
<tr>
<td align="center">2</td><td align="center">1</td>
</tr>
<tr>
<td align="center">3</td><td align="center">2</td>
</tr>
<tr>
<td align="center">4</td><td align="center">3</td>
</tr>
<tr>
<td align="center">5</td><td align="center">4</td>
</tr>
<tr>
<td align="center">6</td><td align="center">5</td>
</tr>
<tr>
<td align="center">7</td><td align="center">6</td>
</tr>
<tr>
<td align="center">8</td><td align="center">7</td>
</tr>
<tr>
<td align="center">9</td><td align="center">8</td>
</tr>
<tr>
<td align="center">10</td><td align="center">9</td>
</tr>
<tr>
<td align="center">11</td><td align="center">10</td>
</tr>
</table>
</body>
</html>

Figure 4-1. HTML document generated by our generated stylesheet

Notice in the generated document that the HTML <title> and <h1> values come directly from the XML template, as do the table headings and the definition of the HTML table itself.

Previous | Home | Next
4.7. A Stylesheet That Emulates a for Loop | Book Index | 4.9. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

4.9. Summary

We've covered a lot of ground in this chapter, haven't we? We've gone over all of the basic elements you need to add logic and branching to your stylesheets. We discussed some of the similarities between XSLT and other programming languages you might know; more importantly, we discussed how XSLT is different from most of the code you've probably written. In particular, the use of recursion and the principles of variables that don't change take some getting used to. Despite the learning curve, most of the common tasks you'll need to do will be similar to the exercises we've gone through in this chapter. Now that we've covered these basic elements, we'll talk about links and references, discovering ways to build links between different parts of an XML document.

Previous | Home | Next
4.8. A Stylesheet That Generates a Stylesheet That Emulates a for Loop | Book Index | 5. Creating Links and Cross-References
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Creating Links and Cross-References (XSLT)

 XSLTSearch this book

Chapter 5. Creating Links and Cross-References

Contents:

Generating Links with the id() Function
Generating Links with the key() Function
Generating Links in Unstructured Documents
Summary

If you're creating a web site, publishing a book, or creating an XML transaction, chances are many pieces of information will refer to other things. This chapter discusses a several ways to link XML elements. It reviews three techniques:

  • Using the id() function

  • Doing more advanced linking with the key() function

  • Generating links in unstructured documents

5.1. Generating Links with the id() Function

Our first attempt at linking will be with the XPath id() function.

5.1.1. The ID, IDREF, and IDREFs Datatypes

Three of the basic datatypes supported by XML Document Type Definitions (DTDs) are ID, IDREF, and IDREFS. Here's a simple DTD that illustrates these datatypes:

<!--glossary.dtd-->
<!--The containing tag for the entire glossary-->
<!ELEMENT glossary  (glentry+) >

<!--A glossary entry-->
<!ELEMENT glentry  (term,defn+) >

<!--The word being defined-->
<!ELEMENT term  (#PCDATA) >

<!--The id is used for cross-referencing, and the
    xreftext is the text used by cross-references.-->
<!ATTLIST term
               id  ID    #REQUIRED
               xreftext  CDATA    #IMPLIED  >

<!--The definition of the term-->
<!ELEMENT defn  (#PCDATA | xref | seealso)* >

<!--A cross-reference to another term-->
<!ELEMENT xref   EMPTY  >

<!--refid is the ID of the referenced term-->
<!ATTLIST xref
               refid  IDREF    #REQUIRED >

<!--seealso refers to one or more other definitions-->
<!ELEMENT seealso EMPTY>
<!ATTLIST seealso
                  refids   IDREFS  #REQUIRED >

In this DTD, each <term> element is required to have an id attribute, and each <xref> element must have an refid attribute. The ID and IDREF datatypes work according to two rules:

  • Each value of the id attribute must be unique.

  • Each value of the refid attribute must match a value of an id attribute elsewhere in the document.

To round out our example, the <seealso> element contains an attribute of type IDREFS. This datatype contains one or more values, each of which must match a value of an ID elsewhere in the document. Multiple values, if present, are separated by whitespace.

There are some complications of ID and related datatypes, but we'll discuss them later. For now, we'll focus on how the id() function works.

5.1.2. An XML Document in Need of Links

To illustrate the value of linking, we'll use a small glossary written in XML. The glossary contains some <glentry> elements, each of which contains a single <term> and one or more <defn> elements. In addition, a definition is allowed to contain a cross-reference (<xref>) to another <term>. Here's a short sample document:

<?xml version="1.0" ?>
<!DOCTYPE glossary SYSTEM "glossary.dtd">
<glossary>
  <glentry>
    <term id="applet">applet</term>
    <defn>
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.
      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the file system of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <xref refid="servlet"/>.
      <seealso refids="wildcard-char DMZlong pattern-matching"/>
    </defn>
  </glentry>

  <glentry>
    <term id="DMZlong" xreftext="demilitarized zone">demilitarized
      zone (DMZ)</term>
    <defn>
      In network security, a network that is isolated from, and
      serves as a neutral zone between, a trusted network (for example,
      a private intranet) and an untrusted network (for example, the
      Internet). One or more secure gateways usually control access
      to the DMZ from the trusted or the untrusted network.
    </defn>
  </glentry>

  <glentry>
    <term id="DMZ">DMZ</term>
    <defn>
      See <xref refid="DMZlong"/>.
    </defn>
  </glentry>

  <glentry>
    <term id="pattern-matching">pattern-matching character</term>
    <defn>
      A special character such as an asterisk (*) or a question mark
      (?) that can be used to represent zero or more characters.
      Any character or set of characters can replace a pattern-matching
      character.
    </defn>
  </glentry>

  <glentry>
    <term id="servlet">servlet</term>
    <defn>
      An application program, written in the Java programming language,
      that is executed on a web server. A reference to a servlet
      appears in the markup for a web page, in the same way that a
      reference to a graphics file appears. The web server executes
      the servlet and sends the results of the execution (if there are
      any) to the web browser. Contrast with <xref refid="applet" />.
    </defn>
  </glentry>

  <glentry>
    <term id="wildcard-char">wildcard character</term>
    <defn>
      See <xref refid="pattern-matching"/>.
    </defn>
  </glentry>
</glossary>

In this XML listing, each <term> element has an id attribute that identifies it uniquely. Many <xref> elements also refer to other terms in the listing. Notice that each time we refer to another term, we don't use the actual text of the referenced term. When we write our stylesheet, we'll use the XPath id function to retrieve the text of the referenced term; if the name of a term changes (as buzzwords go in and out of fashion, some marketing genius might want to rename the "pattern-matching character," for example), we can rerun our stylesheet and be confident that all references to the new term contain the correct text.

Finally, some <term> elements have an xreftext element because some of the actual terms are longer than we'd like to use in a cross-reference. When we have an <xref> to the term ASCII (American Standard Code for Information Interchange), it would get pretty tedious if the entire text of the term appeared throughout our document. For this term, we'll use the xreftext attribute's value, ensuring that the cross-reference contains the less-intimidating text ASCII.

5.1.3. A Stylesheet That Uses the id() Function

Let's look at our desired output. What we want is an HTML document, such as that shown in Figure 5-1, that displays the various definitions in an easy-to-read format, with the cross-references formatted as hyperlinks.

In the HTML document, we'll need to address several things in our stylesheet:

  • The <title> and the <h1> contain the first and last terms in the glossary. We can use XPath expressions to generate that information.

  • The <xref> elements have been replaced with the xreftext attribute of the referenced <term> element, if there is one. If that attribute doesn't exist, <xref> is replaced by the text of the <term> element. We'll use the id() function to find the referenced <term>, and we'll use XSLT's control elements to check if the xreftext attribute exists.

  • The hyperlinks generated from the <xref> elements refer to a named anchor point elsewhere in the HTML document. If <xref> elements refer to a given <term>, we have to create a named anchor (<a name="...">) at the location of the referenced <term>. To simplify things, we'll generate a named anchor for each term automatically, using the id attribute (required to be unique by our DTD) as the name of the anchor.

  • We need to process any <seealso> elements, as well. These elements are handled similarly to the <xref> elements, the main difference being that the refids attribute of the <seealso> element can refer to more than one glossary entry.

Figure 5-1. HTML document with generated cross-references

Here's the template that takes care of our first task, generating the HTML <title> and the <h1>:

<xsl:template match="glossary">
  <html>
    <head>
      <title>
        <xsl:text>Glossary Listing: </xsl:text>
        <xsl:value-of select="glentry[1]/term"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="glentry[last()]/term"/>
      </title>
    </head>
    <body>
      <h1>
        <xsl:text>Glossary Listing: </xsl:text>
        <xsl:value-of select="glentry[1]/term"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="glentry[last()]/term"/>
      </h1>
      <xsl:apply-templates select="glentry"/>
    </body>
  </html>
</xsl:template>

We generate the <title> and <h1> using the XPath expressions glentry[1]/term for the first <term> in the document, and using glentry[last()]/term for the last term.

Our next step is to process all the <glentry> elements. We'll generate an HTML paragraph for each one, and then we'll generate a named anchor point, using the id attribute as the name of the anchor. Here's the template:

<xsl:template match="glentry">
  <p>
    <b>
      <a name="{@id}"/>
      <xsl:value-of select="term"/>
      <xsl:text>: </xsl:text>
    </b>
    <xsl:apply-templates select="defn"/>
  </p>
</xsl:template>

In this template, we're using an attribute value template to generate the name attribute of the HTML <a> element. The XPath expression @id retrieves the id attribute of the <glentry> element we're currently processing. We use this attribute to generate a named anchor. We then write the term itself in bold and apply the template for the <defn> element. In our output document, each glossary entry contains a paragraph with the highlighted term and its definition.

The name attribute of this HTML <a> element is generated with an attribute value template. See Section 3.3, "Attribute Value Templates" for more information.

Our next step is to process the cross-reference. Here's the template for the <xref> element:

<xsl:template match="xref">
  <a href="#{@refid}">
    <xsl:choose>
      <xsl:when test="id(@refid)/@xreftext">
        <xsl:value-of select="id(@refid)/@xreftext"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="id(@refid)"/>
      </xsl:otherwise>
    </xsl:choose>
  </a>
</xsl:template>

We create the <a> element in two steps:

  • Create the href attribute. It must refer to the correctly named anchor in the HTML document.

  • Create the text of the link. This text is the word or phrase that appears in the browser; clicking on the link should take the user to the referenced term.

For the first step, we know that the href attribute must contain a hash mark (#) followed by the name of the anchor point. Because we generated all the named anchors from the id attributes of the various <glentry> elements, we know the name of the anchor point is the same as the id.

Now all that's left is for us to retrieve the text. This retrieval is the most complicated part of the process (relatively speaking, anyway). Remember that we want to use the xreftext attribute of the <term> element, if there is one, and use the text of the <term> element, otherwise. To implement an if-then-else statement, we use the <xsl:choose> element. In the previous sample, we used a test expression of id(@refid)/@xreftext to see if the xreftext attribute exists. (Remember, an empty node-set is considered false. If the attribute doesn't exist, the node-set will be empty and the <xsl:otherwise> element will be evaluated.) If the test is true, we use id(@refid)/@xreftext to retrieve the cross-reference text. The first part of the XPath expression (id(@refid)) returns the node that has an ID that matches the value @refid; the second part (@xreftext) retrieves the xreftext attribute of that node. We insert the text of the xreftext attribute inside the <a> element.

Finally, we handle any <seealso> elements. The difference here is that the refids attribute can reference any number of glossary terms, so we'll use the id() function differently. Here's the template for <seealso>:

<xsl:template match="seealso">
  <b>
    <xsl:text>See also: </xsl:text>
  </b>
  <xsl:for-each select="id(@refids)">
    <a href="#{@id}">
      <xsl:choose>
        <xsl:when test="@xreftext">
          <xsl:value-of select="@xreftext"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="."/>
        </xsl:otherwise>
      </xsl:choose>
    </a>
    <xsl:if test="not(position()=last())">
      <xsl:text>, </xsl:text>
    </xsl:if>
  </xsl:for-each>
  <xsl:text>. </xsl:text>
</xsl:template>

There are a couple of important differences here. First, we call the id() function in an <xsl:for-each> element. Calling the id() function with an attribute of type IDREFS returns a node-set; each node in the node-set is the match for one of the IDs in the attribute.

The second difference is that referencing the correctly named anchor is more difficult. When we processed the <xref> element, we knew that the correct anchor name was the value of the refid attribute. When processing <seealso>, the refids attribute doesn't do us any good because it may contain any number of IDs. All is not lost, however. What we did previously was use the id attribute of each node returned by the id() function -- a minor inconvenience, but another difference in processing an attribute of type IDREFS instead of IDREF.

The final difference is that we want to add commas after all items except the last. The <xsl:if> element shown previously does just this. If the position() of the current item is the last, we don't output the comma and space (defined here with the <xsl:text> element). We formatted all references here as a sentence; as an exercise, feel free to process the items in a more sophisticated way. For example, you could generate an HTML list from the IDREFS, or maybe format things differently if the refids attribute only contains a single ID.

We've done several useful things with the id() function. We've been able to use attributes of type ID to discover the links between related pieces of information, and we've converted the XML into HTML links, renderable in an ordinary household browser. If this is the only kind of linking and referencing you need to do, that's great. Unfortunately, there are times when we need to do more, and on those occasions, the id() function doesn't quite cut it. We'll mention the limitations of the id() function briefly, then we'll discuss XSLT functions that let us overcome them.

5.1.4. Limitations of IDs

To this point, we've been able to generate cross-references easily. There are some limitations of the ID datatype and the id() function, though:

  • If you want to use the ID datatype, you have to declare the attributes that use that datatype in your DTD or schema. Unfortunately, if your DTD is defined externally to your XML document, the XML parser isn't required to read it. If the DTD isn't read, then the parser has no idea that a given attribute is of type ID.

  • You must define the ID and IDREF relationship in the XML document. It would be nice to have the XML document define the data only, with the relationships between parts of the document defined externally (say, in a stylesheet). That way, if you needed to define a new relationship between parts of the document, you could do it by creating a new stylesheet, and you wouldn't have to modify your XML document. Requiring the XML document structure to change every time you need to define a new relationship between parts of the document will become unwieldy quickly.

  • An element can have at most one attribute of type ID. If you'd like to refer to the same element in more than one way, you can't use the id() function.

  • Any given ID value can be found on at most one element. If you'd like to refer to more than one element with a single value, you can't use the id() function for that, either.

  • Only one set of IDs exists for the entire document. In other words, if you declare the attributes customer_number, part_number, and order_number to be of type ID, the value of a customer_number must be unique across all the attributes of type ID. It is illegal in this case for a customer_number to be the same as a part_number, even though those attributes might belong to different elements.

  • An ID can only be an attribute of an XML element. The only way you can use the id() function to refer to another element is through its attribute of type ID. If you want to find another element based on an attribute that isn't an ID, based on the element's content, based on the element's children, etc., the id() function is of no use whatsoever.

  • The value of an ID must be an XML name. In other words, it can't contain spaces, it can't start with a number, and it's subject to the other restrictions of XML names. (Section 2.3 of the XML Recommendation defines these restrictions; see http://www.w3.org/TR/REC-xml if you'd like more information.)

To get around all of these limitations, XSLT defines the key() function. We'll discuss that function in the next section.

Previous | Home | Next
4.9. Summary | Book Index | 5.2. Generating Links with the key() Function
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Generating Links with the key() Function (XSLT)

 XSLSearch this book

5.2. Generating Links with the key() Function

Now that we've covered the id() function in great detail, we'll move on to XSLT's key() function. Each key() function effectively creates an index of the document. You can then use that index to find all elements that have a particular property. Conceptually, key() works like a database index. If you have a database of (U.S. postal) addresses, you might want to index that database by the people's last names, by the states in which they live, by their Zip Codes, etc. Each index takes a certain amount of time to build, but it saves processing time later. If you want to find all the people who live in the state of Idaho, you can use the index to find all those people directly; you don't have to search the entire database.

We'll discuss the details of how the key() function works, then we'll compare it to the id() function.

5.2.1. Defining a key()

You define a key() function with the <xsl:key> element:

<xsl:key name="language-index" match="defn" use="@language"/>

The key has three elements:

  • A name, used to refer to this particular key. When you want to find parts of your XML document, use the name to indicate the key you want to use.

  • A match attribute containing an XPath expression. This specifies what part of the document you want to index. The previous example created an index on all of the <defn> elements. When we call the key() function, it will return a <defn> element. Note: according to Section 12.2 of the XSLT specification, the value of the match attribute can't contain a variable.

  • A use attribute containing another XPath expression. This attribute is interpreted in the context of the match attribute. In other words, the previous <xsl:key> element created an index of all the <defn> elements, and used the language attribute to retrieve them. Note: according to Section 12.2 of the XSLT specification, the value of the use attribute can't contain a variable.

5.2.2. A Slightly More Complicated XML Document in Need of Links

To illustrate the full power of the key() function, we'll modify our original glossary slightly. Here's an excerpt:

<glentry>
  <term id="DMZlong" xreftext="demilitarized zone">demilitarized
    zone (DMZ)</term>
  <defn topic="security" language="en">
    In network security, a network that is isolated from, and
    serves as a neutral zone between, a trusted network (for example,
    a private intranet) and an untrusted network (for example, the
    Internet). One or more secure gateways usually control access
    to the DMZ from the trusted or the untrusted network.
  </defn>
  <defn topic="security" language="it">
    [Pretend this is an Italian definition of DMZ.]
  </defn>
  <defn topic="security" language="es">
    [Pretend this is a Spanish definition of DMZ.]
  </defn>
  <defn topic="security" language="jp">
    [Pretend this is a Japanese definition of DMZ.]
  </defn>
  <defn topic="security" language="de">
    [Pretend this is a German definition of DMZ.]
  </defn>
</glentry>

<glentry>
  <term id="DMZ" acronym="yes">DMZ</term>
  <defn topic="security" language="en">
    See <xref refid="DMZlong"/>.
  </defn>
</glentry>

In our modified document, we've added two new attributes to <defn>: topic and language. We also added the acronym attribute to the <term> element. We've modified our DTD to add these attributes and enumerate their valid values:

<!--The word being defined-->
<!ELEMENT term  (#PCDATA) >

<!--The id is used for cross-referencing, and the
    xreftext is the text used by cross-references.-->
<!ATTLIST term
               id        ID       #REQUIRED
               xreftext  CDATA    #IMPLIED
               acronym   (yes|no) "no">

<!--The definition of the term-->
<!ELEMENT defn  (#PCDATA | xref | seealso)* >

<!--The topic defines the subject of the definition, the
    language code defines the language of this definition,
    and the acronym is yes or no (default is no).-->
<!ATTLIST defn
                topic    (Java|general|security) "general"
                language (en|de|es|it|jp)        "en">

The topic attribute defines the computing topic to which this definition applies, and the language attribute defines the language in which this definition is written. The acronym attribute defines whether or not this term is an acronym.

Now that we've created a more flexible XML document, we can use the key() function to do several useful things:

  • We can find all <defn> elements that are written in a particular language (as long as it's one of the five languages we defined).

  • We can find all <defn> elements that apply to a particular topic.

  • We can find all <term> elements that are acronyms.

Thinking back to our earlier discussion, these are all things we can't do with the id() function. If the language, topic, and acronym attributes were defined to be of type ID, only one definition could be written in English, only one definition could apply to the security topic, and only one term could be an acronym. Clearly, that's an unacceptable limitation on our document.

5.2.3. Stylesheets That Use the key() Function

We've mentioned some useful things we can do with the key() function, so now we'll build some stylesheets that use it. Our first stylesheet will list all definitions written in a particular language. We'll go through the various parts of the stylesheet, explaining all the things we had to add to make everything work. The first thing we'll do, of course, is define the key() function:

<xsl:key name="language-index" match="defn" use="@language"/>

Notice that the match attribute we used was the simple element name defn. This tells the XSLT processor to match all <defn> elements at all levels of the document. Because of the structure of our document, we could have written match="/glossary/glentry/defn", as well. Although this XPath expression is more restrictive, it matches the same elements because all <defn> elements must appear inside <glentry> elements, which in turn appear inside the <glossary> element.

Next, we set up our stylesheet to determine what value of the language attribute we're searching for. We'll do this with a global <xsl:param> element:

<xsl:param name="targetLanguage"/>

Recall from our earlier discussion of the <xsl:param> element that any top-level <xsl:param> is a global parameter to the stylesheet and may be set or initialized from outside the stylesheet. The way to do this varies from one XSLT processor to another. Here's how it's done with Xalan. (The command should be on one line.)

java org.apache.xalan.xslt.Process -in moreterms.xml -xsl crossref2.xsl
-param targetLanguage it

If you use Michael Kay's Saxon processor, the syntax looks like this:

java com.icl.saxon.StyleSheet moreterms.xml crossref2.xsl targetLanguage=it

Now that we've defined our key() function and defined a parameter to specify which language we're looking for, we need to generate our output. Here's the modified template that generates the HTML <title> and <h1> tags:

<xsl:template match="glossary">
  <html>
    <head>
      <title>
        <xsl:text>Glossary Listing: </xsl:text>
        <xsl:value-of select="key('language-index',
          $targetLanguage)[1]/preceding-sibling::term"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="key('language-index',
          $targetLanguage)[last()]/preceding-sibling::term"/>
      </title>
    </head>
    <body>
      <h1>
        <xsl:text>Glossary Listing: </xsl:text>
        <xsl:value-of select="key('language-index',
          $targetLanguage)[1]/ancestor::glentry/term"/>
        <xsl:text> - </xsl:text>
        <xsl:value-of select="key('language-index',
          $targetLanguage)[last()]/ancestor::glentry/term"/>
      </h1>
      <xsl:for-each select="key('language-index', $targetLanguage)">
        <xsl:apply-templates select="ancestor::glentry"/>
      </xsl:for-each>
    </body>
  </html>
</xsl:template>

There are a couple of significant changes here. When we were using the id() function, it was easy to find the first and last terms in the document. Because we're now trying to list only the definitions that are written in a particular language, that won't work. Reading the XPath expressions in the <xsl:value-of> elements from left to right, we find the first and last <defn> elements returned by the key() function, then use the preceding-sibling axis to reference the <term> element that preceded it. We could also have written our XPath expressions using the ancestor axis:

<h1>
  <xsl:text>Glossary Listing: </xsl:text>
  <xsl:value-of select="key('language-index',
    $targetLanguage)[1]/ancestor::glentry/term"/>
  <xsl:text> - </xsl:text>
  <xsl:value-of select="key('language-index',
    $targetLanguage)[last()]/ancestor::glentry/term"/>
</h1>

Now that we've successfully generated the HTML <title> and <h1> elements, we need to process the actual definitions for the chosen language. To do this, we'll use the targetLanguage parameter. Here's how the rest of the template looks:

<xsl:for-each select="key('language-index', $targetLanguage)">
  <xsl:apply-templates select="ancestor::glentry"/>
</xsl:for-each>

In this code, we've selected all the values from the language-index key that match the targetLanguage parameter. For each one, we use the ancestor axis to select the <glentry> element. We've already written the templates that process these elements correctly, so we can just reuse them.

The final change we make is to select only those <defn> elements whose language attributes match the targetLanguage parameter. We do this with a simple XPath expression:

<xsl:apply-templates select="defn[@language=$targetLanguage]"/>

Here's the complete stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*"/>

  <xsl:key name="language-index" match="defn" use="@language"/>

  <xsl:param name="targetLanguage"/>

  <xsl:template match="/">
    <xsl:apply-templates select="glossary"/>
  </xsl:template>

  <xsl:template match="glossary">
    <html>
      <head>
        <title>
          <xsl:text>Glossary Listing: </xsl:text>
          <xsl:value-of select="key('language-index',
            $targetLanguage)[1]/preceding-sibling::term"/>
          <xsl:text> - </xsl:text>
          <xsl:value-of select="key('language-index',
            $targetLanguage)[last()]/preceding-sibling::term"/>
        </title>
      </head>
      <body>
        <h1>
          <xsl:text>Glossary Listing: </xsl:text>
          <xsl:value-of select="key('language-index',
            $targetLanguage)[1]/ancestor::glentry/term"/>
          <xsl:text> - </xsl:text>
          <xsl:value-of select="key('language-index',
            $targetLanguage)[last()]/ancestor::glentry/term"/>
        </h1>
        <xsl:for-each select="key('language-index', $targetLanguage)">
          <xsl:apply-templates select="ancestor::glentry"/>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="glentry">
    <p>
      <b>
        <a name="{term/@id}"/>
        <xsl:value-of select="term"/>
        <xsl:text>: </xsl:text>
      </b>
      <xsl:apply-templates select="defn[@language=$targetLanguage]"/>
    </p>
  </xsl:template>

  <xsl:template match="defn">
    <xsl:apply-templates
     select="*|comment()|processing-instruction()|text()"/>
  </xsl:template>

  <xsl:template match="xref">
    <a href="#{@refid}">
      <xsl:choose>
        <xsl:when test="id(@refid)/@xreftext">
          <xsl:value-of select="id(@refid)/@xreftext"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="id(@refid)"/>
        </xsl:otherwise>

      </xsl:choose>
    </a>
  </xsl:template>

  <xsl:template match="seealso">
    <b>
      <xsl:text>See also: </xsl:text>
    </b>
    <xsl:for-each select="id(@refids)">
      <a href="#{@id}">
        <xsl:choose>
          <xsl:when test="@xreftext">
            <xsl:value-of select="@xreftext"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="."/>
          </xsl:otherwise>
        </xsl:choose>
      </a>
      <xsl:if test="not(position()=last())">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
    <xsl:text>.  </xsl:text>
  </xsl:template>

</xsl:stylesheet>

Given our sample document and a targetLanguage of en, we get these results:

<html>
  <head>
    <title>Glossary Listing: applet - wildcard character</title>
  </head>
  <body>
    <h1>Glossary Listing: applet - wildcard character</h1>
    <p>
      <b><a name="applet"></a>applet: </b>
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.
      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the file system of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <a href="#servlet">servlet</a>.
      ...

Changing the targetLanguage to it, the results are now different:

<html>
  <head>
    <title>Glossary Listing: applet - servlet</title>
  </head>
  <body>
    <h1>Glossary Listing: applet - servlet</h1>
    <p>
      <b><a name="applet"></a>applet: </b>
      [Pretend this is an Italian definition of applet.]
    </p>
    <p>
      <b><a name="DMZlong"></a>demilitarized
      zone (DMZ): </b>
      [Pretend this is an Italian definition of DMZ.]
    </p>
    <p>
      <b><a name="servlet"></a>servlet: </b>
      [Pretend this is an Italian definition of servlet.]
    </p>
  </body>
</html>

With this stylesheet, we have a way to create a useful subset of our glossary. Notice that we're still using our original technique of ID, IDREF, and IDREFS to process the <xref> and <seealso> elements. If you want, you could redefine the processing to use the key() function instead. Here's how you'd define a key() function to mimic our earlier use of ID and IDREF:

<xsl:template match="xref">
  <a href="#{@refid}">
    <xsl:choose>
      <xsl:when test="key('term-ids', @refid)[1]/@xreftext">
        <xsl:value-of select="key('term-ids', @refid)[1]/@xreftext"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="key('term-ids', @refid)[1]"/>
      </xsl:otherwise>
    </xsl:choose>
  </a>
</xsl:template>

As an exercise for the reader, you can modify this stylesheet so that it lists only definitions that apply to a particular topic, or only terms that are acronyms.

5.2.3.1. The key() function and the IDREFS datatype

For all its flexibility, the key() function doesn't support anything like the IDREFS datatype. We can try to use the key() function the same way we used id():

<xsl:template match="seealso">
  <b>
    <xsl:text>See also: </xsl:text>
  </b>
  <xsl:for-each select="key('term-ids', @refids)">
    <a>
  ...

But the <xsl:for-each> doesn't have anything to work with. That's because the key value we're looking for is "wildcard-char DMZlong pattern-matching". When we were dealing with the id() function, this string was broken into three tokens because anything with a datatype of ID can't contain a space. With the key() function, we can search on anything, including the contents of an element. (See Section 5.3, "Generating Links in Unstructured Documents" for an example of this.) For this reason, our call to the key() function asking for all the <term> elements with an id attribute equal to "wildcard-char DMZlong pattern-matching" returns nothing. Any attribute with a datatype of ID can't contain spaces, so we get no results.

There are several ways to deal with this problem; we'll go through our choices next.

5.2.3.2. Solution #1: Replace the IDREFS datatype

If you consider this a problem and refuse to use the id() function, there are several approaches you can take. The most drastic (but probably the simplest to implement) is to not use the IDREFS datatype at all. You could change the <seealso> element so that it contains a list of references to other elements:

<seealso>
  <item refid="wildcard-character"/>
  <item refid="DMZlong"/>
  <item refid="pattern-matching"/>
</seealso>

This approach has the advantage that we can use the value of all the refid attributes of all <item> elements with the key() function. That means we can search on anything, not just values of attributes. The disadvantage, of course, is that we had to change the structure of our XML document to make this approach work. If you have control of the structure of your XML document, that's possible; it's entirely likely, of course, that you can't change the XML document at all. A variation on this approach would be to use a stylesheet to transform the IDREFS datatype into the previous structure.

5.2.3.3. Solution #2: Use the XPath contains() function

A second approach is to leave the structure of the XML document unchanged, then use the XPath contains() function to find all <term> elements whose id attributes are contained in the value of the refids attribute of the <seealso> element. Here's how that would work:

<xsl:template match="seealso">
  <b>
    <xsl:text>See also: </xsl:text>
  </b>
  <xsl:variable name="id_list" select="@refids"/>
  <xsl:for-each select="//term">
    <xsl:if test="contains($id_list, @id)">
      <a href="#{@id}">
        <xsl:choose>
          <xsl:when test="@xreftext">
            <xsl:value-of select="@xreftext"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="."/>
          </xsl:otherwise>
        </xsl:choose>
      </a>
      <xsl:if test="not(position()=last())">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:if>
  </xsl:for-each>
  <xsl:text>.  </xsl:text>
</xsl:template>

We've done a couple of things here: First, we've saved the value of the refids attribute of the <seealso> element in the variable id_list. That's because we can't access it within the <for-each> element. We can find a given <seealso> element from within a given <term> element, but it's too difficult to find that element generically from every <term> element. The simplest way to find the element is to save the value in a variable.

Second, we look at all of the <term> elements in the document. For each one, if our variable (containing the refids attribute of the <seealso> element) contains the value of the current <term> element's id attribute, then we process that <term> element.

Here are the results our stylesheet generates:

<html>
  <head>
    <title>Glossary Listing: applet - wildcard character</title>
  </head>
  <body>
    <h1>Glossary Listing: applet - wildcard character</h1>
    <p>
      <b><a name="applet"></a>applet: </b>
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.
      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the file system of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <a href="#servlet">servlet</a>.
      <b>See also: </b><a
      href="#DMZlong">demilitarized zone</a>, <a href="#DMZ">
      DMZ</a>, <a href="#pattern-matching">pattern-matching
      character</a>, <a href="#wildcard-char">wildcard
      character</a>.
    </p>
      ...

There are a couple of problems here. The most mundane is that in our stylesheet, we don't know how many <term> elements have id attributes contained in our variable. That means it's difficult to insert commas correctly between the matching <term>s. In the output here, we were lucky that the last match was in fact the last term, so the results here are correct. For any <seealso> element whose refid attribute doesn't contain the id attribute of the last <term> element in the document, this stylesheet won't work.

The more serious problem is that one of the matches is, in fact, wrong. If you look closely at the output, we get a match for the term DMZ, even though there isn't an exact match for its id in our variable. That's because the XPath contains() function says (correctly) that the value DMZlong contains the ids DMZlong and DMZ.

So our second attempt at solving this problem doesn't require us to change the structure of the XML document, but in this case, we have to change some of our IDs so that the problem we just mentioned doesn't occur. That's probably going to be a maintenance nightmare and a serious drawback to this approach.

5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype

Here we use a recursive template to tokenize the refids attribute into individual IDs, then process each one individually. This style of programming takes a while to get used to, but it can be fairly simple. Here's the crux of our stylesheet:

<xsl:template match="seealso">
  <b>
    <xsl:text>See also: </xsl:text>
  </b>
  <xsl:call-template name="resolveIDREFS">
    <xsl:with-param name="stringToTokenize" select="@refids"/>
  </xsl:call-template>
</xsl:template>

<xsl:template name="resolveIDREFS">
  <xsl:param name="stringToTokenize"/>
  <xsl:variable name="normalizedString">
    <xsl:value-of
      select="concat(normalize-space($stringToTokenize), ' ')"/>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="$normalizedString!=' '">
      <xsl:variable name="firstOfString"
        select="substring-before($normalizedString, ' ')"/>
      <xsl:variable name="restOfString"
        select="substring-after($normalizedString, ' ')"/>
      <a href="#{$firstOfString}">
        <xsl:choose>
          <xsl:when
            test="key('term-ids', $firstOfString)[1]/@xreftext">
            <xsl:value-of
              select="key('term-ids', $firstOfString)[1]/@xreftext"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of
              select="key('term-ids', $firstOfString)[1]"/>
          </xsl:otherwise>
        </xsl:choose>
      </a>
      <xsl:if test="$restOfString!=''">
        <xsl:text>, </xsl:text>
      </xsl:if>
      <xsl:call-template name="resolveIDREFS">
        <xsl:with-param name="stringToTokenize"
          select="$restOfString"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>.</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The first thing we did was invoke the named template resolveIDREFS in the template for the <seealso> element. While invoking the template, we pass in the value of the refids attribute and let recursion work its magic.

The resolveIDREFS template works like this:

  • Break the string into two parts: the first ID and the rest of the string. If there is no first ID (i.e., the string contains only whitespace), we're done.

  • Resolve the cross-reference for the first ID.

  • Invoke the template with the rest of the string.

One technique in particular is worth mentioning here: the way we handled whitespace in the attribute value. We pass the string we want to tokenize as a parameter to the template, but we need to normalize the whitespace. We use two XPath functions to do this: normalize-space() and concat(). The call looks like this:

<xsl:template name="resolveIDREFS">
  <xsl:param name="stringToTokenize"/>
  <xsl:variable name="normalizedString">
    <xsl:value-of
      select="concat(normalize-space($stringToTokenize), ' ')"/>
  </xsl:variable>

The normalize-space() function removes all leading and trailing whitespace from a string and replaces internal whitespace characters with a single space. Remember that whitespace inside an attribute isn't significant; our <seealso> element could be written like this:

  <seealso refids="  wildcard-char

DMZlong
pattern-matching       "/>

When we pass this attribute to normalizeSpace(), the returned value is wildcard-char DMZlong pattern-matching. All whitespace at the start and end of the value has been removed and all the whitespace between characters has been replaced with a single space.

Because we're using the substring-before() and substring-after() functions to find the first token and the rest of the string, it's important that there be at least one space in the string. (It's possible, of course, that an IDREFS attribute contains only one ID.) We use the concat() function to add a space to the end of the string. When the string contains only that space, we know we're done.

Although this approach is more tedious, it does everything we need it to do. We don't have to change our XML document, and we correctly resolve all the IDs in the IDREFS datatype.

5.2.3.5. Solution #4: Use an extension function

The final approach is to write an extension function that tokenizes the refids attribute and returns a node-set containing all id values we need to search for. Xalan ships with an extension that does just that. We invoke the extension function on the value of the refids attribute, then use a <xsl:for-each> element to process all items in the node-set. We'll cover extension functions in Chapter 8, "Extending XSLT", but for now, here's what the stylesheet looks like:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:java="http://xml.apache.org/xslt/java"
  exclude-result-prefixes="java">

<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*"/>

  <xsl:key name="term-ids" match="term" use="@id"/>

  <xsl:template match="/">
    <xsl:apply-templates select="glossary"/>
  </xsl:template>

  <xsl:template match="glossary">
    <html>
      <head>
        <title>
          <xsl:text>Glossary Listing: </xsl:text>
          <xsl:value-of select="glentry[1]/term"/>
          <xsl:text> - </xsl:text>
          <xsl:value-of select="glentry[last()]/term"/>
        </title>
      </head>
      <body>
        <h1>
          <xsl:text>Glossary Listing: </xsl:text>
          <xsl:value-of select="glentry[1]/term"/>
          <xsl:text> - </xsl:text>
          <xsl:value-of select="glentry[last()]/term"/>
        </h1>
        <xsl:apply-templates select="glentry"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="glentry">
    <p>
      <b>
        <a name="{term/@id}"/>
        <xsl:value-of select="term"/>
        <xsl:text>: </xsl:text>
      </b>
      <xsl:apply-templates select="defn"/>
    </p>
  </xsl:template>

  <xsl:template match="defn">
    <xsl:apply-templates
     select="*|comment()|processing-instruction()|text()"/>
  </xsl:template>

  <xsl:template match="xref">
    <a href="#{@refid}">
      <xsl:choose>
        <xsl:when test="key('term-ids', @refid)[1]/@xreftext">
          <xsl:value-of select="key('term-ids', @refid)[1]/@xreftext"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="key('term-ids', @refid)[1]"/>
        </xsl:otherwise>
      </xsl:choose>
    </a>
  </xsl:template>

  <xsl:template match="seealso">
    <b>
      <xsl:text>See also: </xsl:text>
    </b>
    <xsl:for-each
      select="java:org.apache.xalan.lib.Extensions.tokenize(@refids)">
      <a href="{key('term-ids', .)/@id}">
        <xsl:choose>
          <xsl:when test="key('term-ids', .)/@xreftext">
            <xsl:value-of select="key('term-ids', .)/@xreftext"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="key('term-ids', .)"/>
          </xsl:otherwise>
        </xsl:choose>
      </a>
      <xsl:if test="not(position()=last())">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
    <xsl:text>.</xsl:text>
  </xsl:template>

</xsl:stylesheet>

In this case, the tokenize function (defined in the Java class org.apache.xalan.lib.Extensions) takes a string as input, then converts the string into a node-set in which each token in the original string becomes a node.

Be aware that using extension functions limits the portability of your stylesheets. The extension function here does what we want, but we couldn't use this extension function with Saxon, XT, or the XSLT tools from Oracle or Microsoft. They may or may not supply similar functions, and if they do, you'll have to modify your stylesheet slightly to use them. If it's important to you that you be able to switch XSLT processors at some point in the future, using extensions will limit your ability to do that.

Hopefully at this point you're convinced of at least one of the following two things:

  • If you have an attribute with a datatype of IDREFS, you should use the id() function to resolve cross-references.

  • The IDREFS datatype is pretty limited, so you should avoid using it.

5.2.4. Advantages of the key() Function

Now that we've taken the key() function through its paces, you can see that it has several advantages:

  • The key() function is defined in a stylesheet. That means I can define any number of relationships between parts of an XML document at any time. If I need to define a new relationship tomorrow, I don't have to change my XML documents.

  • Any number of key() functions can be defined for a given element. In our glossary example, we could define key() functions for the values of the language, topic, and acronym attributes. We could also create key() functions based on the text of various elements or their children. If we used IDs instead of the key() function, we would be limited to a single index based on the value of the single attribute of the ID datatype.

    To sum up the advantages for this point, an element can have more than one key() defined against it, and that key doesn't have to be based on an attribute. The key can be based on the element's text, the text of child elements, or other constructs.

  • Any number of elements can match a given value. Taking another look at our glossary example, when we use the key() function to find all <defn> elements that are written in a particular language, the function returns a node-set that can have any number of nodes. If we use an ID instead, legally there can be only one element that matches a given ID value.

  • The value we use to look up elements in the key function isn't constrained to be an XML name. If we use the ID datatype, its value can't contain spaces, among other constraints.

Previous | Home | Next
5. Creating Links and Cross-References | Book Index | 5.3. Generating Links in Unstructured Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Generating Links in Unstructured Documents (XSLT)

 XSLSearch this book

5.3. Generating Links in Unstructured Documents

Before we leave the topic of linking, we'll discuss one more useful technique. So far, all of this chapter's examples have been structured nicely. When there was a relationship between two pieces of information, we had an id and refid pair to match them. What happens if the XML document you're transforming isn't written that way? Fortunately, we can use the key() function and a new function, generate-id(), to create structure where there isn't any.

5.3.1. An Unstructured XML Document in Need of Links

For our example here, we'll take out all of the id and refid attributes that have served us well so far. This may be a contrived example, but it demonstrates how we can use the key() and generate-id() functions to generate links between parts of our document.

In our new sample document, we've stripped out the references that neatly tied things together before:

<?xml version="1.0" ?>
<!DOCTYPE glossary SYSTEM "unstructuredglossary.dtd">
<glossary>
  <glentry>
    <term>applet</term>
    <defn>
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.
      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the file system of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <refterm>servlet</refterm>.
    </defn>
  </glentry>

  <glentry>
    <term>demilitarized zone</term>
    <defn>
      In network security, a network that is isolated from, and
      serves as a neutral zone between, a trusted network (for example,
      a private intranet) and an untrusted network (for example, the

      Internet). One or more secure gateways usually control access
      to the DMZ from the trusted or the untrusted network.
    </defn>
  </glentry>

  <glentry>
    <term>DMZ</term>
    <defn>
      See <refterm>delimitarized zone</refterm>.
    </defn>
  </glentry>

  <glentry>
    <term>pattern-matching character</term>
    <defn>
      A special character such as an asterisk (*) or a question mark
      (?) that can be used to represent zero or more characters.
      Any character or set of characters can replace a pattern-matching
      character.
    </defn>
  </glentry>

  <glentry>
    <term>servlet</term>
    <defn>
      An application program, written in the Java programming language,
      that is executed on a web server. A reference to a servlet
      appears in the markup for a web page, in the same way that a
      reference to a graphics file appears. The web server executes
      the servlet and sends the results of the execution (if there are
      any) to the web browser. Contrast with <refterm>applet</refterm>.
    </defn>
  </glentry>

  <glentry>
    <term>wildcard character</term>
    <defn>
      See <refterm>pattern-matching character</refterm>.
    </defn>
  </glentry>
</glossary>

To generate cross-references between the <refterm> elements and the associated <term> elements, we'll need to do three things:

  1. Define a key for all terms. We'll use this key to find terms that match the text of the <refterm> element.

  2. Generate a new ID for each <term> we find.

  3. For each <refterm>, use the key() function to find the <term> element that matches the text of <refterm>. Once we've found the matching <term>, we call generate-id() to find the newly created ID.

We'll go through the relevant parts of the stylesheet. First, we define the key:

<xsl:key name="terms" match="term" use="."/>

Notice that we use the value of the <term> element itself as the lookup value for the key. Given a string, we can find all <term> elements with that same text.

Second, we need to generate a named anchor point for each <term> element:

<xsl:template match="glentry">
  <p>
    <b>
      <a name="{generate-id(term)}">
        <xsl:value-of select="term"/>
        <xsl:text>: </xsl:text>
      </a>
    </b>
    <xsl:apply-templates select="defn"/>
  </p>
</xsl:template>

Third, we find the appropriate reference for a given <refterm>. Given the text of a <refterm>, we can use the key() function to find the <term> that matches. Passing the <term> to the generate-id() function returns the same ID generated when we created the named anchor for that <term>:

<xsl:template match="refterm">
  <a href="#{generate-id(key('terms', .))}">
    <xsl:value-of select="."/>
  </a>
</xsl:template>

Our generated HTML output creates cross-references similar to those in our earlier stylesheets:

    <h1>Glossary Listing: applet - wildcard character</h1>
    <p>
        <b><a name="N11">applet: </a></b>
  An application program,
  written in the Java programming language, that can be
  retrieved from a web server and executed by a web browser.
  A reference to an applet appears in the markup for a web
  page, in the same way that a reference to a graphics
  file appears; a browser retrieves an applet in the same
  way that it retrieves a graphics file.
  For security reasons, an applet's access rights are limited
  in two ways: the applet cannot access the file system of the
  client upon which it is executing, and the applet's
  communication across the network is limited to the server
  from which it was downloaded.
  Contrast with <a href="#N53">servlet</a>.
</p>
...
    <p>
        <b><a name="N53">servlet: </a></b>
  An application program, written in the Java programming language,
  that is executed on a web server. A reference to a servlet
  appears in the markup for a web page, in the same way that a
  reference to a graphics file appears. The web server executes
  the servlet and sends the results of the execution (if there are
  any) to the web browser. Contrast with <a href="#N11">applet</a>.
</p>

Using the key() and generate-id() functions, we've been able to create IDs and references automatically. This approach isn't perfect; we have to make sure the text of the <refterm> element matches the text of the <term> exactly.

This example, like all of the examples we've shown so far, uses a single input file. A more likely scenario is that we have one XML document that contains terms, and we want to reference definitions in a second XML document that contains definitions, but no IDs. We can combine the technique we've described here with the document() function to import a second XML document and generate links between the two. We'll talk about the document() function in a later chapter; for now, just remember that there are ways to use more than one XML input document in your transformations.

5.3.2. The generate-id() Function

Before we leave the topic of linking, we'll go over the details of the generate-id() function. This function takes a node-set as its argument, and works as follows:

  • For a given transformation, every time generate-id() is invoked against a given node, it returns the same ID. The ID doesn't change while you're doing a given transformation. If you run the transformation again, there's no guarantee generate-id() will generate the same ID the second time around. All calls to generate-id() in the second transformation will return the same ID, but that ID might not be the same as in the first transformation.

    TIP: The generate-id() function is not required to check if an ID it generates duplicates an ID that's already in the document. In other words, if your document has an attribute of type ID with a value of sdk3829a, there's a possibility that an ID returned by generate-id() will also be sdk3829a. It's not likely, but be aware that it could happen.

  • If you invoke generate-id() against two different nodes, the two generated IDs will be different.

  • Given a node-set, generate-id() returns an ID for the node in the node-set that occurs first in document order.

  • If the node-set you pass to the function is empty (you invoke generate-id(fleeber), and there are no <fleeber> elements in the current context), generate-id() returns an empty string.

  • If no node-set is passed in (you invoke generate-id()), the function generates an ID for the context node.

Previous | Home | Next
5.2. Generating Links with the key() Function | Book Index | 5.4. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

5.4. Summary

In this chapter, we've examined a several ways to generate links and cross-references between different parts of a document. If your XML document has a reasonable amount of structure, you can use the id() and key() functions to define many different relationships between the parts of a document. Even if your XML document isn't structured, you may be able to use key() and generate-id() to create simple references. In the next chapter, we'll look at sorting and grouping, two more ways to organize the information in our XML documents.

Previous | Home | Next
5.3. Generating Links in Unstructured Documents | Book Index | 6. Sorting and Grouping Elements
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Sorting and Grouping Elements (XSLT)

 XSLTSearch this book

Chapter 6. Sorting and Grouping Elements

Contents:

Sorting Data with <xsl:sort>
Grouping Nodes
Summary

By now, I hope you're convinced that you can use XSLT to convert big piles of XML data into other useful things. Our examples to this point have pretty much gone through the XML source in what's referred to as document order. We'd like to go through our XML documents in a couple of other common ways, though:

  • We could sort some or all of the XML elements, then generate output based on the sorted elements.

  • We could group the data, selecting all elements that have some property in common, then sorting the groups of elements.

We'll give several examples of these operations in this chapter.

6.1. Sorting Data with <xsl:sort>

The simplest way to rearrange our XML elements is to use the <xsl:sort> element. This element temporarily rearranges a collection of elements based on criteria we define in our stylesheet.

6.1.1. Our First Example

For our first example, we'll have a set of U.S. postal addresses that we want to sort. (No chauvinism is intended here; obviously every country has different conventions for mailing addresses. We just needed a short sample document that can be sorted in many useful ways.) Here's our original document:

<?xml version="1.0"?>
<addressbook>
  <address>
    <name>
      <title>Mr.</title>
      <first-name>Chester Hasbrouck</first-name>
      <last-name>Frisby</last-name>
    </name>
    <street>1234 Main Street</street>
    <city>Sheboygan</city>
    <state>WI</state>
    <zip>48392</zip>
  </address>
  <address>
    <name>
      <first-name>Mary</first-name>
      <last-name>Backstayge</last-name>
    </name>
    <street>283 First Avenue</street>
    <city>Skunk Haven</city>
    <state>MA</state>
    <zip>02718</zip>
  </address>
  <address>
    <name>
      <title>Ms.</title>
      <first-name>Natalie</first-name>
      <last-name>Attired</last-name>
    </name>
    <street>707 Breitling Way</street>
    <city>Winter Harbor</city>
    <state>ME</state>
    <zip>00218</zip>
  </address>
  <address>
    <name>
      <first-name>Harry</first-name>
      <last-name>Backstayge</last-name>
    </name>
    <street>283 First Avenue</street>
    <city>Skunk Haven</city>
    <state>MA</state>
    <zip>02718</zip>
  </address>
  <address>
    <name>
      <first-name>Mary</first-name>
      <last-name>McGoon</last-name>
    </name>
    <street>103 Bryant Street</street>
    <city>Boylston</city>
    <state>VA</state>
    <zip>27318</zip>
  </address>
  <address>
    <name>

      <title>Ms.</title>
      <first-name>Amanda</first-name>
      <last-name>Reckonwith</last-name>
    </name>
    <street>930-A Chestnut Street</street>
    <city>Lynn</city>
    <state>MA</state>
    <zip>02930</zip>
  </address>
</addressbook>

We'd like to generate a list of these addresses, sorted by <last-name>. We'll use the magical <xsl:sort> element to do the work. Our stylesheet looks like this:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" indent="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:for-each select="addressbook/address">
      <xsl:sort select="name/last-name"/>
      <xsl:value-of select="name/title"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="name/first-name"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="name/last-name"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="street"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="city"/>
      <xsl:text>, </xsl:text>
      <xsl:value-of select="state"/>
      <xsl:text>  </xsl:text>
      <xsl:value-of select="zip"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

The heart of our stylesheet are the <xsl:for-each> and <xsl:sort> elements. The <xsl:for-each> element selects the items with which we'll work, and the <xsl:sort> element rearranges them before we write them out.

Notice that we're generating a text file (<xsl:output method="text"/>). (You could generate an HTML file or something more complicated if you want.) To invoke the stylesheet engine, we run this command:

java org.apache.xalan.xslt.Process -in names.xml -xsl namesorter1.xsl
  -out names.text

Here are the results we get from our first attempt at sorting:

Ms. Natalie Attired
707 Breitling Way
Winter Harbor, ME  00218

 Mary Backstayge
283 First Avenue
Skunk Haven, MA  02718

 Harry Backstayge
283 First Avenue
Skunk Haven, MA  02718

Mr. Chester Hasbrouck Frisby
1234 Main Street
Sheboygan, WI  48392

 Mary McGoon
103 Bryant Street
Boylston, VA  27318

Ms. Amanda Reckonwith
930-A Chestnut Street
Lynn, MA  02930

As you can see from the output, the addresses in our original document were sorted by last name. All we had to do was add xsl:sort to our stylesheet, and all the elements were magically reordered. If you aren't convinced that XSLT can increase your programmer productivity, try writing the Java code and DOM method calls to do the same thing.

We can do a couple of things to improve our original stylesheet, however. For one thing, there's an annoying blank space at the start of every name that doesn't have a <title> element. A more significant improvement is that we'd like to sort addresses by <first-name> within <last-name>. In our last example, Mary Backstayge should appear after Harry Backstayge. Here's how we can modify our stylesheet to use more than one sort key:

<xsl:template match="/">
  <xsl:for-each select="addressbook/address">
    <xsl:sort select="name/last-name"/>
    <xsl:sort select="name/first-name"/>
    ...

We've simply added a second <xsl:sort> element to our stylesheet. This element does what we want; it sorts the <address> elements by <first-name> within <last-name>. To be thoroughly obsessive about our output, we can use an <xsl:if> element to get rid of that annoying blank space in front of names with no <title> element:

<xsl:if test="name/title">
  <xsl:value-of select="name/title"/>
  <xsl:text> </xsl:text>
</xsl:if>

Now our output is perfect:

Ms. Natalie Attired
707 Breitling Way
Winter Harbor, ME  00218

Harry Backstayge
283 First Avenue
Skunk Haven, MA  02718

Mary Backstayge
283 First Avenue
Skunk Haven, MA  02718

Mr. Chester Hasbrouck Frisby
1234 Main Street
Sheboygan, WI  48392

Mary McGoon
103 Bryant Street
Boylston, VA  27318

Ms. Amanda Reckonwith
930-A Chestnut Street
Lynn, MA  02930

6.1.2. The Details on the <xsl:sort> Element

Now that we've seen a couple of examples of how <xsl:sort> works, we'll go over its syntax, its attributes, and where you can use it.

6.1.2.1. What's the deal with that syntax?

I'm so glad you asked that question. One thing the XSLT working group could have done is something like this:

<xsl:for-each select="addressbook/address" sort-key-1="name/last-name"
  sort-key-2="name/first-name"/>

The problem with this approach is that no matter how many sort-key-x attributes you define, out of sheer perverseness, someone will cry out that they really need the sort-key-8293 attribute. To avoid this messy problem, the XSLT designers decided to let you specify the sort keys by using a number of <xsl:sort> elements. The first is the primary sort key, the second is the secondary sort key, the 8293rd one is the eight-thousand-two-hundred-and-ninety-third sort key, etc.

Well, that's why the syntax looks the way it does, but how does it actually work? When I first saw this syntax:

<xsl:for-each select="addressbook/address">
  <xsl:sort select="name/last-name"/>
  <xsl:sort select="name/first-name"/>
  <xsl:apply-templates select="."/>
</xsl:for-each>

I thought it meant that all the nodes were sorted during each iteration through the <xsl:for-each> element. That seemed incredibly inefficient; if you've sorted all the nodes, why resort them each time through the <xsl:for-each> element? Actually, the XSLT processor handles all <xsl:sort> elements before it does anything, then it processes the <xsl:for-each> element as if the <xsl:sort> elements weren't there.

It's less efficient, but if it makes you feel better about the syntax, you could write the stylesheet like this:

<xsl:template match="/">
  <xsl:for-each select="addressbook/address">
    <xsl:sort select="name/last-name"/>
    <xsl:sort select="name/first-name"/>
    <xsl:for-each select=".">  <!-- This is slower, but it works -->
      <xsl:apply-templates select="."/>
    </xsl:for-each>
  </xsl:for-each>
</xsl:template>

(Don't actually do this. I'm only trying to make a point.) This stylesheet generates the same results as our earlier stylesheet.

6.1.2.2. Attributes

The <xsl:sort> element has several attributes, all of which are discussed here.

select
The select attribute defines the characteristic we'll use for sorting. Its contents are an XPath expression, so you can select elements, text, attributes, comments, ancestors, etc. As always, the XPath expression defined in select is evaluated in terms of the current context.

data-type
The data-type attribute can have three values:

  • data-type="text"

  • data-type="number"

  • A data-type="QName" that identifies a particular datatype. The stated goal of the XSLT working group is that the datatypes defined in the XML Schema specification will eventually be supported here.

The XSLT specification defines the behavior for data-type="text" and data-type="number". Consider this XML document:

<?xml version="1.0"?>
<numberlist>
  <number>127</number>
  <number>23</number>
  <number>10</number>
</numberlist>

We'll sort these values using the default value (data-type="text"):

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" indent="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:for-each select="numberlist/number">
      <xsl:sort select="."/>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

When we sort these elements using data-type="text", here's what we get:

10
127
23

We get this result because a text-based sort puts anything that starts with a "1" before anything that starts with a "2." If we change the <xsl:sort> element to be <xsl:sort select="." data-type="number"/>, we get these results:

10
27
123

If you use something else here (data-type="floating-point", for example), what the XSLT processor does is anybody's guess. The XSLT specification allows for other values here, but it's up to the XSLT processor to decide how (or if) it wants to process those values. Check your processor's documentation to see if it does anything relevant or useful for values other than data-type="text" or data-type="number".

A final note: if you're using data-type="number", and any of the values aren't numbers, those non-numeric values will sort before the numeric values. That means if you're using order="ascending", the non-numeric values appear first; if you use order="descending", the non-numeric values appear last.

<?xml version="1.0"?>
<numberlist>
  <number>127</number>
  <number>23</number>
  <number>zzz</number>
  <number>10</number>
  <number>yyy</number>
</numberlist>

Given this less-than-perfect data, here are the correctly sorted results:

zzz
yyy
10
23
127

Notice that the non-numeric values were not sorted; they simply appear in the output document in the order in which they were encountered.

order
You can order the sort as order="ascending" or order="descending". The default is order="ascending".

case-order
This attribute can have two values. case-order="upper-first" means that uppercase letters sort before lowercase letters, and case-order="lower-first" means that lowercase letters sort first. The case-order attribute is used only when the data-type attribute is text. The default value depends on the value of the soon-to-be-discussed lang attribute.

lang
This attribute defines the language of the sort keys. The valid values for this attribute are the same as those for the xml:lang attribute defined in Section 2.12 of the XML 1.0 specification. The language codes are those commonly used in Java programming, UNIX locales, and other places ISO language and country namings are defined. For example, lang="en" means "English," lang="en-US" means "U.S. English," and lang="en-GB" means "U.K. English." Without the lang attribute (it's rarely used in practice), the XSLT processor determines the default language from the system environment.

6.1.2.3. Where can you use <xsl:sort>?

The <xsl:sort> element can appear inside two elements:

  • <xsl:apply-templates>

  • <xsl:for-each>

If you use an <xsl:sort> element inside <xsl:for-each>, the <xsl:sort> element(s) must appear first. If you tried something like this, you'd get an exception from the XSLT processor:

<xsl:for-each select="addressbook/address">
  <xsl:sort select="name/last-name"/>
  <xsl:value-of select="name/title"/>
  <xsl:sort select="name/first-name"/> <!-- NOT LEGAL! -->
  ...

6.1.3. Another Example

We've pretty much covered the <xsl:sort> element at this point. To add another wrinkle to our example, we'll change the stylesheet so the xsl:sort element acts upon a subset of the addresses, then sorts that subset. We'll sort only the addresses from states that start with the letter M. As you'd expect, we'll do this magic with an XPath expression that limits the elements to be sorted:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" indent="no"/>
  <xsl:strip-space elements="*"/>
  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:for-each select="addressbook/address/[starts-with(state, 'M')]">
      <xsl:sort select="name/last-name"/>
      <xsl:sort select="name/first-name"/>
      <xsl:if test="name/title">
        <xsl:value-of select="name/title"/>
        <xsl:text> </xsl:text>
      </xsl:if>
      <xsl:value-of select="name/first-name"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="name/last-name"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="street"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="city"/>
      <xsl:text>, </xsl:text>
      <xsl:value-of select="state"/>
      <xsl:text>  </xsl:text>
      <xsl:value-of select="zip"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Here are the results, only those addresses from states beginning with the letter M, sorted by first name within last name:

Ms. Natalie Attired
707 Breitling Way
Winter Harbor, ME  00218

Harry Backstayge
283 First Avenue
Skunk Haven, MA  02718

Mary Backstayge
283 First Avenue
Skunk Haven, MA  02718

Ms. Amanda Reckonwith
930-A Chestnut Street
Lynn, MA  02930

Notice that in the xsl:for-each element, we used a predicate in our XPath expression so that only addresses containing <state> elements whose contents begin with M are selected. This example starts us on the path to grouping nodes. We could do lots of other things here:

  • We could generate output that prints all the unique Zip Codes, along with the number of addresses that have those Zip Codes.

  • For each unique Zip Code (or state, or last name, etc.) we could sort on a field and list all addresses with that Zip Code.

We'll discuss these topics in the next section.

Previous | Home | Next
5.4. Summary | Book Index | 6.2. Grouping Nodes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Grouping Nodes (XSLT)

 XSLSearch this book

6.2. Grouping Nodes

When grouping nodes, we sort things to get them into a certain order, then we group all items that have the same value for the sort key (or keys). We'll use xsl:sort for this grouping, then use variables or functions like key() or generate-id() to finish the job.

6.2.1. Our First Attempt

For our first example, we'll take our list of addresses and group them. We'll look for all unique values of the <zip> element and list the addresses that match each one. What we'll do is sort the list by Zip Code, then go through the list. If a given item doesn't match the previous Zip Code, we'll print out a heading; if it does match, we'll just print out the address. Here's our first attempt:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" indent="no"/>
  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:text>Addresses sorted by Zip Code</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="addressbook/address">
      <xsl:sort select="zip"/>
      <xsl:if test="zip!=preceding-sibling::address[1]/zip">
        <xsl:value-of select="$newline"/>
        <xsl:text>Zip code </xsl:text>
        <xsl:value-of select="zip"/>
        <xsl:text> (</xsl:text>
        <xsl:value-of select="city"/>
        <xsl:text>, </xsl:text>
        <xsl:value-of select="state"/>
        <xsl:text>): </xsl:text>
        <xsl:value-of select="$newline"/>
      </xsl:if>
      <xsl:if test="name/title">
        <xsl:value-of select="name/title"/>
        <xsl:text> </xsl:text>
      </xsl:if>
      <xsl:value-of select="name/first-name"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="name/last-name"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="street"/>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>

    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Our approach in this stylesheet consists of two steps:

  1. Sort the addresses by Zip Code.

    <xsl:sort select="zip"/>

  2. For each Zip Code, if it doesn't match the previous Zip Code, print out a heading, then print out the addresses that match it.

    <xsl:if test="zip!=preceding-sibling::address[1]/zip">
  <xsl:value-of select="$newline"/>
  <xsl:text>Zip code </xsl:text>
  ...

    (Remember that preceding-sibling returns a NodeSet, so preceding-sibling::address[1] represents the first preceding sibling.)

That sounds reasonable, doesn't it? Let's take a look at the results:

Addresses sorted by Zip Code

Zip code 00218 (Winter Harbor, ME):
Ms. Natalie Attired
707 Breitling Way

Zip code 02718 (Skunk Haven, MA):
Mary Backstayge
283 First Avenue

Harry Backstayge
283 First Avenue

Zip code 02930 (Lynn, MA):
Ms. Amanda Reckonwith
930-A Chestnut Street

Zip code 27318 (Boylston, VA):
Mary McGoon
103 Bryant Street

Mr. Chester Hasbrouck Frisby
1234 Main Street

Yes, that certainly seemed like a good approach, but there's one minor problem: it doesn't work.

Looking at our results, there seems to be only one problem: one of the addresses (Mr. Chester Hasbrouck Frisby) is grouped under the heading for Boylston, Virginia, but he actually lives in Sheboygan, Wisconsin, Zip Code 48392. The problem here is that the axes work with the document order, not the sorted order we've created inside the xsl:for-each element.

As straightforward as our logic seemed, we'll have to find another way.

6.2.2. A Brute-Force Approach

One thing we could do is make the transformation in two passes; we could write an intermediate stylesheet to sort the names and generate a new XML document, then use the stylesheet we've already written, because document order and sorted order will be the same. Here's how that intermediate stylesheet would look:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <addressbook>
      <xsl:for-each select="addressbook/address">
        <xsl:sort select="name/last-name"/>
        <xsl:sort select="name/first-name"/>
        <xsl:copy-of select="."/>
      </xsl:for-each>
    </addressbook>
  </xsl:template>
</xsl:stylesheet>

This stylesheet generates a new <addressbook> document that has all of the <address> elements sorted correctly. We can then run our original stylesheet against the sorted document and get the results we want. This works, but it's not very elegant. Even worse, it's really slow because we have to stop in the middle and write a file out to disk, then read that data back in. We'll find a way to group elements in a single stylesheet, but we'll have to do it with a different technique.

6.2.3. Grouping with <xsl:variable>

We mentioned earlier that sometimes <xsl:variable> is useful for grouping, so let's try that approach. We'll save the value of the <zip> element each time through the <xsl:for-each> element and use preceding-sibling in a slightly different way. Here's how attempt number three looks:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" indent="no"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:text>Addresses sorted by Zip Code</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="addressbook/address">
      <xsl:sort select="zip"/>
      <xsl:sort select="name/last-name"/>
      <xsl:sort select="name/first-name"/>
      <xsl:variable name="lastZip" select="zip"/>
      <xsl:if test="not(preceding-sibling::address[zip=$lastZip])">
        <xsl:text>Zip code </xsl:text>
        <xsl:value-of select="zip"/>
        <xsl:text>: </xsl:text>
        <xsl:value-of select="$newline"/>
        <xsl:for-each select="/addressbook/address[zip=$lastZip]">
          <xsl:sort select="name/last-name"/>
          <xsl:sort select="name/first-name"/>
          <xsl:if test="name/title">
            <xsl:value-of select="name/title"/>
            <xsl:text> </xsl:text>
          </xsl:if>
          <xsl:value-of select="name/first-name"/>
          <xsl:text> </xsl:text>
          <xsl:value-of select="name/last-name"/>
          <xsl:value-of select="$newline"/>
          <xsl:value-of select="street"/>
          <xsl:value-of select="$newline"/>
          <xsl:value-of select="$newline"/>
        </xsl:for-each>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

This stylesheet generates what we want:

Addresses sorted by Zip Code
Zip code 00218:
Ms. Natalie Attired
707 Breitling Way

Zip code 02718:
Harry Backstayge
283 First Avenue

Mary Backstayge
283 First Avenue

Zip code 02930:
Ms. Amanda Reckonwith
930-A Chestnut Street

Zip code 27318:
Mary McGoon
103 Bryant Street

Zip code 48392:
Mr. Chester Hasbrouck Frisby
1234 Main Street

So why does this approach work when our first attempt didn't? The answer is: we don't count on the sorted order of the elements to generate the output. The downside of this approach is that we go through several steps to get the results we want:

  1. We sort all the addresses by Zip Code:

    <xsl:sort select="zip"/>

  2. We store the current <zip> element's value in the variable lastZip:

    <xsl:variable name="lastZip" select="zip"/>

  3. For each <zip> element, we look at all of its preceding siblings to see if this is the first time we've encountered this particular value (stored in lastZip). If it is, there won't be any preceding siblings that match.

    <xsl:if test="not(preceding-sibling::address[zip=$lastZip])">

  4. If this is the first time we've encountered this value in the <zip> element, we go back and reselect all <address> elements with <zip> children that match this value. Once we have that group, we sort them by first name within last name and print each address.

    <xsl:for-each select="/addressbook/address[zip=$lastZip]">
  <xsl:sort select="name/last-name"/>
  <xsl:sort select="name/first-name"/>

So, we've found a way to get the results we want, but it's really inefficient. We sort the data, then we look at each Zip Code in sorted order, then see if we've encountered that value before in document order, then we reselect all the items that match the current Zip Code and resort them before we write them out. Whew! There's got to be a better way, right? Well, since we're not at the end of the chapter, it's a safe bet we'll find a better way in the next section. Read on....

6.2.4. The <xsl:key> Approach

In this section, we'll look at using <xsl:key> to group items in an XML document. This approach is commonly referred to as the "Muench method," after Oracle XML Evangelist (and O'Reilly author) Steve Muench, who first suggested this technique. The Muench method has three steps:

  1. Define a key for the property we want to use for grouping.

  2. Select all of the nodes we want to group. We'll do some tricks with the key() and generate-id() functions to find the unique grouping values.

  3. For each unique grouping value, use the key() function to retrieve all nodes that match it. Because the key() function returns a node-set, we can do further sorts on the set of nodes that match any given grouping value.

Well, that's how the technique works -- let's start building the stylesheet that makes the magic happen. The first step, creating a key function, is easy. Here's how it looks:

<xsl:key name="zipcodes" match="address" use="zip"/>

This <xsl:key> element defines a new index called zipcodes. It indexes <address> elements based on the value of the <zip> element they contain.

Now that we've defined our key, we're ready for the complicated part. We use the key() and generate-id() functions together. Here's the syntax, which we'll discuss extensively in a minute:

<xsl:for-each select="//address[generate-id(.)=
  generate-id(key('zipcodes', zip)[1])]">

Okay, let's take a deep, cleansing breath and start digging through this syntax. What we're selecting here is all <address> elements in which the automatically generated id matches the automatically generated id of the first node returned by the key() function when we ask for all <address> elements that match the current <zip> element.

Well, that's clear as crystal, isn't it? Let me try to explain that again from a slightly different perspective.

For each <address>, we use the key() function to retrieve all <address>es that have the same <zip>. We then take the first node from that node-set. Finally, we use the generate-id() function to generate an id for both nodes. If the two generated ids are identical, then the two nodes are the same.

Whew. Let me catch my breath.

If this <address> matches the first node returned by the key() function, then we know we've found the first <address> that matches this grouping value. Selecting all of the first values (remember, our previous predicate ends with [1]) gives us a node-set of some number of <address> elements, each of which contains one of the unique grouping values we need.

Well, that's how this technique works. At this point, we've got a way to generate a node-set that contains all of the unique grouping values; now we need to process those nodes. From this point, we'll do several things, all of which are comparatively simple:

  • Sort all nodes based on the grouping property. In this example, the property is the <zip> element. We start by selecting the first occurrence of every unique <zip> element in the document, then we sort those <zip> elements. Here's how it looks in the stylesheet:

    <xsl:for-each
  select="//address[generate-id(.)=generate-id(key('zipcodes', zip)[1])]">
  <xsl:sort select="zip"/>

  • The outer <xsl:for-each> element selects all the unique values of the <zip> element. Next, we use the key() function to retrieve all <address> elements that match the current <zip> element:

    <xsl:for-each select="key('zipcodes', zip)">

  • The key() function gives us a node-set of all matching <address> elements. We sort that node-set based on the <last-name> and <first-name> elements, then process them in turn:

        <xsl:sort select="name/last-name"/>
    <xsl:sort select="name/first-name"/>
    <tr>
      <xsl:if test="position() = 1">
        <td valign="center" bgcolor="#999999">
          <xsl:attribute name="rowspan">
            <xsl:value-of select="count(key('zipcodes', zip))"/>
          </xsl:attribute>
          <b>
            <xsl:text>Zip code </xsl:text><xsl:value-of select="zip"/>
          </b>
        </td>
      </xsl:if>
      <td align="right">
        <xsl:value-of select="name/first-name"/>
        <xsl:text> </xsl:text>
        <b><xsl:value-of select="name/last-name"/></b>
      </td>
      <td>
        <xsl:value-of select="street"/>
        <xsl:text>, </xsl:text>
        <xsl:value-of select="city"/>
        <xsl:text>, </xsl:text>
        <xsl:value-of select="state"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="zip"/>
      </td>
    </tr>
  </xsl:for-each>
</xsl:for-each>

    We generate a table cell that contains the Zip Code common to all addresses, creating a rowspan attribute based on the number of matches for the current Zip Code. From there, we write the other data items into table cells.

Here's our complete stylesheet:

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" indent="no"/>

  <xsl:key name="zipcodes" match="address" use="zip"/>

  <xsl:template match="/">
    <table border="1">
      <xsl:for-each select="//address[generate-id(.)=
        generate-id(key('zipcodes', zip)[1])]">
        <xsl:sort select="zip"/>
        <xsl:for-each select="key('zipcodes', zip)">
          <xsl:sort select="name/last-name"/>
          <xsl:sort select="name/first-name"/>
          <tr>
            <xsl:if test="position() = 1">
              <td valign="center" bgcolor="#999999">
                <xsl:attribute name="rowspan">
                  <xsl:value-of select="count(key('zipcodes', zip))"/>
                </xsl:attribute>
                <b>
                  <xsl:text>Zip code </xsl:text><xsl:value-of select="zip"/>
                </b>
              </td>
            </xsl:if>
            <td align="right">
              <xsl:value-of select="name/first-name"/>
              <xsl:text> </xsl:text>
              <b><xsl:value-of select="name/last-name"/></b>
            </td>
            <td>
              <xsl:value-of select="street"/>
              <xsl:text>, </xsl:text>
              <xsl:value-of select="city"/>
              <xsl:text>, </xsl:text>
              <xsl:value-of select="state"/>
              <xsl:text> </xsl:text>
              <xsl:value-of select="zip"/>
            </td>
          </tr>
        </xsl:for-each>
      </xsl:for-each>
    </table>
  </xsl:template>

</xsl:stylesheet>

When we view the generated HTML document in a browser, it looks like Figure 6-1.

Figure 6-1. HTML document with grouped items

Notice how the two <xsl:for-each> and the various <xsl:sort> elements work together. The outer <xsl:for-each> element selects the unique values of the <zip> element and sorts them; the inner <xsl:for-each> element selects all <address> elements that match the current <zip> element, and then sorts them by <last-name> and <first-name>.

Previous | Home | Next
6. Sorting and Grouping Elements | Book Index | 6.3. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

6.3. Summary

In this chapter, we've gone over all of the common techniques used for sorting and grouping elements. Regardless of the kinds of stylesheets you'll need to write in your XML projects, you'll probably use these techniques in everything you do. Now that we've covered how to sort and group elements, we'll talk about how to combine multiple input documents next; this subject will build on the topics we've covered here.

Previous | Home | Next
6.2. Grouping Nodes | Book Index | 7. Combining XML Documents
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Combining XML Documents (XSLT)

 XSLTSearch this book

Chapter 7. Combining XML Documents

Contents:

Overview
The document() Function
Invoking the document() Function
More Sophisticated Techniques
Summary

One of XSLT's most powerful features is the document() function. document() lets you use part of an XML document (identified with an XPath expression, of course) as a URI. In other words, you can look in a document, use parts of that document as URLs (or filenames), open and parse those files, then perform stylesheet functions on the combination of all those documents. In this chapter, we'll cover the document() function in all its glory.

7.1. Overview

The document() function is very useful for defining views of multiple XML documents. In this chapter, we'll use XML-tagged purchase orders that look like this:

<purchase-order id="38292">
  <customer id="4738" level="Platinum">
    <address type="business">
      <name>
        <title>Mr.</title>
        <first-name>Chester Hasbrouck</first-name>
        <last-name>Frisby</last-name>
      </name>
      <street>1234 Main Street</street>
      <city>Sheboygan</city>
      <state>WI</state>
      <zip>48392</zip>
    </address>
    <address type="ship-to"/>
  </customer>
  <items>
    <item part_no="28392-33-TT">
      <name>Turnip Twaddler</name>

      <qty>3</qty>
      <price>9.95</price>
    </item>
    <item part_no="28813-70-PG">
      <name>Prawn Goader</name>
      <qty>1</qty>
      <price>18.95</price>
    </item>
  </items>
</purchase-order>

If we had a few dozen documents like this, we might want to view the collection of purchase orders in a number of ways. We could view them sorted (or even grouped) by customer, by part number, by the amount of the total order, by the state to which they were shipped, etc. One way to do this would be to write code that worked directly with the Document Object Model. We could parse each document, retrieve its DOM tree, then use DOM functions to order and group the various DOM trees, display certain parts of the DOM trees, etc. Because this is an XSLT book, though, you probably won't be surprised to learn that XSLT provides a function to handle most of the heavy lifting for us.

Previous | Home | Next
6.3. Summary | Book Index | 7.2. The document() Function
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The document() Function (XSLT)

 XSLSearch this book

7.2. The document() Function

We'll start with a couple of simple examples that use the document() function. We'll assume that we have several purchase orders and that we want to combine them into a single report document. One thing we can do is create a master document that references all the purchase orders we want to include in the report. Here's what that master document might look like:

<report>
  <title>Purchase Orders</title>
  <po filename="po38292.xml"/>
  <po filename="po38293.xml"/>
  <po filename="po38294.xml"/>
  <po filename="po38295.xml"/>
</report>

We'll fill in the details of our stylesheet as we go along, but here's what the shell of our stylesheet looks like:

<xsl:template match="/">
  <xsl:for-each select="/report/po">
    <xsl:apply-templates select="document(@filename)"/>
  </xsl:for-each>
</xsl:template>

In this template, we use the filename attribute as the argument to the document() function. The simplest thing we can do is open each purchase order, then write its details to the output stream. Here's a stylesheet that does this:

<?xml version="1.0"?>-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" indent="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="/report/title"/></title>
      </head>
      <body>
        <xsl:for-each select="/report/po">
          <xsl:apply-templates select="document(@filename)/purchase-order"/>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="purchase-order">
    <h1>
      <xsl:value-of select="customer/address[@type='business']/name/title"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="customer/address[@type='business']/name/first-name"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="customer/address[@type='business']/name/last-name"/>
    </h1>
    <p>
      <xsl:text>Ordered on </xsl:text>
      <xsl:value-of select="date/@month"/>
      <xsl:text>/</xsl:text>
      <xsl:value-of select="date/@day"/>
      <xsl:text>/</xsl:text>
      <xsl:value-of select="date/@year"/>
    </p>
    <h2>Items:</h2>
    <table width="100%" border="1" cols="55% 15% 15% 15%">
      <tr bgcolor="lightgreen">
        <th>Item</th>
        <th>Quantity</th>
        <th>Price Each</th>
        <th>Total</th>
      </tr>
      <xsl:for-each select="items/item">
        <tr>
          <xsl:attribute name="bgcolor">
            <xsl:choose>
              <xsl:when test="position() mod 2">
                <xsl:text>white</xsl:text>
              </xsl:when>
              <xsl:otherwise>
                <xsl:text>lightgreen</xsl:text>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:attribute>
          <td>
            <b><xsl:value-of select="name"/></b>
            <xsl:text> (part #</xsl:text>
            <xsl:value-of select="@part_no"/>
            <xsl:text>)</xsl:text>
          </td>
          <td align="center">
            <xsl:value-of select="qty"/>
          </td>
          <td align="right">
            <xsl:value-of select="price"/>
          </td>
          <td align="right">
            <xsl:choose>
              <xsl:when test="position()=1">
                <xsl:value-of select="format-number(price * qty, '$#,###.00')"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="format-number(price * qty, '#,###.00')"/>
              </xsl:otherwise>
            </xsl:choose>
          </td>
        </tr>
      </xsl:for-each>
      <tr>
        <td colspan="3" align="right">
          <b>Total:</b>
        </td>
        <td align="right">
          <xsl:variable name="orderTotal">
            <xsl:call-template name="sumItems">
              <xsl:with-param name="index" select="'1'"/>
              <xsl:with-param name="items" select="items"/>
              <xsl:with-param name="runningTotal" select="'0'"/>
            </xsl:call-template>
          </xsl:variable>
          <xsl:value-of select="format-number($orderTotal, '$#,###.00')"/>
        </td>
      </tr>
    </table>
  </xsl:template>

  <xsl:template name="sumItems">
    <xsl:param name="index" select="'1'"/>
    <xsl:param name="items"/>
    <xsl:param name="runningTotal" select="'0'"/>
    <xsl:variable name="currentItem">
      <xsl:value-of select="$items/item[$index]/qty *
  $items/item[$index]/price"/>
    </xsl:variable>
    <xsl:variable name="remainingItems">
      <xsl:choose>
        <xsl:when test="$index=count($items/item)">
          <xsl:text>0</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="sumItems">
            <xsl:with-param name="index" select="$index+1"/>
            <xsl:with-param name="items" select="$items"/>
            <xsl:with-param name="runningTotal"
              select="$runningTotal+$currentItem"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="$currentItem+$remainingItems"/>
  </xsl:template>

</xsl:stylesheet>

When we process our master document with this stylesheet, the results look like Figure 7-1.

Figure 7-1. Document generated from multiple input files

The most notable thing about our results is that we've been able to generate a document that contains the contents of several other documents. To keep our example short, we've only combined four purchase orders, but there's no limit (beyond the physical limits of our machine) to the number of documents we could combine. Best of all, we didn't have to modify any of the individual purchase orders to generate our report.

7.2.1. An Aside: Doing Math with Recursion

While we're here, we'll also mention the recursive technique we used to calculate the total for each purchase order. At first glance, this seems like a perfect opportunity to use the sum() function. We want to add the total of the price of each item multiplied by its quantity. We could try to invoke the sum() function like this:

<xsl:value-of select="sum(item/qty*item/price)"/>

Unfortunately, the sum() function simply takes the node-set passed to it, converts each item in the node-set to a number, then returns the sum of all of those numbers. The expression item/qty*item/price, while a perfectly valid XPath expression, isn't a valid node-set. With that in mind, we have to create a recursive <xsl:template> to do the work for us. There are a couple of techniques worth mentioning here; we'll go through them in the order we used them in our stylesheet.

7.2.1.1. Recursive design

First, we pass three parameters to the template:

items
The node-set of all <item> elements in the current <items> element.

index
The position in that node-set of the <item> we're currently processing.

runningTotal
The total of all the <item>s we've processed so far.

Our basic design is as follows:

  • Calculate the total for the current <item>. This total is the value of the <qty> element multiplied by the value of the <price> element. We store this value in the variable currentItem:

    <xsl:variable name="currentItem">
  <xsl:value-of select="$items/item[$index]/qty *
    $items/item[$index]/price"/>
</xsl:variable>

    Notice how the XPath expression in the select attribute uses the $items and $index parameters to find the exact items we're looking for.

  • Calculate the total for the remaining items. If this is the last item (the parameter $index is equal to the number of <item> elements), then the total for the remaining items is zero. Otherwise, the total for the remaining items is returned by calling the template again.

    When we call the template again, we increment the position of the current item:

    <xsl:with-param name="index" select="$index+1"/>

    We also update the parameter $runningTotal, which is equal to the value of the current item plus the previous value of $runningTotal:

    <xsl:with-param name="runningTotal"
  select="$runningTotal+$currentItem"/>

This recursive design lets us generate the totals we need for our purchase order. Our approach is equivalent to invoking a function against each node in a node-set, only this approach doesn't require us to use extensions. As a result, we can use this stylesheet with any standards-compliant stylesheet processor, without having to worry about porting any extension functions or extension elements.

7.2.1.2. Generating output to initialize a variable

When we needed to set the value of the variable runningTotal, we simply called the template named sumItems. The sumItems template uses the <xsl:value-of> element to output some text; everything output by the template is concatenated to form the value of the variable runningTotal. The advantage of this technique is that it allows us to completely control the value of the variable, and it allows us to avoid converting the variable to a number until we're ready. Once the sumItems template finishes its work, we can pass the variable's value to the format-number() function to print the invoice total exactly how we want.

7.2.1.3. Using format-number() to control output

The final nicety in our stylesheet is that we use the XSLT format-number() function to display the total for the current purchase order. We've already discussed how we set the value of the variable $orderTotal to be the output of the template named sumItems; once the variable is set, we use format-number to display it with a currency sign, commas, and two decimal places:

<xsl:value-of select="format-number($order-total, '$#,###.00')"/>
Previous | Home | Next
7. Combining XML Documents | Book Index | 7.3. Invoking the document() Function
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Invoking the document() Function (XSLT)

 XSLSearch this book

7.3. Invoking the document() Function

In our previous stylesheet, we used the document() function to select some number of nodes from the original source document (our list of purchase orders), then open those files. There are a number of ways to invoke the document() function; we'll discuss them briefly here.

The most common way to use the document() function is as we just did. We use an XPath expression to describe a node-set; the document() function takes each node in the node-set, converts it to a string, then uses that string as a URI. So, when we passed a node-set containing the filename attributes in the list of purchase orders, each one is used as a URI. If those URIs are relative references (i.e., they don't begin with a protocol like http), the base URI of the stylesheet is used as the base URI for the reference.

If the document() function has two arguments, the second must be a node-set. The first argument is processed as just described, with the difference that the base URI of the first node in the node-set is used as the base URI for any relative URIs. That combination isn't used often, but it's there if you need it.
NOTE: Every node in the XPath source tree is associated with a base URI. When using the document() function, the base URI is important for resolving references to various resources (typically specified with relative links in a file opened with the document() function.

If a given node is an element or processing instruction node, and that node occurs in an external entity, then the base URI for that node is the base URI of the external entity. If an element or processing instruction node does not occur in an external entity, then its base URI is the base URI of the document in which it appears. The base URI of a document node is the base URI of the document itself, and the base URI of an attribute, comment, namespace, or text node is the base URI of that node's parent.

You can also pass a string or any other XPath datatype to the document() function. If we wanted to open a particular resource, we could simply pass the name of the resource:

document('http://www.ibm.com/pricelist.xml')

This action would open this particular resource and process it. Be aware that XSLT processors are required to return an empty node-set if a resource can't be found, but they aren't required to signal an error. XSLT processors also don't have to support any particular protocols (http, ftp, etc.); you have to check the documentation of your XSLT processor to see what protocols are and aren't supported.

A special case occurs when you pass an empty string to the document() function. As we've discussed the various combinations of arguments that can be passed to the function, we've gone over the rules for resolving URIs. When we call document(''), the XSLT processor parses the current stylesheet and returns a single node, the root node of the stylesheet itself. This technique is very useful for processing lookup tables in a stylesheet, something we'll discuss later in this chapter.

Previous | Home | Next
7.2. The document() Function | Book Index | 7.4. More Sophisticated Techniques
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


More Sophisticated Techniques (XSLT)

 XSLSearch this book

7.4. More Sophisticated Techniques

Up to now, we've written a simple XML document that contains references to other XML documents, then we created a stylesheet that combines all those referenced XML documents into a single output document. That's all well and good, but we'll probably want to do more advanced things. For example, it might be useful to generate a document that lists all items ordered by all the customers. It might be useful to sort all the purchase orders by the state to which they were shipped, by the last name of the customer, or to group them by the state to which they were shipped. We'll go through some of these scenarios to illustrate the design challenges we face when generating documents from multiple input files.

7.4.1. The document() Function and Sorting

Our first challenge will be to generate a listing of all purchase orders and sort them by state. This isn't terribly difficult; we'll simply use the <xsl:sort> element in conjunction with the document() function. Here's the heart of our new stylesheet:

<body>
  <h3>Selected Purchase Orders - <i>Sorted by state</i></h3>
  <xsl:for-each
  select="document(/report/po/@filename)/purchase-order/customer/address/state">
    <xsl:sort select="."/>
    <xsl:apply-templates select="ancestor::purchase-order"/>
  </xsl:for-each>
</body>

What makes this process slightly challenging is the fact that we're sorting on one thing (the value of the <state> element), then invoking <xsl:apply-templates> against the <purchase-order> ancestor of the <state> element. We simply used the ancestor:: axis to do this. Figure 7-2 shows our output document, sorted by the value of the <state> element in each purchase order.

Figure 7-2. Another document generated from multiple input files

7.4.2. Implementing Lookup Tables

We mentioned earlier that calling the document() function with an empty string enabled us to access the nodes in the stylesheet itself. We can use this behavior to implement a lookup table. As an example, we'll create a lookup table that replaces an abbreviation such as ME with Maine. We can then use the value from the lookup table as the sort key. More attentive readers might have noticed in our previous example that although the abbreviation MA does indeed sort before the abbreviation ME, a sorted list of the state names themselves would put Maine (abbreviation ME) before Massachusetts (abbreviation MA).

First, we'll create our lookup table. We'll use the fact that a stylesheet can have any element as a top-level element, provided that element is namespace-qualified to distinguish it from the xsl: namespace reserved for stylesheets. Here's the namespace prefix definition and part of the lookup table that uses it:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:states="http://new.usps.com/cgi-bin/uspsbv/scripts/content.jsp?D=10090">

  <states:name abbrev="AL">Alabama</states:name>
  <states:name abbrev="AL">Alabama</states:name>
  <states:name abbrev="AK">Alaska</states:name>
  <states:name abbrev="AS">American Samoa</states:name>
  <!-- Most state abbreviations removed to keep this listing brief... -->
  <states:name abbrev="ME">Maine</states:name>
  <states:name abbrev="MH">Marshall Islands</states:name>
  <states:name abbrev="MD">Maryland</states:name>
  <states:name abbrev="MA">Massachusetts</states:name>

(The namespace mapped to the states prefix is the URL for the official list of state abbreviations from the United States Postal Service.)

To look up values in our table, we'll use the document() function to return the root node of our stylesheet, then we'll look for a <states:name> element with a abbrev attribute that matches the value of the current <state> element in the purchase order we're currently processing. Here's the somewhat convoluted syntax that performs this magic:

<body>
  <h3>Selected Purchase Orders - <i>Sorted by state</i></h3>
  <xsl:for-each
  select="document(/report/po/@filename)/purchase-order/customer/address/state">
    <xsl:sort select="document('')/*/states:name[@abbrev=current()]"/>
    <xsl:apply-templates select="ancestor::purchase-order"/>
  </xsl:for-each>
</body>

Notice that we use the document() function twice; once to open the document referred to by the filename element, and once to open the stylesheet itself. We also need to discuss the XPath expression in the select attribute of the <xsl:sort> element. There are four significant parts to this expression:

document('')
Returns the root node of the current stylesheet.

/*/

Indicates that what follows must be a top-level element of the stylesheet. This syntax starts at the root of the document, then has a single element. The element's name can be anything. For our current stylesheet, we could have written the XPath expression like this:

  select="document('')/xsl:stylesheet/states:name[@abbrev=current()]"

Because the root element of a stylesheet can be either xsl:stylesheet or xsl:transform, it's better to use the asterisk.

states:name
Indicates a name element combined with a namespace prefix that maps to http://new.usps.com/cgi-bin/uspsbv/scripts/content.jsp?D=10090. If we were referencing elements in another document, the prefix wouldn't have to be states; it could be anything, as long as it mapped to the same string.

[@abbrev=current()]
Means that the abbrev attribute of the current <states:name> element has the same value as the current node. We have to use the XSLT current() function here because we want the current node, not the context node. Inside the predicate expression, the current node is the <state> element we process, while the context node is the <states:name> element that contains the abbrev attribute we evaluate.

Figure 7-3 shows the output from the stylesheet with a lookup table.

Figure 7-3. Document generated with a lookup table

Notice that now the purchase orders have been sorted by the actual name of the state referenced in the address, not by the state's abbreviation. Lookup tables are an extremely useful side effect of the way the document('') function works. You could place a lookup table in another file and you could use the document('') function for other purposes, but the technique we've covered here is the most common way to implement lookup tables.

7.4.3. Grouping Across Multiple Documents

Our final task will be to group our collection of purchase orders. We'll create a new listing that groups all the purchase orders by the state to which they were shipped. We'll start by attempting the grouping technique we used earlier.

The most efficient grouping technique we used before was to use the XSLT key() function along with the XPath generate-id() function. We create a key for the nodes we want to index (in this case, the <state> elements), then compare each address we find to the first value returned by the key() function. Here's how we define the key:

<xsl:key name="states"
  match="document(/report/po/@filename)/purchase-order/customer/address"
  use="state"/>

Unfortunately, the match attribute of the <xsl:key> element can't begin with a call to the document() function. Maybe we could try creating a variable that contains all the nodes we want to use, then use that node-set to create the key:

<xsl:variable name="addresses"
  select="document(/report/po/@filename)/purchase-order/customer/address"/>
<xsl:key name="states" match="$addresses" use="state"/>

This doesn't work either; you can't use a variable in the match attribute. Our hopes for a quick solution to this problem are fading quickly. Complicating the problem is the fact that axes won't help, either. Trying to use the preceding:: axis to see if a previous purchase order came from the current state also doesn't work. Consider this example:

<xsl:if test="not(preceding::address[state=$state])">

When we were working with a single document, the preceding:: axis gave us useful information. Because all of the nodes we're working with are now in separate documents, the various axes defined in XPath won't help. When I ask for any nodes in the preceding:: axis, I only get nodes from the current document. We're going to have to roll up our sleeves and do this the hard way.

Now that we're resigned to grouping nodes with brute force, we'll try to make the process as efficient as possible. For performance reasons, we want to avoid having to call the document() function any more than we have to. This won't be pretty, but here's our approach:

  • Use the document() function to retrieve the values of all of the <state> elements. To keep things simple, we'll write these values out to a string, separating them with spaces. We'll also use the <xsl:sort> element to sort the <state> elements; that will save us some time later.

  • Take our string of sorted, space-separated state names (to be precise, they're the values of all the <state> elements) and remove the duplicates. Because things are sorted, I only have to compare two adjacent values. We'll use recursion to handle this.

  • For each item in our string of sorted, space-separated, unique state names, use the document() function to see which purchase orders match the current state.

This certainly isn't efficient; for each unique state, we'll have to call the document() function once for every filename attribute. In other words, if we had 500 purchase orders from 50 unique states, we would have to open each of those 500 documents 51 times, invoking the document() 25,500 times! It's not pretty, but it works.

Retrieving the values of all <state> elements is relatively straightforward. We'll use the technique of creating a variable whose value contains output from an <xsl:for-each> element:

  <xsl:variable name="list-of-states">
    <xsl:for-each
select="document(/report/po/@filename)/purchase-order/customer/address/state">
      <xsl:sort select="document('')/*/states:name[@abbrev=current()]"/>
      <xsl:value-of select="."/><xsl:text> </xsl:text>
    </xsl:for-each>
  </xsl:variable>

This code produces the string "ME MA MA WI" for our current set of purchase orders. Our next step will remove any duplicate values from the list. We'll do this with recursion, using the following algorithm:

  • Call our recursive template with two arguments: the list of states and the name of the last state we found. the first time we invoke this template, the name of the last state will be blank.

  • Break the list of states into two parts: The first state in the list, followed by the remaining states in the list.

  • If the list of states is empty, exit.

    If the first state in the list is different from the last state we found, output the first state and invoke the template on the remaining states on the list.

    If the first state in the list is the same as the last state we found, simply invoke the template on the remaining states on the list.

Again, we use our technique of calling this template inside an <xsl:variable> element to save the list of unique states for later. Here is the <xsl:variable> element, along with the recursive template that removes duplicate state names from the string:

<xsl:variable name="list-of-unique-states">
  <xsl:call-template name="remove-duplicates">
    <xsl:with-param name="list-of-states" select="$list-of-states"/>
    <xsl:with-param name="last-state" select="''"/>
  </xsl:call-template>
</xsl:variable>

<xsl:template name="remove-duplicates">
  <xsl:param name="list-of-states"/>
  <xsl:param name="last-state" select="''"/>
  <xsl:variable name="next-state">
    <xsl:value-of select="substring-before($list-of-states, ' ')"/>
  </xsl:variable>
  <xsl:variable name="remaining-states">
    <xsl:value-of select="substring-after($list-of-states, ' ')"/>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="not(string-length(normalize-space($list-of-states)))">
      <!-- If the list of states is empty, do nothing -->
    </xsl:when>
    <xsl:when test="not($last-state=$next-state)">
      <xsl:value-of select="$next-state"/>
      <xsl:text> </xsl:text>
      <xsl:call-template name="remove-duplicates">
        <xsl:with-param name="list-of-states" select="$remaining-states"/>
        <xsl:with-param name="last-state" select="$next-state"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="$last-state=$next-state">
      <xsl:call-template name="remove-duplicates">
        <xsl:with-param name="list-of-states" select="$remaining-states"/>
        <xsl:with-param name="last-state" select="$next-state"/>
      </xsl:call-template>
    </xsl:when>
  </xsl:choose>
</xsl:template>

At this point, we have a variable named list-of-unique-states that contains the value ME MA WI. Now all we have to do is get each value and output all the purchase orders from each state. We'll use recursion yet again to make this happen. We'll pass our list of unique states to our recursive template, which does the following:

  • Breaks the string into two parts: the first state in the list and the remaining states.

  • Outputs a heading for the first state in the list.

  • Invokes the document() function against each purchase order. If a given purchase order is from the first state in the list, use <xsl:apply-templates> to transform it.

  • Invokes the template again for the remaining states. If no states remain (the value of normalize-space($remaining-states) is an empty string), we're done.

Here is the root template and the recursive template we use to group our data. The result of our hard work looks like Figure 7-4.

Figure 7-4. Document featuring grouped items from multiple input files

<xsl:template match="/">
  <html>
    <head>
      <title><xsl:value-of select="/report/title"/></title>
    </head>
    <body>
      <h3>Selected Purchase Orders - <i><b>Grouped</b> by state</i></h3>
      <xsl:call-template name="group-by-state">
        <xsl:with-param name="list-of-unique-states"
          select="$list-of-unique-states"/>
      </xsl:call-template>
    </body>
  </html>
</xsl:template>

<xsl:template name="group-by-state">
  <xsl:param name="list-of-unique-states"/>
  <xsl:variable name="next-state">
    <xsl:value-of select="substring-before($list-of-unique-states, ' ')"/>
  </xsl:variable>
  <xsl:variable name="remaining-states">
    <xsl:value-of select="substring-after($list-of-unique-states, ' ')"/>
  </xsl:variable>
  <hr/>
  <h1>Purchase Orders from
  <xsl:value-of select="document('')/*/states:name[@abbrev=$next-state]"/>
  </h1>
  <xsl:for-each
    select="document(/report/po/@filename)/purchase-order/customer/address">
    <xsl:if test="state=$next-state">
      <xsl:apply-templates select="ancestor::purchase-order"/>
    </xsl:if>
  </xsl:for-each>
  <xsl:if test="normalize-space($remaining-states)">
    <xsl:call-template name="group-by-state">
      <xsl:with-param name="list-of-unique-states"
        select="$remaining-states"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Previous | Home | Next
7.3. Invoking the document() Function | Book Index | 7.5. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

7.5. Summary

This chapter completes our tour of the document() function. This powerful function allows us to generate an output document containing elements from many different input documents. In our examples here, we generated several views of those input documents, but many more combinations might be useful. The biggest benefit of the document() function is that it allows us to define views of multiple documents that are separate from those documents themselves. As we need to define other views, we don't have to change our input documents. The document() function can save you a tremendous amount of development time in generating reports and other summarizing documents.

Previous | Home | Next
7.4. More Sophisticated Techniques | Book Index | 8. Extending XSLT
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Extending XSLT (XSLT)

 XSLTSearch this book

Chapter 8. Extending XSLT

Contents:

Extension Elements, Extension Functions, and Fallback Processing
Extending the Saxon Processor
More Examples
Summary

To this point, we've spent a lot of time learning how to use the built-in features of XSLT and XPath to get things done. We've also talked about the somewhat unusual processing model that makes life challenging for programmers from the world of procedural languages (a.k.a. Earth). But what do you do if you still can't do everything with XSLT and XPath?

In this section, we'll discuss the XSLT extension mechanism that allows you to add new functions and elements to the language. Unfortunately, Version 1.0 of the XSLT standard doesn't define all of the details about how these things should work, so there are some inconsistencies between processors. The good news is that if you write an extension function or element that works with your favorite processor, another vendor can't do something sinister to prevent your functions or elements from working. On the other hand, if you decide to change XSLT processors, you'll probably have to change your code.

Most examples in this chapter are written for the Xalan processor. We'll discuss how to write stylesheets that can work with multiple processors, and we'll briefly look at the differences between the various APIs supported by those processors. In addition, Xalan comes with extensions written in Java, but you can use other languages, as well. We'll look at extensions written in Jython (formerly JPython), JavaScript, and Jacl.

8.1. Extension Elements, Extension Functions, and Fallback Processing

Section 14 of the XSLT standard defines two kinds of extensions: extension elements and extension functions. Section 15 of the specification defines fallback processing, a way for stylesheets to respond gracefully when extension elements and functions aren't available. We'll talk about these items briefly, then we'll move on to some examples that illustrate the full range of extensions.

8.1.1. Extension Elements

An extension element is an element that should be processed by a piece of code external to the XSLT processor. In the case of the Java version of Xalan, our stylesheet defines the Java class that should be loaded and invoked to process the extension element. Although the implementation details vary from one XSLT processor to the next, we'll discuss how an extension element can access the XPath representation of our source document, how it can generate output, and how it can move through the XPath tree to manipulate the source document.

8.1.1.1. Example: Generating multiple output files

The whole point of extensions is to allow you to add new capabilities to the XSLT processor. One of the most common needs is the ability to generate multiple output documents. As we saw earlier, the document() function allows you to have multiple input documents -- but XSLT doesn't give us any way to create multiple output documents. Xalan, Saxon, and XT all ship with extensions that allow you to create such documents. Here's an XML document that we'll use for several of our examples in this chapter:

<?xml version="1.0"?>
<book>
  <title>XSLT</title>
  <chapter>
    <title>Getting Started</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>The Hello World Example</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>XPath</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Stylesheet Basics</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Branching and Control Elements</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Functions</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Creating Links and Cross-References</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Sorting and Grouping Elements</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Combining XML Documents</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
</book>

For our first example, we want to create a stylesheet that converts the document to HTML, writing the contents of each <chapter> element to a separate HTML file. Here's what that stylesheet looks like:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  extension-element-prefixes="redirect">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:choose>
      <xsl:when test="element-available('redirect:write')">
        <xsl:for-each select="/book/chapter">
          <redirect:write select="concat('chapter', position(), '.html')">
            <html>
              <head>
                <title><xsl:value-of select="title"/></title>
              </head>
              <body>
                <h1><xsl:value-of select="title"/></h1>
                <xsl:apply-templates select="para"/>
                <xsl:if test="not(position()=1)">
                  <p><a href="chapter{position()-1}.html">Previous</a></p>
                </xsl:if>
                <xsl:if test="not(position()=last())">
                  <p><a href="chapter{position()+1}.html">Next</a></p>
                </xsl:if>
              </body>
            </html>
          </redirect:write>
        </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
        <html>
          <head>
            <title><xsl:value-of select="/book/title"/></title>
          </head>
          <xsl:for-each select="/book/chapter">
            <h1><xsl:value-of select="title"/></h1>
            <xsl:apply-templates select="para"/>
          </xsl:for-each>
        </html>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="para">
    <p><xsl:apply-templates select="*|text()"/></p>
  </xsl:template>

</xsl:stylesheet>

Let's go through the relevant parts of this example. To begin with, our <xsl:stylesheet> element defines the redirect namespace prefix and tells the XSLT engine that the prefix will be used to refer to an extension element.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  extension-element-prefixes="redirect">

The syntax of everything we've done so far is according to the standard, although there's a fair amount of latitude in what the XSLT engines do with the information we've defined. For example, when defining the redirect namespace, Xalan uses the value here as a Java class name. In other words, Xalan attempts to load the class org.apache.xalan.xslt.extensions.Redirect when it encounters an extension element or function defined with this namespace. The way other XSLT processors use the namespace URI can vary.

To this point, we've simply defined our extension class so Xalan can find our code, load it, and invoke it. Our next step is to actually use it:

<xsl:when test="element-available('redirect:write')">
  <xsl:for-each select="/book/chapter">
    <redirect:write select="concat('chapter', position(), '.html')">
      <html>
        <head>
          <title><xsl:value-of select="title"/></title>
        </head>
        <body>
          <h1><xsl:value-of select="title"/></h1>
          <xsl:apply-templates select="para"/>
          <xsl:if test="not(position()=1)">
            <p><a href="chapter{position()-1}.html">Previous</a></p>
          </xsl:if>
          <xsl:if test="not(position()=last())">
            <p><a href="chapter{position()+1}.html">Next</a></p>
          </xsl:if>
        </body>
      </html>
    </redirect:write>
  </xsl:for-each>
</xsl:when>

This code does several things:

  • It checks to see if our extension element is available. If it is, we'll use it; if not, the <xsl:otherwise> element will be evaluated instead.

  • For each <chapter> in our XML document, it calls an extension element from our Redirect class. In the example here, we're calling the <redirect:write> element, which opens a file and directs all the output generated by Xalan into that file. Notice that the filename here is generated automatically; the filename for the first <chapter> is chapter1.html, the filename for the second is chapter2.html, etc. This convenient naming convention creates a unique filename for each chapter and makes it easy to figure out which filename contains the output from each chapter.

  • It creates the HTML tags to define the document and its <title>. After creating the <head> section, it creates an <h1> for the chapter title, followed by a <p> generated from each <para> element in the XML source.

  • It generates hyperlinks between the different documents. If a given document is any chapter other than the first (not(position()=1)), it creates a link to the previous chapter. The filename for the previous chapter is generated with the expression chapter{position()-1}.html. If the document is any chapter other than the last (not(position()=last())), it creates a link to the next chapter. The filename for the next chapter is generated with the function call concat('chapter', position()+1, '.html').

    In this example, we used both the curly brace notation and the <xsl:attribute> element. Both work the same way; for the rest of this chapter, we'll use the curly brace notation to save space. (For more information, see the discussion of Section 3.3, "Attribute Value Templates" in Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks".)

  • After any required hyperlinks have been generated, it writes the closing HTML tags and ends the <redirect:write> element. Ending the <redirect:write> element closes the file.

An individual output file looks like Figure 8-1.

Figure 8-1. An individual output file

This particular chapter contains both a Previous and a Next link. The first chapter won't have a Previous link, and the last chapter won't have a Next; other than that, all individual chapters are formatted the same way.

That code covers how we generate multiple output files when the extension element is available. When it isn't available, we simply generate a single HTML file that contains the text of all the chapters:

<xsl:otherwise>
  <html>
    <head>
      <title><xsl:value-of select="/book/title"/></title>
    </head>
    <xsl:for-each select="/book/chapter">
      <h1><xsl:value-of select="title"/></h1>
      <xsl:apply-templates select="para"/>
    </xsl:for-each>
  </html>
</xsl:otherwise>

In the <xsl:otherwise> element, we create a single HTML element, then process each <chapter> in turn. The output is a single large file; not ideal for a really large document, but an acceptable alternative when our extension element isn't available.

In this relatively simple example, we've broken a single XML document into multiple HTML files, we've generated useful filenames for all of them, and we've automatically built hyperlinks between the different HTML files. If we add, delete, or move a chapter, we can simply rerun our stylesheet and all the files and links between them will be updated. For now, we've simply discussed how to use an extension; we'll talk about how to write your own extension later in this chapter.

8.1.1.2. Example: Using extension functions from multiple processors

So far, we've used an extension function to convert a single XML document into multiple HTML files. Unfortunately, our stylesheet only works with the Xalan XSLT processor. How can we write a stylesheet that will work with multiple XSLT processors? The answer is to define more extension elements, one for each processor. Here's a stylesheet that works with Xalan, Saxon, and XT:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  xmlns:saxon="http://icl.com/saxon"
  xmlns:xt="http://www.jclark.com/xt"
  extension-element-prefixes="redirect saxon xt">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:choose>
      <xsl:when test="contains(system-property('xsl:vendor'), 'James Clark')">
        <xsl:for-each select="/book/chapter">
          <xt:document method="xml"
            href="chapter{position()}.html">
            <html>
              <head>
                <title><xsl:value-of select="title"/></title>
              </head>
              <body>
                <h1><xsl:value-of select="title"/></h1>
                <xsl:apply-templates select="para"/>
                <xsl:if test="not(position()=1)">
                  <p><a href="chapter{position()-1}.html">Previous</a></p>
                </xsl:if>
                <xsl:if test="not(position()=last())">
                  <p><a href="chapter{position()+1}.html">Next</a></p>
                </xsl:if>
              </body>
            </html>
          </xt:document>
        </xsl:for-each>
      </xsl:when>
      <xsl:when test="contains(system-property('xsl:vendor'), 'Apache')">
        <xsl:for-each select="/book/chapter">
          <redirect:write select="concat('chapter', position(), '.html')">
            <html>
              <head>
                <title><xsl:value-of select="title"/></title>
              </head>
              <body>
                <h1><xsl:value-of select="title"/></h1>
                <xsl:apply-templates select="para"/>
                <xsl:if test="not(position()=1)">
                  <p><a href="chapter{position()-1}.html">Previous</a></p>
                </xsl:if>
                <xsl:if test="not(position()=last())">
                  <p><a href="chapter{position()+1}.html">Next</a></p>
                </xsl:if>
              </body>
            </html>
          </redirect:write>
        </xsl:for-each>
      </xsl:when>
      <xsl:when test="contains(system-property('xsl:vendor'), 'SAXON')">
        <xsl:for-each select="/book/chapter">
          <saxon:output href="chapter{position()}.html">
            <html>
              <head>
                <title><xsl:value-of select="title"/></title>
              </head>
              <body>
                <h1><xsl:value-of select="title"/></h1>
                <xsl:apply-templates select="para"/>
                <xsl:if test="not(position()=1)">
                  <p><a href="chapter{position()-1}.html">Previous</a></p>
                </xsl:if>
                <xsl:if test="not(position()=last())">
                  <p><a href="chapter{position()+1}.html">Next</a></p>
                </xsl:if>
              </body>
            </html>
          </saxon:output>
        </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
        <html>
          <head>
            <title><xsl:value-of select="/book/title"/></title>
          </head>
          <xsl:for-each select="/book/chapter">
            <h1><xsl:value-of select="title"/></h1>
            <xsl:apply-templates select="para"/>
          </xsl:for-each>
        </html>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="para">
    <p><xsl:apply-templates select="*|text()"/></p>
  </xsl:template>

</xsl:stylesheet>

All we've done here is add more <xsl:when> elements, each of which tries to figure out which XSLT processor we're using. The difference here is that the XT processor doesn't implement the element-available() function, so we can't use it in any stylesheet that will be processed by XT. To get around this problem, we use the system-property() function to get the vendor property. If the vendor contains the string "James Clark," we know that we're using XT. We test the other processors similarly. If we find that we're using an XSLT processor we recognize, we use its extension functions to split the output into multiple HTML files; otherwise, we write all the output to a single file. Obviously, maintenance of this stylesheet is more involved, but it does give us the freedom to switch XSLT processors. (The other downside is that we depend on the value of the vendor system property; if the next release of Saxon identifies the vendor as Saxon instead of SAXON, our stylesheet won't work properly.)

8.1.2. Extension Functions

As you might guess, an extension function is defined in a piece of code external to the XSLT processor. You can pass values to the function, and the function can return a result. That result can be any of the datatypes supported by XPath. In addition, various XSLT processors are free to allow extension functions to return other datatypes, although those other datatypes must be handled by some other function that does return one of XPath's datatypes.

8.1.2.1. Example: A library of trigonometric functions

As we outlined the functions and operators available in XPath and XSLT, you probably noticed that the mathematical functions at your disposal are rather limited. In this example, we'll write an extension that provides a variety of trignometric functions.

Our scenario here is that we want to generate a Scalable Vector Graphics (SVG) pie chart from an XML document. Our XML document contains the sales figures for various regions of a company; we need to calculate the dimensions of the various slices of the pie graph for our SVG document. Here's the XML source we'll be working with:

<?xml version="1.0" ?>
<sales>
  <caption>
    <heading>3Q 2001 Sales Figures</heading>
    <subheading>($ millions)</subheading>
  </caption>
  <region>
    <name>Southeast</name>
    <product name="Heron">38.3</product>
    <product name="Kingfisher">12.7</product>
    <product name="Pelican">6.1</product>
    <product name="Sandpiper">29.9</product>
    <product name="Crane">57.2</product>
  </region>
  <region>
    <name>Northeast</name>
    <product name="Heron">49.7</product>
    <product name="Kingfisher">2.8</product>
    <product name="Pelican">4.8</product>
    <product name="Sandpiper">31.5</product>
    <product name="Crane">60.0</product>
  </region>
  <region>
    <name>Southwest</name>
    <product name="Heron">31.1</product>
    <product name="Kingfisher">9.8</product>
    <product name="Pelican">8.7</product>
    <product name="Sandpiper">34.3</product>
    <product name="Crane">50.4</product>
  </region>
  <region>
    <name>Midwest</name>
    <product name="Heron">44.5</product>
    <product name="Kingfisher">9.3</product>
    <product name="Pelican">5.7</product>
    <product name="Sandpiper">28.8</product>
    <product name="Crane">54.6</product>
  </region>
  <region>
    <name>Northwest</name>
    <product name="Heron">36.6</product>
    <product name="Kingfisher">5.4</product>
    <product name="Pelican">9.1</product>
    <product name="Sandpiper">39.1</product>
    <product name="Crane">58.2</product>
  </region>
</sales>

Our goal is to create an SVG file that looks like that in Figure 8-2.

Figure 8-2. Target SVG file format

To make things really interesting, we'll generate an HTML page that embeds the SVG file. We'll use the Redirect extension we used earlier to generate an HTML file and an SVG file in a single transformation. If we view the HTML page in a web browser, we can use Adobe's SVG plug-in to make the graphic interactive. If we move the mouse over a given slice of the pie, the legend will change to show the sales details for that region of the company. Thus, we'll also have to create all the different legends and generate the JavaScript code to make the various SVG elements visible or hidden in response to mouse events. Figure 8-3 shows how the graphic looks if we move the mouse over the pie slice for the Southwest region.

Figure 8-3. SVG chart changes in response to mouse events

XPath's limited math functions won't allow us to calculate the dimensions of the various arcs that make up the pie chart, so we'll use extension functions to solve this problem. Fortunately for us, Java provides all the basic trigonometric functions we need in the java.lang.Math class. Even better, Xalan makes it easy for us to load this class and execute its methods (such as sin(), cos(), and toRadians()).

We'll go over the relevant details as they appear in the stylesheet. First, we have to declare our namespace prefixes, just as we did when we used an extension element:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  extension-element-prefixes="java redirect"
  xmlns:java="http://xml.apache.org/xslt/java">

We associated the java namespace prefix with the string "http://xml.apache.org/xslt/java"; Xalan uses this string to support extension functions and elements written in Java. Before we use the extension functions to generate the SVG file, we'll take care of the HTML. First, we generate the <head> section of the HTML document, including the JavaScript code we need to make the pie chart interactive:

<xsl:template match="sales">
  <html>
    <head>
      <title>
        <xsl:value-of select="caption/heading"/>
      </title>
      <script language="JavaScript1.2">
        <xsl:comment>
          <xsl:call-template name="js"/>
          <xsl:text>// </xsl:text></xsl:comment>
      </script>
    </head>
    <body>
      <center>
        <embed name="pie" width="650" height="500" src="saleschart.svg"/>
      </center>
    </body>
  </html>

The HTML file we create generates an HTML <title> element from the XML document, calls the named template that generates the JavaScript code, then embeds the SVG file that we'll generate in a minute. The template we use to generate the JavaScript code is worth a closer look. Here's the code:

<xsl:template name="js">
  <xsl:text>
      function suppress_errors ()
      {
        return true;
      }

      function does_element_exist (svg_name, element_name)
      {
        // First, redirect the error handler so that if the SVG plug-in has
        // not yet loaded or is not present, it doesn't cause the browser to
        // issue a JavaScript error.
        var old_error = window.onerror;
        window.onerror = suppress_errors;

        // Now attempt to get the SVG object.
        var svgobj = document.embeds[svg_name].
            getSVGDocument().getElementById(element_name);

        // Reset the error handler to the browser's default handler.
        window.onerror = old_error;

        // Return appropriate value.
        if (svgobj == null)
          return false;
        else
          return true;
      }

      function mouse_over (target_id)
      {
        var svgdoc = document.pie.getSVGDocument();
        var svgobj;
        var svgstyle;

        var detail_name = 'details' + target_id;
        svgobj = svgdoc.getElementById(detail_name);
        if (svgobj != null)
        {
          svgstyle = svgobj.getStyle();
          svgstyle.setProperty ('visibility', 'visible');
        }
  </xsl:text>
  <xsl:for-each select="/sales/region">
    <xsl:text>svgobj = svgdoc.getElementById('legend</xsl:text>
    <xsl:value-of select="position()"/><xsl:text>');</xsl:text>
    <xsl:text>
        if (svgobj != null)
        {
          svgstyle = svgobj.getStyle();
          svgstyle.setProperty ('visibility', 'hidden');
        }
    </xsl:text>
  </xsl:for-each>
  <xsl:text>
        // Propagate the event to other handlers.
        return true;
      }

      function mouse_out ()
      {
        var svgdoc = document.pie.getSVGDocument();
        var svgobj;
        var svgstyle;

  </xsl:text>
  <xsl:for-each select="/sales/region">
    <xsl:text>svgobj = svgdoc.getElementById('legend</xsl:text>
    <xsl:value-of select="position()"/><xsl:text>');</xsl:text>
    <xsl:text>
        if (svgobj != null)
        {
          svgstyle = svgobj.getStyle();
          svgstyle.setProperty ('visibility', 'visible');
        }
    </xsl:text>
    <xsl:text>svgobj = svgdoc.getElementById('details</xsl:text>
    <xsl:value-of select="position()"/><xsl:text>');</xsl:text>
    <xsl:text>
        if (svgobj != null)
        {
          svgstyle = svgobj.getStyle();
          svgstyle.setProperty ('visibility', 'hidden');
        }
    </xsl:text>
  </xsl:for-each>
  <xsl:text>

        // Propagate the event to other handlers.
          return true;
      }
  </xsl:text>
</xsl:template>

We begin with the functions (suppress_errors() and does_element_exist()) that we'll need for error checking and handling. The mouse_over() function is more complicated. When the user moves the mouse over a particular section of the pie, we need to make some SVG elements visible and others invisible. We'll use a naming convention here; for each <region> in our original document, we'll generate a legend entry and a set of details. Originally, the legend is visible and all details are hidden. When our mouse_over() function is called, it makes all the legend elements hidden and makes the appropriate details element visible. Here's how the generated code looks:

function mouse_over (target_id)
{
  var svgdoc = document.pie.getSVGDocument();
  var svgobj;
  var svgstyle;

  var detail_name = 'details' + target_id;
  svgobj = svgdoc.getElementById(detail_name);
  if (svgobj != null)
  {
  svgstyle = svgobj.getStyle();
  svgstyle.setProperty ('visibility', 'visible');
  }
  svgobj = svgdoc.getElementById('legend1');
  if (svgobj != null)
  {
    svgstyle = svgobj.getStyle();
    svgstyle.setProperty ('visibility', 'hidden');
  }

  ...

  // Propagate the event to other handlers.
  return true;
}

The section that begins svgdoc.getElementById('legend1') repeats for each <region> in the XML source document. The repeated code ensures that all legend elements are hidden. This code handles the mouse over event; our final task is to handle the mouse out event. The generated mouse_out() function looks like:

function mouse_out ()
{
  var svgdoc = document.pie.getSVGDocument();
  var svgobj;
  var svgstyle;

  svgobj = svgdoc.getElementById('legend1');
  if (svgobj != null)
  {
    svgstyle = svgobj.getStyle();
    svgstyle.setProperty ('visibility', 'visible');
  }
  svgobj = svgdoc.getElementById('details1');
  if (svgobj != null)
  {
    svgstyle = svgobj.getStyle();
    svgstyle.setProperty ('visibility', 'hidden');
  }

  ...

  // Propagate the event to other handlers.
    return true;
}

The mouse_out() function makes sure that all legend elements are visible and that all details elements are hidden. Although these two functions are relatively simple, they work together to make our pie chart interactive and dynamic.

TIP: One final note about our generated JavaScript code; notice how we invoked the named template inside an <xsl:comment> element:

<script language="JavaScript1.2">
  <xsl:comment>
    <xsl:call-template name="js"/>
    <xsl:text>// </xsl:text></xsl:comment>
</script>

Scripting code is typically written inside a comment in an HTML file, allowing browsers that don't support scripting to safely ignore the code. The end of the generated JavaScript code looks like this:

  // Propagate the event to other handlers.
    return true;
}
    // --></script>

We used an <xsl:text> element to draw the double slash at the end of the script. The double slash is a JavaScript comment, which tells the JavaScript processor to ignore the --> at the end of the script. Without this slash, some JavaScript processors attempt to process the end of the comment and issue an error message. Keep this in mind when you're generating JavaScript code with your stylesheets; if you don't, you'll have trouble tracking down the occasional errors that occur in some browsers.

Now that we've built the HTML file, here's how we draw each wedge of the pie:

  1. Calculate the total sales for the entire company and store it in a variable. Calculating total sales is relatively expensive because the XSLT processor has to navigate the entire tree. We'll need to use this value many times, so we calculate it once and store it away. Here's the calculation for total sales:

    <xsl:variable name="totalSales" select="sum(//product)"/>

  2. For each slice of the pie, we calculate certain values and pass them as parameters to the region template. First, we determine the color of the slice and the total sales for this particular region of the company. We use the position() function and the mod operator to calculate the color, and we use the sum() function to calculate the sales for this region of the company.

  3. If this is the first slice of the pie, we'll explode it. That means that the first slice will be offset from the rest of the pie. We will set the variable $explode as follows:

    <xsl:variable name="explode" select="position()=1"/>

  4. The last value we calculate is the total sales of all previous regions. When we draw each slice of the pie, we rotate the coordinate axis a certain number of degrees. The amount of the rotation depends on how much of the total sales have been drawn so far. In other words, if we've drawn exactly 50 percent of the total sales so far, we'll rotate the axis 180 degrees (50 percent of 360). Rotating the axis simplifies the trigonometry we have to do. To calculate the total sales we've drawn so far, we use the preceding-sibling axis:

    <xsl:with-param name="runningTotal"
  select="sum(preceding-sibling::region/product)"/>

  5. Inside the template itself, our first step is to calculate the angle in radians of the current slice of the pie. This is the first time we use one of our extension functions:

    <xsl:variable name="currentAngle"
  select="java:java.lang.Math.toRadians(($regionSales div
                                         $totalSales) * 360.0)"/>

    We store this value in the variable currentAngle; we'll use this value later with the sin() and cos() functions.

  6. Now we're finally ready to draw the pie slice. We'll do this with an SVG <path> element. Here's what one looks like; we'll discuss what the attributes mean in a minute:

    <path onclick="return false;" onmouseout="mouse_out();" style="fill:orange;
stroke:black; stroke-width:2; fillrule:evenodd; stroke-linejoin:bevel;"
transform="translate(100,160)  rotate(-72.24046757584189)"
onmouseover="return mouse_over(2);"
d="M 80 0 A 80 80 0 0 0 21.318586104178774 -77.10718440274366 L 0 0 Z ">
</path>

    The following stylesheet fragment generated this intimidating element:

    <path style="fill:{$color}; stroke:black; stroke-width:2;
  fillrule:evenodd; stroke-linejoin:bevel;"
  onmouseout="mouse_out();" onclick="return false;">
  <xsl:attribute name="transform">
    <xsl:choose>
      <xsl:when test="$explode">
        <xsl:text>translate(</xsl:text>
        <xsl:value-of
          select="(java:java.lang.Math.cos($currentAngle div 2) * 20) +
                  100"/>
        <xsl:text>,</xsl:text>
        <xsl:value-of
          select="(java:java.lang.Math.sin($currentAngle div 2) * -20) +
                  160"/>
          <xsl:text>) </xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>translate(100,160) </xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:text> rotate(</xsl:text>
    <xsl:value-of
      select="-1 * (($runningTotal div $totalSales) * 360.0)"/>
    <xsl:text>)</xsl:text>
  </xsl:attribute>
  <xsl:attribute name="onmouseover">
    <xsl:text>return mouse_over(</xsl:text>
    <xsl:value-of select="$position"/><xsl:text>);</xsl:text>
  </xsl:attribute>
  <xsl:attribute name="d">
    <xsl:text>M 80 0 A 80 80 0 </xsl:text>
    <xsl:choose>
      <xsl:when test="$currentAngle > 3.14">
        <xsl:text>1 </xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>0 </xsl:text>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:text>0 </xsl:text>
    <xsl:value-of
      select="java:java.lang.Math.cos($currentAngle) * 80"/>
    <xsl:text> </xsl:text>
    <xsl:value-of
      select="java:java.lang.Math.sin($currentAngle) * -80"/>
    <xsl:text> L 0 0 Z </xsl:text>
  </xsl:attribute>
</path>

    The style attribute defines various properties of the path, including the color in which the path should be drawn, with which the path should be filled, etc. With the exception of the color, everything in the style attribute is the same for all slices of the pie. The transform attribute does two things: it moves the center of the coordinate space to a particular point, then it rotates the axes some number of degrees. The center of the coordinate space is moved to a slightly different location if the $explode variable is true. The extent to which the axes are rotated depends on the percentage of total sales represented by the previous regions of the company. Moving the center of the coordinate space and rotating the axes simplifies the math we have to do later.

    That brings us to the gloriously cryptic d attribute. This attribute contains a number of drawing commands; in our previous example, we move the current point to (80,0) (M stands for move), then we draw an elliptical arc (A stands for arc) with various properties. Finally, we draw a line (L stands for line) from the current point (the end of our arc) to the origin, then we use the Z command, which closes the path by drawing a line from wherever we are to wherever we started.

    If you really must know what the properties of the A command are, they are the two radii of the ellipse, the degrees by which the x-axis should be rotated, two parameters called the large-arc-flag and the sweep-flag that determine how the arc is drawn, and the x- and y-coordinates of the end of the arc. In our example here, the two radii of the ellipse are the same (we want the pie to be round, not elliptical). Next is the x-axis rotation, which is 0. After that is the large-arc-flag, which is 1 if this particular slice of the pie is greater than 180 degrees, 0 otherwise. The sweep-flag is 0, and the last two parameters, the x- and y-coordinates of the end point, are calculated. See the SVG specification for more details on the path and shape elements.

  7. Our next task is to draw all of the legends. We'll draw one legend to identify each slice of the pie; after that, we'll create a separate legend for each slice of the pie. Initially, all of the separate legends will be invisible (<g style="visibility:hidden">, in SVG parlance), and the basic legend for the pie will be visible. As we mouse over the various slices of the pie, different legends will become visible or invisible. First, we'll draw the basic legend, using the mode attribute of the <apply-templates> element:

    <xsl:apply-templates select="." mode="legend">
  <xsl:with-param name="color" select="$color"/>
  <xsl:with-param name="regionSales" select="$regionSales"/>
  <xsl:with-param name="y-legend-offset"
    select="90 + (position() * 20)"/>
  <xsl:with-param name="position" select="position()"/>
</xsl:apply-templates>

    When we apply our template, we pass in several parameters, including the color of the box in the legend entry and the y-coordinate offset where the legend entry should be drawn. We call this template once for each <region> element, ensuring that our legend identifies each slice of the pie, regardless of how many slices there are. For each slice, we draw a box filled with the appropriate color, with the name of the region next to it.

  8. Our final task is to draw the details for this region of the company. We'll draw the name of the region in the same color we used for the pie slice, then list all sales figures for the region. Here's what the template looks like:

    <xsl:template match="region" mode="details">
  <xsl:param name="color" select="black"/>
  <xsl:param name="position" select="'0'"/>
  <xsl:param name="y-legend-offset"/>

  <g style="visibility:hidden">
    <xsl:attribute name="id">
      <xsl:text>details</xsl:text><xsl:value-of select="$position"/>
    </xsl:attribute>
    <text style="font-size:14; font-weight:bold;
      text-anchor:start; fill: {$color}" x="220">
      <xsl:attribute name="y">
        <xsl:value-of select="$y-legend-offset"/>
      </xsl:attribute>
      <xsl:value-of select="name"/><xsl:text> Sales:</xsl:text>
    </text>
    <xsl:for-each select="product">
      <text style="font-size:12; text-anchor:start" x="220">
        <xsl:attribute name="y">
          <xsl:value-of select="$y-legend-offset + (position() * 20)"/>
        </xsl:attribute>
        <xsl:value-of select="@name"/>
        <xsl:text>: </xsl:text><xsl:value-of select="."/>
      </text>
    </xsl:for-each>
  </g>
</xsl:template>

    Notice that we draw this item to be invisible (style="visibility:hidden"); we'll use our JavaScript effects to make the various legends and details visible or hidden. In our stylesheet, we draw the title of the current region using the same color we used for the slice of the pie, followed by the sales figures for each product sold in this region.

Here's the complete stylesheet:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  extension-element-prefixes="java redirect"
  xmlns:java="http://xml.apache.org/xslt/java">

  <xsl:output method="html"/>

  <xsl:strip-space elements="*"/>

  <xsl:template name="js">
    <xsl:text>
        function suppress_errors ()
        {
          return true;
        }

        function does_element_exist (svg_name, element_name)
        {
          // First, redirect the error handler so that if the SVG plug-in has
          // not yet loaded or is not present, it doesn't cause the browser to
          // issue a JavaScript error.
          var old_error = window.onerror;
          window.onerror = suppress_errors;

          // Now attempt to get the SVG object.
          var svgobj = document.embeds[svg_name].
              getSVGDocument().getElementById(element_name);

          // Reset the error handler to the browser's default handler.
          window.onerror = old_error;

          // Return appropriate value.
          if (svgobj == null)
            return false;
          else
            return true;
        }

        function mouse_over (target_id)
        {
          var svgdoc = document.pie.getSVGDocument();
          var svgobj;
          var svgstyle;

          var detail_name = 'details' + target_id;
          svgobj = svgdoc.getElementById(detail_name);
          if (svgobj != null)
          {
            svgstyle = svgobj.getStyle();
            svgstyle.setProperty ('visibility', 'visible');
          }
    </xsl:text>
    <xsl:for-each select="/sales/region">
      <xsl:text>svgobj = svgdoc.getElementById('legend</xsl:text>
      <xsl:value-of select="position()"/><xsl:text>');</xsl:text>
      <xsl:text>
          if (svgobj != null)
          {
            svgstyle = svgobj.getStyle();
            svgstyle.setProperty ('visibility', 'hidden');
          }
      </xsl:text>
    </xsl:for-each>
    <xsl:text>
          // Propagate the event to other handlers.
          return true;
        }

        function mouse_out ()
        {
          var svgdoc = document.pie.getSVGDocument();
          var svgobj;
          var svgstyle;

    </xsl:text>
    <xsl:for-each select="/sales/region">
      <xsl:text>svgobj = svgdoc.getElementById('legend</xsl:text>
      <xsl:value-of select="position()"/><xsl:text>');</xsl:text>
      <xsl:text>
          if (svgobj != null)
          {
            svgstyle = svgobj.getStyle();
            svgstyle.setProperty ('visibility', 'visible');
          }
      </xsl:text>
      <xsl:text>svgobj = svgdoc.getElementById('details</xsl:text>
      <xsl:value-of select="position()"/><xsl:text>');</xsl:text>
      <xsl:text>
          if (svgobj != null)
          {
            svgstyle = svgobj.getStyle();
            svgstyle.setProperty ('visibility', 'hidden');
          }
      </xsl:text>
    </xsl:for-each>
    <xsl:text>
          // Propagate the event to other handlers.
            return true;
        }
    </xsl:text>
  </xsl:template>

  <xsl:template match="/">
    <xsl:apply-templates select="sales"/>
  </xsl:template>

  <xsl:template match="sales">
    <html>
      <head>
        <title>
          <xsl:value-of select="caption/heading"/>
        </title>
        <script language="JavaScript1.2">
          <xsl:comment>
            <xsl:call-template name="js"/>
            <xsl:text>// </xsl:text></xsl:comment>
        </script>
      </head>
      <body>
        <center>
          <embed name="pie" width="650" height="500" src="saleschart.svg"/>
        </center>
      </body>
    </html>
    <redirect:write select="concat('sales', 'chart', '.svg')">
      <svg width="450" height="300">
        <text style="font-size:24; text-anchor:middle;
          font-weight:bold" x="130" y="20">
          <xsl:value-of select="caption/heading"/>
        </text>
        <text style="font-size:14; text-anchor:middle" y="40" x="130">
          <xsl:value-of select="caption/subheading"/>
        </text>

        <xsl:variable name="totalSales" select="sum(//product)"/>

        <xsl:for-each select="region">
          <xsl:variable name="regionSales" select="sum(product)"/>
          <xsl:variable name="color">
            <xsl:choose>
              <xsl:when test="(position() mod 6) = 1">
                <xsl:text>red</xsl:text>
              </xsl:when>
              <xsl:when test="(position() mod 6) = 2">
                <xsl:text>orange</xsl:text>
              </xsl:when>
              <xsl:when test="(position() mod 6) = 3">
                <xsl:text>purple</xsl:text>
              </xsl:when>
              <xsl:when test="(position() mod 6) = 4">
                <xsl:text>blue</xsl:text>
              </xsl:when>
              <xsl:when test="(position() mod 6) = 5">
                <xsl:text>green</xsl:text>
              </xsl:when>
              <xsl:otherwise>
                <xsl:text>orange</xsl:text>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:variable>
          <xsl:variable name="explode" select="position()=1"/>

          <xsl:apply-templates select=".">
            <xsl:with-param name="color" select="$color"/>
            <xsl:with-param name="regionSales" select="$regionSales"/>
            <xsl:with-param name="totalSales" select="$totalSales"/>
            <xsl:with-param name="runningTotal"
              select="sum(preceding-sibling::region/product)"/>
            <xsl:with-param name="explode" select="$explode"/>
            <xsl:with-param name="position" select="position()"/>
          </xsl:apply-templates>

          <xsl:apply-templates select="." mode="legend">
            <xsl:with-param name="color" select="$color"/>
            <xsl:with-param name="regionSales" select="$regionSales"/>
            <xsl:with-param name="y-legend-offset"
              select="90 + (position() * 20)"/>
            <xsl:with-param name="position" select="position()"/>
          </xsl:apply-templates>

          <xsl:apply-templates select="." mode="details">
            <xsl:with-param name="color" select="$color"/>
            <xsl:with-param name="position" select="position()"/>
            <xsl:with-param name="y-legend-offset" select="110"/>
          </xsl:apply-templates>

        </xsl:for-each>
      </svg>
    </redirect:write>
  </xsl:template>

  <xsl:template match="region">
    <xsl:param name="color" select="'red'"/>
    <xsl:param name="runningTotal" select="'0'"/>
    <xsl:param name="totalSales" select="'0'"/>
    <xsl:param name="regionSales" select="'0'"/>
    <xsl:param name="explode"/>
    <xsl:param name="position" select="'1'"/>

    <xsl:variable name="currentAngle"
      select="java:java.lang.Math.toRadians(($regionSales div
                                             $totalSales) * 360.0)"/>

    <path style="fill:{$color}; stroke:black; stroke-width:2;
      fillrule:evenodd; stroke-linejoin:bevel;"
      onmouseout="mouse_out();" onclick="return false;">
      <xsl:attribute name="transform">
        <xsl:choose>
          <xsl:when test="$explode">
            <xsl:text>translate(</xsl:text>
            <xsl:value-of
              select="(java:java.lang.Math.cos($currentAngle div 2) * 20) +
                      100"/>
            <xsl:text>,</xsl:text>
            <xsl:value-of
              select="(java:java.lang.Math.sin($currentAngle div 2) * -20) +
                      160"/>
              <xsl:text>) </xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>translate(100,160) </xsl:text>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:text> rotate(</xsl:text>
        <xsl:value-of select="-1 * (($runningTotal div $totalSales) * 360.0)"/>
        <xsl:text>)</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="onmouseover">
        <xsl:text>return mouse_over(</xsl:text>
        <xsl:value-of select="$position"/><xsl:text>);</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="d">
        <xsl:text>M 80 0 A 80 80 0 </xsl:text>
        <xsl:choose>
          <xsl:when test="$currentAngle > 3.14">
            <xsl:text>1 </xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>0 </xsl:text>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:text>0 </xsl:text>
        <xsl:value-of select="java:java.lang.Math.cos($currentAngle) * 80"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="java:java.lang.Math.sin($currentAngle) * -80"/>
        <xsl:text> L 0 0 Z </xsl:text>
      </xsl:attribute>
    </path>
  </xsl:template>

  <xsl:template match="region" mode="legend">
    <xsl:param name="color" select="'red'"/>
    <xsl:param name="regionSales" select="'0'"/>
    <xsl:param name="y-legend-offset" select="'0'"/>
    <xsl:param name="position" select="'1'"/>

    <g>
      <xsl:attribute name="id">
        <xsl:text>legend</xsl:text><xsl:value-of select="$position"/>
      </xsl:attribute>

      <text>
        <xsl:attribute name="style">
          <xsl:text>font-size:12; text-anchor:start</xsl:text>
        </xsl:attribute>
        <xsl:attribute name="x">
          <xsl:text>240</xsl:text>
        </xsl:attribute>
        <xsl:attribute name="y">
          <xsl:value-of select="$y-legend-offset"/>
        </xsl:attribute>
        <xsl:value-of select="name"/>
        <xsl:text> (</xsl:text>
        <xsl:value-of select="$regionSales"/>
        <xsl:text>) </xsl:text>
      </text>

      <path>
        <xsl:attribute name="style">
          <xsl:text>stroke:black; stroke-width:2; fill:</xsl:text>
          <xsl:value-of select="$color"/>
        </xsl:attribute>
        <xsl:attribute name="d">
          <xsl:text>M 220 </xsl:text>
          <xsl:value-of select="$y-legend-offset - 10"/>
          <xsl:text> L 220 </xsl:text>
          <xsl:value-of select="$y-legend-offset"/>
          <xsl:text> L 230 </xsl:text>
          <xsl:value-of select="$y-legend-offset"/>
          <xsl:text> L 230 </xsl:text>
          <xsl:value-of select="$y-legend-offset - 10"/>
          <xsl:text> Z</xsl:text>
        </xsl:attribute>
      </path>
    </g>
  </xsl:template>

  <xsl:template match="region" mode="details">
    <xsl:param name="color" select="black"/>
    <xsl:param name="position" select="'0'"/>
    <xsl:param name="y-legend-offset"/>

    <g style="visibility:hidden">
      <xsl:attribute name="id">
        <xsl:text>details</xsl:text><xsl:value-of select="$position"/>
      </xsl:attribute>
      <text style="font-size:14; font-weight:bold;
        text-anchor:start; fill: {$color}" x="220">
        <xsl:attribute name="y">
          <xsl:value-of select="$y-legend-offset"/>
        </xsl:attribute>
        <xsl:value-of select="name"/><xsl:text> Sales:</xsl:text>
      </text>
      <xsl:for-each select="product">
        <text style="font-size:12; text-anchor:start" x="220">
          <xsl:attribute name="y">
            <xsl:value-of select="$y-legend-offset + (position() * 20)"/>
          </xsl:attribute>
          <xsl:value-of select="@name"/>
          <xsl:text>: </xsl:text><xsl:value-of select="."/>
        </text>
      </xsl:for-each>
    </g>
  </xsl:template>

</xsl:stylesheet>

In this example, we've used XSLT extension functions to add new capabilities to the XSLT processor. We needed a couple of simple trigonometric functions, and Xalan's ability to use existing Java classes made adding new capabilities simple. You can use this technique to invoke methods of Java classes anywhere you need them. Best of all, we didn't have to write any Java code to make this happen.

8.1.2.2. Example: Writing extensions in other languages

One of the nice features of Xalan's extension mechanism is that it uses the Bean Scripting Framework (BSF), an open source library from IBM that allows you to execute code written in a variety of scripting languages. We'll take the HTML/SVG stylesheet we just discussed and implement it again, writing the extension functions in Jython.

NOTE: Other languages supported by the Bean Scripting Framework include NetRexx, PerlScript, Jacl, Tcl, VBScript, and pnuts. If you're using a Microsoft platform, BSF also supports Windows Script Technologies, so you may have even more choices if you're running some flavor of Windows.

As you would expect, we must do several things to identify our extension code to Xalan. We'll cover them, and then look at the source of the various extension functions. First we need to define the namespace prefixes we'll use:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:jython-extension="http://www.jython.org/"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  extension-element-prefixes="redirect"
  xmlns:lxslt="http://xml.apache.org/xslt"
  exclude-result-prefixes="lxslt">

We still need the Redirect class, so that prefix is still with us. The other two prefixes are jython-extension, associated with the URL of the Jython home page (though the value could be anything), and lxslt. Xalan uses this prefix to implement scripting languages. Our next step is to actually write the Jython code. With Xalan, this code goes inside an <lxslt:component> element:

  <lxslt:component prefix="jython-extension" functions="cos sin toRadians">
    <lxslt:script lang="jpython">
import math

def cos(d):
  return math.cos(d)

def sin(d):
  return math.sin(d)

def toRadians(d):
  return d / 180 * math.pi
    </lxslt:script>
  </lxslt:component>

The prefix attribute associates this <lxslt:component> with the jython-extension prefix, and the functions attribute lists all of the functions supported by this script. The <lxslt:script lang="jpython"> tells Xalan to use the Jython interpreter (the current version of BSF requires us to use lang="jpython", the language's former name) whenever these functions are invoked. Now that we've set everything up, all we have to do is invoke the extension functions:

<xsl:variable name="currentAngle"
  select="jython-extension:toRadians(($regionSales div
                                      $totalSales) * 360.0)"/>

Other than the jython-extension extension before the function call, the rest of our stylesheet is exactly the same. Notice that the Python math library does not define a toRadians function, so we had to define that function ourselves. The other two functions are part of the library, so all we had to do was invoke them.

One final point: when we invoke these extension functions written in other languages, the Java CLASSPATH must be set up correctly. If the class libraries for Jython or Javascript or whatever scripting language you're using can't be found, the extension functions will fail. Our example here uses jython.jar, available at http://www.jython.org.

We promised we'd look at extensions in JavaScript, as well. Here's how the <lxslt:component> element looks when we write the extension functions in JavaScript:

<lxslt:component prefix="javascript-extension" functions="cos sin toRadians">
  <lxslt:script lang="javascript">
    function cos(d)
    {
      return Math.cos(d);
    }

    function sin(d)
    {
      return Math.sin(d);
    }

    function toRadians(d)
    {
      return d * Math.PI / 180;
    }
  </lxslt:script>
</lxslt:component>

Here is the <lxslt:component> element with the extension functions written in Jacl:

<lxslt:component prefix="jacl-extension"
 functions="cosine sine toRadians">
 <lxslt:script lang="jacl">
  proc cosine {d} {expr cos($d)}
  proc sine {d} {expr sin($d)}
  proc toRadians {d} {expr $d * 3.1415926535897932384626433832795 / 180.0}
 </lxslt:script>
</lxslt:component>

Again, most of our task is to use existing features of the language. In the JavaScript and Jacl code, the cos() and sin() functions are part of the language, and we wrote our own versions of the toRadians() function. Jacl doesn't define a constant for pi, so we hardcoded the first 32 digits into the Jacl version of toRadians().

8.1.3. Fallback Processing

If the code that implements a given extension element can't be found, we need some relatively graceful way for the stylesheet to handle the situation. XSLT defines the <xsl:fallback> element to handle this case. In an earlier stylesheet, we used the element-available() function to determine whether a given function is available. In this case, we'll use the <xsl:fallback> to transform our document if the Redirect extension can't be found:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  extension-element-prefixes="redirect">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:for-each select="/book/chapter">
      <redirect:write
        select="concat('chapter', position(), '.html')">
        <html>
          <head>
            <title><xsl:value-of select="title"/></title>
          </head>
          <body>
            <h1><xsl:value-of select="title"/></h1>
            <xsl:apply-templates select="para"/>
            <xsl:if test="not(position()=1)">
              <p><a href="chapter{position()-1}.html">Previous</a></p>
            </xsl:if>
            <xsl:if test="not(position()=last())">
              <p>
                <a>
                  <xsl:attribute name="href">

                    <xsl:value-of
                      select="concat('chapter', position()+1, '.html')"/>
                  </xsl:attribute>
                  Next
                </a>
              </p>
            </xsl:if>
          </body>
        </html>
        <xsl:fallback>
          <xsl:if test="position()=1">
            <html>
              <head>
                <title><xsl:value-of select="/book/title"/></title>
              </head>
              <body>
                <xsl:for-each select="/book/chapter">
                  <h1><xsl:value-of select="title"/></h1>
                  <xsl:apply-templates select="para"/>
                </xsl:for-each>
              </body>
            </html>
          </xsl:if>
        </xsl:fallback>
      </redirect:write>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="para">
    <p><xsl:apply-templates select="*|text()"/></p>
  </xsl:template>

</xsl:stylesheet>

In our example, we only invoke the fallback processing once. This approach assumes that if something's wrong with the extension, it will fail the first time and be completely inaccessible. Using <xsl:fallback>, we know that the contents of the <xsl:fallback> element will be invoked if anything goes wrong when the stylesheet processor attempts to use an extension element. If you'd like more complete control over fallback processing, you can use the element-available() and function-available() functions as we did in our earlier example.

Previous | Home | Next
7.5. Summary | Book Index | 8.2. Extending the Saxon Processor
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Extending the Saxon Processor (XSLT)

 XSLSearch this book

8.2. Extending the Saxon Processor

Michael Kay's excellent Saxon processor also provides an extension mechanism. One of the nice features of Saxon's extensibility mechanism is that you can implement your own sort functions. When we discussed the <xsl:sort> element a couple of chapters ago, we mentioned that it has a lang attribute that defines the language of the things being sorted. While Xalan doesn't currently support this attribute (although by the time you're reading this, it might), Saxon lets you create your own extension function to handle the sorting. Your extension function must extend the com.icl.saxon.sort.TextComparer class. Here's a sample XML document we'll use to illustrate this function:

<?xml version="1.0"?>
<wordlist>
  <word>campo</word>
  <word>luna</word>
  <word>ciudad</word>
  <word>llaves</word>
  <word>chihuahua</word>
  <word>arroz</word>
  <word>limonada</word>
</wordlist>

This document contains Spanish words that are sorted differently than they would be in English. (In Spanish, "ch" and "ll" are separate letters that sort after "c" and "l," respectively.) We'll write a stylesheet that uses three <xsl:template>s to illustrate how our extension function works. Here's the stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" indent="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:apply-templates select="wordlist" mode="unsorted"/>
    <xsl:apply-templates select="wordlist" mode="default"/>
    <xsl:apply-templates select="wordlist" mode="Spanish"/>
  </xsl:template>

  <xsl:template match="wordlist" mode="unsorted">
    <xsl:text>Word list - unsorted:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="word">
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="wordlist" mode="default">
    <xsl:text>Word list - sorted with default rules:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="word">
      <xsl:sort select="."/>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="wordlist" mode="Spanish">
    <xsl:text>Word list - sorted with Spanish rules:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="word">
      <xsl:sort select="." lang="es"/>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>
</xsl:stylesheet>

When we run the stylesheet against our document, it invokes the three templates with three different modes. One template simply lists the <word> elements as they appear in the original document, the second sorts the <word> elements using the default sorting sequence, and the third sorts the <word> elements using the traditional rules of Spanish sorting. Refreshingly enough, the code that implements the sorting function is simple. Here's the entire listing:

package com.icl.saxon.sort;

import java.text.ParseException;
import java.text.RuleBasedCollator;
import java.util.Locale;

public class Compare_es extends TextComparer
{
  private static String smallnTilde  = new String("\u00F1");
  private static String capitalNTilde = new String("\u00D1");

  private static String traditionalSpanishRules =
    ("< a,A < b,B < c,C " +
     "< ch, cH, Ch, CH "  +
     "< d,D < e,E < f,F " +
     "< g,G < h,H < i,I < j,J < k,K < l,L " +
     "< ll, lL, Ll, LL "  +
     "< m,M < n,N " +
     "< " + smallnTilde + "," + capitalNTilde + " " +
     "< o,O < p,P < q,Q < r,R " +
     "< s,S < t,T < u,U < v,V < w,W < x,X " +
     "< y,Y < z,Z");

  private static RuleBasedCollator rbc = null;

  static
  {
    try
    {
      rbc = new RuleBasedCollator(traditionalSpanishRules);
    }
    catch (ParseException pe)
    {
      System.err.println("Error creating RuleBasedCollator: " + rbc);
    }
  }

  public int compare(Object a, Object b)
  {
    if (rbc != null)
      return rbc.compare((String)a, (String)b);
    else
      return 0;
  }
}

(See the documentation for the java.text.RuleBasedCollator class for an explanation of the traditionalSpanishRules string.)

When Saxon sees an <xsl:sort> element with a lang attribute of es, it attempts to load a Java class named com.icl.saxon.sort.Compare_es. If that class can be loaded, Saxon calls that class's compare method as it sorts the <word> elements. When we run the stylesheet against our earlier example document, here are the results:

Word list - unsorted:
campo
luna
ciudad
llaves
chihuahua
arroz
limonada

Word list - sorted with default rules:
arroz
campo
chihuahua
ciudad
limonada
llaves
luna

Word list - sorted with Spanish rules:
arroz
campo
ciudad
chihuahua
limonada
luna
llaves

In the output, our Spanish sorting routine puts chihuahua after ciudad, and llaves after luna. With less than 20 lines of code, we've been able to add a new sorting function to our stylesheet. Most of the work is done for us by the Saxon processor and the methods of the java.text.RuleBasedCollator class.

The Saxon documentation has more information on extending Saxon with your own code. As you'll see in the examples in this chapter, most of the Java extensions you'll need to write will be simple pieces of code that simply make Java library methods and classes available to the XSLT processor.

Previous | Home | Next
8. Extending XSLT | Book Index | 8.3. More Examples
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


More Examples (XSLT)

 XSLSearch this book

8.3. More Examples

You can use XSLT extension mechanisms to push XSLT processing beyond text or markup generation and to read information from non-XML sources.

8.3.1. Generating JPEG Files from XML Content

When converting XML content into HTML files for a web site, there are times when you want to have complete control over the look of a piece of text. In this example, we'll use an extension function to convert the text of an XML element into a JPEG graphic. Our code will load a JPEG background graphic, draw the text from the XML document on top of it, and then write the graphic out to a new JPEG file. We'll reuse the XML file from our first example to demonstrate the extension function.

Our stylesheet passes each <title> element to the extension function. When we invoke the extension, we'll also pass in the name of the background JPEG, the name of the output file (which we'll call title1.jpg, title2.jpg, etc.), and various information about the font name, font size, and other parameters. Here's what our stylesheet looks like:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:jpeg="xalan://JPEGWriter"
  extension-element-prefixes="jpeg">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="/book/title"/>
        </title>
      </head>
      <body>
        <xsl:for-each select="/book/chapter">
          <xsl:choose>
            <xsl:when test="function-available('jpeg:createJPEG')">
              <xsl:value-of
                select="jpeg:createJPEG(title, 'bg.jpg',
                concat('title', position(), '.jpg'),
                'Swiss 721 Bold Condensed', 'BOLD', 22, 52, 35)"/>
              <img>
                <xsl:attribute name="src">
                  <xsl:value-of select="concat('title', position(), '.jpg')"/>
                </xsl:attribute>
              </img>
              <br />
            </xsl:when>
            <xsl:otherwise>
              <h1><xsl:value-of select="title"/></h1>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Our background JPEG looks like Figure 8-4.

Figure 8-4. Background JPEG image

Figure 8-5 shows a couple of graphics created by the combination of our XML sample document, our stylesheet, and the extension function.

Figure 8-5. Generated JPEG files for XML <title> elements

These files are title1.jpg and title8.jpg, respectively. Our extension function has taken the text of the appropriate <title> elements, drawn it on top of the background graphic, then written the new image out as a JPEG graphic.

Let's take a look at the call to our extension function:

<xsl:value-of
  select="jpeg:createJPEG(title, 'bg.jpg',
  concat('title', position(), '.jpg'),
  'Swiss 721 Bold Condensed', 'BOLD', 22, 52, 35)"/>

First of all, look at the call itself. What we've written here is jpeg:createJPEG as the name of the function. The namespace prefix jpeg is defined in the stylesheet. We associated this prefix with the string xalan://JPEGWriter; this string tells Xalan that any function invoked with this prefix should be treated as a method of the named class JPEGWriter. If you use an XSLT processor other than Xalan, the way you define and invoke an extension function will probably vary.

Next, let's look at the parameters to the function. We're passing eight different parameters:

  • The text that should be written in the JPEG image. This text is passed in as a NodeList, one of the datatypes available to us in the Xalan API. In our previous example, we're selecting all <title> elements contained in the current node.

  • The filename of the background image that should be used. This filename is passed in as a String.

  • The filename of the created JPEG image. The image will be created, then written out to this filename. Notice that in our example, we generate the filename by concatenating the string "title", the position of the current node, and the string ".jpg". This procedure ensures that all our title graphics have unique filenames. It also makes it easy to determine which JPEG matches a given <title> element.

  • The name of the font we want to use. This name is a String.

  • The font style we want to use. We've written our function to accept three different values: BOLD, ITALIC, and BOLDITALIC. These values mirror the three values used by the Java Font class.

  • The point size of the font. Notice that this font size is passed to our extension function as a Java Double; XPath and XSLT do not define an Integer type. The first thing our function does is convert the Double values into ints to simplify our arithmetic instructions.

  • The x-offset where the text should begin. We're using a Java Canvas object, whose coordinate system begins in the upper left corner. The value of x-offset determines where we should start drawing the text on the background JPEG. As with the font size, this value is a Double that we convert to an int.

  • The y-offset where the text should begin.

You could certainly modify this function to support other options, such as the color of the text, the depth of the shadow effects on the text, the location of the shadow, etc. You could also create different versions of the function with different method signatures, allowing some calls to the createJPEG function to default certain parameters. The benefit of this approach is that you can access a wide range of behaviors in your extension function by changing your XSLT stylesheet.

Here's the code for the extension function itself:

import com.sun.image.codec.jpeg.ImageFormatException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.xpath.objects.XNodeSet;
import org.w3c.dom.NodeList;

public class JPEGWriter
{
  public static void createJPEG(NodeList nodes, String backgroundFilename,
                                String outputFilename, String fontName,
                                String fontAttributes, Double dFontSize,
                                Double dXOffset, Double dYOffset)
    throws IOException, FileNotFoundException, ImageFormatException
  {
    try
    {
      int fontSize = dFontSize.intValue();
      int xOffset = dXOffset.intValue();
      int yOffset = dYOffset.intValue();

      String jpegText = (new XNodeSet(nodes.item(1))).str();
      FileInputStream fis = new FileInputStream(backgroundFilename);
      JPEGImageDecoder northDecoder = JPEGCodec.createJPEGDecoder(fis);
      BufferedImage bi = northDecoder.decodeAsBufferedImage();

      int fa = Font.PLAIN;
      if (fontAttributes.equalsIgnoreCase("BOLD"))
        fa = Font.BOLD;
      else if (fontAttributes.equalsIgnoreCase("ITALIC"))
         fa = Font.ITALIC;
      else if (fontAttributes.equalsIgnoreCase("BOLDITALIC"))
        fa = Font.BOLD & Font.ITALIC;

      Graphics2D g = bi.createGraphics();

      int maxTextWidth = bi.getWidth() - xOffset - 5;
      GraphicsEnvironment ge = GraphicsEnvironment.
                               getLocalGraphicsEnvironment();
      Font allFonts[] = ge.getAllFonts();
      Font chosenFont = new Font("Arial", fa, fontSize);
      int i = 0;
      boolean fontNotFound = true;
      while (fontNotFound && (i < allFonts.length))
      {
        if (allFonts[i].getFontName().equalsIgnoreCase(fontName))
        {
          chosenFont = allFonts[i].deriveFont(fa, fontSize);
          if (!chosenFont.getFontName().equalsIgnoreCase(fontName))
          {
            fa = Font.PLAIN;
            chosenFont = allFonts[i].deriveFont(fa, fontSize);
          }
          g.setFont(chosenFont);
          FontMetrics fm = g.getFontMetrics();
          int textWidth = fm.stringWidth(jpegText);
          while (textWidth > maxTextWidth && fontSize > 1)
          {
            fontSize -= 2;
            chosenFont = allFonts[i].deriveFont(fa, fontSize);
            g.setFont(chosenFont);
            fm = g.getFontMetrics();
            textWidth = fm.stringWidth(jpegText);
          }
          if (fontSize < 1)
            chosenFont = allFonts[i].deriveFont(fa, 12);

          g.setFont(chosenFont);
          fontNotFound = false;
        }
        else
          i++;
      }

      g.setColor(Color.black);
      g.drawString(jpegText, xOffset, yOffset);
      g.setColor(Color.gray);
      g.drawString(jpegText, xOffset - 1, yOffset - 1);
      FileOutputStream fos = new FileOutputStream(outputFilename);
      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(fos);
      encoder.encode(bi);
      fos.flush();
      fos.close();
    }
    catch (FileNotFoundException fnfe)
    {
      System.err.println(fnfe);
    }
    catch (IOException ioe)
    {
      System.err.println(ioe);
    }
  }
}

Notice that we use a while loop to check the font size. If drawing the text string in the current font size won't fit inside the graphic, we'll try to reduce the font size until it does. Given this <chapter> element:

<chapter>
  <title>A chapter in which the title is so very long, most people
    don't bother reading it</title>
  <para>If this chapter had any text, it would appear here.</para>
</chapter>

Our extension generates the JPEG shown in Figure 8-6.

Figure 8-6. Generated image with too much text

8.3.2. Accessing a Database with an Extension Element

In this example, we're going to build an extension element rather than an extension function. When we wrote our extension functions, all we had to worry about was the data passed to us on the function call. We weren't concerned with the document tree, the context, or anything else. With an extension element, though, we have to be much more aware of the document as a whole. Our code will use the attributes of the extension element to connect to a database, run a query, and then return the result set as a node set (specifically, a Xalan XNodeSet). That node-set will be inserted into the output tree, giving us the capability to build a document dynamically. Our XML document defines the parameters for the database access and the query, then the extension element does the work in the background magically. Here's how the XML document looks:

<?xml version="1.0"?>
<report>
  <title>HR employee listing</title>
  <section>
    <title>Employees by department</title>
    <dbaccess driver="COM.ibm.db2.jdbc.app.DB2Driver"
      database="jdbc:db2:sample" tablename="employee" where="*"
      fieldnames='workdept as "Department", lastname as "Last Name",
                  firstnme as "First Name"'
      order-by="workdept" group-by="workdept, lastname, firstnme"/>
  </section>
</report>

Notice that all of the vendor-specific information about our database connection is contained in the attributes of our XML document. That means we can use our extension element with any JDBC-compliant database. The following documents work just as well:

<?xml version="1.0"?>
<report>
  <title>Sales Results</title>
  <section>
    <title>Top sales people - 3Q 2001</title>
    <dbaccess driver="com.sybase.jdbc2.jdbc.SybDriver""
      database="jdbc:sybase:Tds:localhost:5000/sales"
      tablename="results" where="*"
      fieldnames='lastname as "Last Name",
      firstnme as "First Name", sum(order) as "Totals"'
      order-by="sum(order)" />
  </section>
</report>

<?xml version="1.0"?>
<report>
  <title>Pets We Own</title>
  <section>
    <title>Our pets</title>
    <dbaccess driver="org.gjt.mm.mysql.Driver"
      database="jdbc:mysql://localhost/test" tablename="pet" where="*"
      fieldnames='name as "Pet Name", species as "Species", sex as "M/F"'/>
  </section>
</report>

The first listing uses DB2, the second uses Sybase, and the final listing uses MySQL. Our stylesheet uses our database-accessing extension element to replace the <dbaccess> elements with HTML tables filled with the results of our database query. In our sample document, the XML input closely mirrors the SQL statements we'll use to interact with the database. Our extension element takes the elements and attributes of the <dbaccess> element, gets data out of the database, then formats it accordingly.

The stylesheet that invokes our extension element looks like this:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:db="DatabaseExtension"
  extension-element-prefixes="db">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="report/title"/>
        </title>
      </head>
      <body>
        <h1>
          <xsl:value-of select="report/title"/>
        </h1>
        <xsl:for-each select="report/section">
          <h2>
            <xsl:value-of select="title"/>
          </h2>
          <xsl:for-each select="dbaccess">
            <db:accessDatabase/>
          </xsl:for-each>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

The stylesheet is pretty straightforward. The namespace declaration xmlns:db="xalan://DatabaseExtension" associates the Java class DatabaseExtension with the namespace prefix db. Whenever our stylesheet processes an XML element with a namespace prefix of db, our code is invoked to do the processing. Notice that in our stylesheet, we used the extension element <db:accessDatabase>; this tells Xalan to invoke the accessDatabase() method of the DatabaseExtension class.

In this example, we want our extension element to look at the various attributes of the <dbaccess> element, build a SQL query from the information it finds there, connect to the requested database, and put in the result tree elements that represent the database query results. To keep our example simple, we'll have our extension element return those results in an HTML <table> element; you could write the extension element to generate other types of output, if you wanted. Our extension element returns an XNodeSet; the nodes in the returned XNodeSet are added to the result tree.

For our extension element to work, it has to do several things:

  • Find the <dbaccess> element we need to process.

  • Use the driver attribute of the <dbaccess> element to determine what JDBC driver to use. Once we have this value, we need to load the driver. Specifying the database driver allows us to use different databases in the same XML document. In our previous sample XML files, the three queries specify databases managed by DB2, Sybase, and MySQL; because JDBC itself is vendor-neutral, we can use our extension element with any JDBC-compliant database.

  • Examine the tablename, where, fieldnames, group-by, and order-by attributes of the <dbaccess> element to build the SQL query statement.

  • Connect to the database specified by the tablename attribute of the <dbaccess> element.

  • Execute the query statement.

  • Build the table based on the items in the JDBC ResultSet object. To build the table, we have to get a DOM Document object; we'll use that object as a factory method to create all the nodes in the node-set our extension element returns. We'll create a <table> element, then for each row in the result set, we'll create a <tr> element (with the appropriate <td> elements as its children) and append it to the table. For Xalan, we use the DOMHelper class to get the Document object that we'll use to create all nodes.

  • Return the result set. We create an XNodeSet, attach our <table> element (with all its children) to it, then return it. This result is added automatically to the output document.

Now that we've said what we're going to do, let's take a look at the code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import org.apache.xalan.extensions.XSLProcessorContext;
import org.apache.xalan.templates.ElemExtensionCall;
import org.apache.xpath.DOMHelper;
import org.apache.xpath.objects.XNodeSet;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class DatabaseExtension
{
   private static boolean getDriver(String driverName)
   {
     boolean gotTheDriver = false;
     try
     {
       Class.forName(driverName);
       gotTheDriver = true;
     }
     catch (Exception e)
     {
       System.out.println("Can't load the database driver " + driverName);
       e.printStackTrace();
     }
     return gotTheDriver;
   }

   public static XNodeSet accessDatabase(XSLProcessorContext context,
                                         ElemExtensionCall elem)
   {
     XNodeSet dbResult = null;
     DOMHelper dh = new DOMHelper();
     Document doc = dh.getDOMFactory();
     Element table = null, header = null, tr = null, td = null, th = null;

     Element contextNode = (Element) context.getContextNode();
     if (getDriver(contextNode.getAttribute("driver")))
     {
       try
       {
         StringBuffer query = new StringBuffer("select ");
         query.append(contextNode.getAttribute("fieldnames") + " ");
         query.append("from " + contextNode.getAttribute("tablename") + " ");
         String nextAttr = contextNode.getAttribute("group-by");
         if (nextAttr != null)
           query.append(" group by " + nextAttr);
         nextAttr = contextNode.getAttribute("order-by");
         if (nextAttr != null)
           query.append(" order by " + nextAttr);

         Connection con = DriverManager.
           getConnection(contextNode.getAttribute("database"));
         Statement stmt = con.createStatement();
         ResultSet rs = stmt.executeQuery(query.toString());

         ResultSetMetaData rsmd = rs.getMetaData();
         int columnCount = rsmd.getColumnCount();
         table = doc.createElement("table");
         table.setAttribute("border", "1");

         header = doc.createElement("tr");
         for (int i = 1; i <= columnCount; i++)

         {
           th = doc.createElement("th");
           th.appendChild(doc.createTextNode(rsmd.getColumnName(i)));
           header.appendChild(th);
         }
         table.appendChild(header);

         while (rs.next())
         {
           tr = doc.createElement("tr");
           for (int i = 1; i <= columnCount; i++)
           {
             td = doc.createElement("td");
             td.appendChild(doc.createTextNode(rs.getString(i)));
             tr.appendChild(td);
           }
           table.appendChild(tr);
         }
         dbResult = new XNodeSet(table);
         rs.close();
         stmt.close();
         con.close();
       }
       catch (java.sql.SQLException sqle)
       {
         System.out.println("Exception: " + sqle);
       }
     }
     else
       System.out.println("Couldn't load the driver!");

     return dbResult;
   }
}

Extension elements in Xalan are called with two arguments: an XSLProcessorContext object and an ElemExtensionCall object. In our code here, we'll use the XSLProcessorContext object to get the context node. Once we have the context node (the <dbaccess> element), we can get the values of the various <dbaccess> element attributes in the source tree.

The first thing we do in our extension element is declare the XNodeSet we're going to return to Xalan. After that, we create a DOMHelper object and use the getDOMFactory method to create the DOM Document object we'll use as a factory for creating new nodes:

XNodeSet dbResult = null;
DOMHelper dh = new DOMHelper();
Document doc = dh.getDOMFactory();

Our next task is to instantiate the JDBC driver. To make our code more flexible, we specify the driver in the driver attribute of the <dbaccess> element. In the previous XML examples, we used drivers for MySQL, Sybase, and DB2. Assuming everything to this point has succeeded, we'll build the query string. To simplify things, our example assumes we're going to build an SQL SELECT statement; feel free to extend this code to do more sophisticated things. The query is built from various attributes of the <dbaccess> element.

Once the query has been built, we connect to the appropriate database. The database is specified with the database attribute of the <dbaccess> element. (In our previous XML samples, notice that DB2, Sybase, and MySQL specify databases in different ways. Specifying this in an attribute makes our extension element more flexible.) We connect to the database, execute the query statement, and get a JDBC ResultSet object in return.

Once we have the ResultSet, our job is relatively simple. We need to create an HTML table, with each row in the table containing a row from the ResultSet. In the previous code, we call our Document object to create each new node. Here are some examples:

while (rs.next())
{
  tr = doc.createElement("tr");
  for (int i = 1; i <= columnCount; i++)
  {
    td = doc.createElement("td");
    td.appendChild(doc.createTextNode(rs.getString(i)));
    tr.appendChild(td);
  }
  table.appendChild(tr);
}
dbResult = new XNodeSet(table);

In this sample, we create the <tr> element with the DOM createElement method. Notice that when we want to add text to a node, we use the createTextNode method to create a text node and append it as a child. In the loop just shown, we take each row of the ResultSet and create a <tr> element for it. We create a <td> element for each column in the ResultSet, then append it to the <tr> element. When we're done with the row, we append the <tr> element to the <table>.

Once we've processed the entire ResultSet, we create a new XNodeSet by passing our <table> element to the XNodeSet constructor. This technique can be used to create any number of nodes, including elements, attributes, text, and comments.

For example, here's how we created the HTML <table> element and added the border="1" attribute to it:

Element table = doc.createElement("table");
table.setAttribute("border", "1");

Our final step is simply to clean up all of the JDBC stuff and return the XNodeSet to Xalan:

rs.close();
stmt.close();
con.close();

...

return dbResult;

The nodes in our XNodeSet are sent straight to the output document, where they appear as ordinary HTML nodes, as shown in Figure 8-7.

Figure 8-7. HTML file with output from an extension element

With this extension, we've been able to generate nodes dynamically, then add them to the output document. Every time this stylesheet is processed, the extension adds the latest data from various databases to the output. You could improve this extension element by adding caching, connection pooling, and other features for performance and scalability; the point of this example was to show you how extension elements work. Whatever its limitations, the best feature of our extension element is that we can use it with any JDBC-compliant database. You can use this code to generate HTML (or XML) from any database, whether the database vendor supports it or not.

TIP: As of this writing, efforts are underway to standardize extension functions and exension elements across XSLT processors. The EXSLT project is one such effort. Visit their web site (http://www.exslt.org) for more information on the EXSLT library of extensions.

Previous | Home | Next
8.2. Extending the Saxon Processor | Book Index | 8.4. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

8.4. Summary

In this chapter, we've run the gamut of extension functions and extension elements, demonstrating how to add sophisticated processing power to our stylesheets. All examples have been self-contained; you could combine these functions and elements to do something really sophisticated. For example, you could use the database extension to extract live sales data from a database, and then convert it into an interactive graphic.

At this point, we've covered just about everything in the XSLT and XPath specifications. In the next chapter, we'll go through a real-world example that illustrates the full power and flexibility of using stylesheets to manipulate structured data.

Previous | Home | Next
8.3. More Examples | Book Index | 9. Case Study: The Toot-O-Matic
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Case Study: The Toot-O-Matic (XSLT)

 XSLTSearch this book

Chapter 9. Case Study: The Toot-O-Matic

Contents:

About the Toot-O-Matic
Design Goals
Tutorial Layout
XML Document Design
XSLT Source Code
Summary

In this chapter, we'll examine a tutorial-building tool called the Toot-O-Matic. Developed by yours truly for IBM's developerWorks web site (http://www.ibm.com/developerWorks, check your local listings), it's a good example of how stylesheets can drive a sophisticated publishing system for structured information. Our tool is built around the Xalan XSLT processor, the Apache XML Project's FOP tool, and various Java facilities (such as the JPEGCodec class).

9.1. About the Toot-O-Matic

Tutorials are the most popular kind of content at developerWorks. Unfortunately, in the early days of the site, we didn't have good tools for creating tutorials. We often started with a document written in a word processor, then we printed it as a PDF file, then we converted it to an HTML file, then we broke the single HTML file into smaller pieces to represent the various panels of the tutorial. Much of this was a tedious, error-prone process that cried out for automation. The Toot-O-Matic handles most of the work necessary to generate files, allowing the tutorial author and production staff to focus on more important things.

To publish a tutorial, we need to create several kinds of output:

  • A web of interlinked HTML files. There should be an introductory panel for the tutorial. It should contain links to all sections of the tutorial. From each panel, there are links to the previous and next panels, as well as links to the Main menu and a section index. All these links are separate HTML files that refer to one another.

  • A pair of PDF files. We produce two PDF files, one with letter-sized pages and one with A4-sized pages. Each PDF file contains a table of contents that lists the different sections of the tutorial. Whenever possible, the graphics used in the tutorial should be part of the PDF file, and any hyperlinks in the tutorial (links between panels, or links to web resources) should be part of the PDF file, as well.

  • A zip file. Many of our customers told us that Internet access was too slow or too expensive for them to read our tutorials online. With a zip file, customers can download everything they need to run the tutorial on their machines without being connected to the Web.

Previous | Home | Next
8.4. Summary | Book Index | 9.2. Design Goals
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Design Goals (XSLT)

 XSLSearch this book

9.2. Design Goals

We had several design goals in mind when we started to design the Toot-O-Matic tag set:

  • Make it easier to create tutorials.

  • Show our audience that we use the technologies we advocate.

  • See just how much we can accomplish through stylesheets.

We'll discuss each of these goals in detail before we move on to the design of the tutorials themselves.

9.2.1. Make It Easier to Create Tutorials

Our first tutorials were incredibly tedious to create. Authors and editors wrote and edited the content in a tool such as Microsoft Word, then we started the publishing process. Our first step was typically to create a PDF version of the tutorial. High-quality printable versions of our tutorials are popular, and it's easy to create them from a single formatted document in Microsoft Word.

Once that was done, we would convert the tutorial document into a single HTML file. We would then take the file, break it into small pieces, and add the standard IBM header and footer to each small piece. This step gave us a number of HTML files (usually 50 to 100) that we needed to link together. In other words, if you look at the third panel in a section, clicking Next should take you to the fourth panel, and clicking Previous should take you to the second panel. We also needed to create a menu panel; from the menu panel, you can link directly to the first panel of any particular section. Finally, each panel had mouseover effects that had to be tested.

While the writer and editor worked on the actual content, our graphic designers created artwork for the titles of the sections and for the tutorial itself. It was important that the heading text look a particular way, so our designers created graphics that contained that text, drawn on the appropriate background. For some titles, both plain and highlighted versions were created for the mouseover effects.

Clearly, much of the tutorial-building process was hand-coded and error-prone (particularly when we were feverishly finishing a tutorial at 5:30 in the morning so it would be on the site by sunrise). We wanted to automate as many of these steps as we could, to save us time and minimize the chance of errors.

9.2.2. Show Our Audience That We Use the Technologies We Advocate

Another goal was to actually use the technologies we espouse. We were certainly aware of the irony of a site that promotes open, standards-based computing creating content with a closed-source, proprietary tool, such as Microsoft Word. One attraction of building tools from XML documents and XSLT stylesheets was that it enabled us to show the world that XML and XSLT can do useful work today. Choosing these technologies to manipulate structured data was a no-brainer for us.

9.2.3. See Just How Much We Could Accomplish Through Stylesheets

Our final goal was to see how much we could do with XSLT. As you'll see, we exercise all the advanced capabilities of XSLT in the Toot-O-Matic, including multiple input files, multiple output files, and extension functions. Through our stylesheets, we convert an XML document into:

  • A web of interlinked HTML documents

  • A menu for the entire tutorial

  • A table of contents for each section of the tutorial

  • JPEG graphics containing the title of the tutorial and each of the individual sections

  • A letter-sized PDF file

  • An A4-sized PDF file

  • A zip file containing everything users need to run the tutorial on their machine

As we'll discuss, creating all of these things through stylesheets required us to push XSLT to its limits. The design of the XML document allows us to manipulate the information for a tutorial in a variety of sophisticated ways, and the structure of our stylesheets makes it easy for us to change the look and feel of our tutorials without having to modify the original XML content.

In addition to these goals, we decided to make the XSLT stylesheets and the necessary extensions open source. We did this so our readers could see what we were doing to "eat our own dog food," and to see if tight integration with a standards-compliant stylesheet processor would allow us to use existing tools to generate tutorials in a semi-WYSIWYG environment.

Previous | Home | Next
9. Case Study: The Toot-O-Matic | Book Index | 9.3. Tutorial Layout
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Tutorial Layout (XSLT)

 XSLSearch this book

9.3. Tutorial Layout

Before we talk about the details of the XML document design and the XSLT source code, we'll review the actual HTML, zip, and PDF files we need to create. One advantage we had in this project is that we didn't have any existing XML documents to contend with; this advantage gave us complete freedom over the XML document design.

9.3.1. Menu Panel

The menu panel is the first HTML document a user sees. It looks like Figure 9-1.

Figure 9-1. Toot-O-Matic menu panel

In this example, the string "Building tutorials with the Toot-O-Matic" and all section titles are JPEG graphics created with our stylesheets and extensions. If you mouseover a section title, its background color changes, as shown in Figure 9-2.

Figure 9-2. Mouseover effect for section titles

In this sample, notice that the text of the menu item appears as a tooltip. This appearance is useful for sight-impaired users, and is consistent with the Web Accessibility Guidelines defined by the W3C.

A variety of navigation controls appear on every panel in a tutorial. The navigation bar contains items such as "Main menu," "Section menu," and "Feedback." Although some items are disabled (if you're already on the Main menu, the "Main menu" item isn't active, for example), they appear on every panel of the tutorial. There are also icons for viewing the tutorial in alternate formats, as Figure 9-3 demonstrates.

Figure 9-3. Icons for alternate tutorial forms

From left to right, these icons allow users to download a zip file that contains all files necessary to run the tutorial, a letter-sized PDF file, an A4-sized PDF file, and email a note to a friend, recommending this tutorial. All icons appear on every panel in the tutorial, and their associated links are generated by the Toot-O-Matic.

Each panel has a masthead and footer, which are defined by corporate standards. They are generated by named templates cleverly named masthead and footer. As corporate standards are updated, we simply change those templates to change the look and feel of the tutorial panel.

9.3.2. Individual Panels

Most HTML files that make up a complete tutorial use this format. Notice that the panel contains the text page 1 of 6; we generate this text with XPath expressions. The 1 is generated by the position() function, and the 6 is generated by the count() function. Each panel's navigation bar also contains links to the Main menu panel and the Section menu panel. An individual panel looks like Figure 9-4.

Figure 9-4. An individual tutorial panel

9.3.3. Section Menu

If you click on the "Section menu" item while on any panel in a tutorial, you'll see a listing similar to Figure 9-5.

Figure 9-5. A section menu

You can click on the title of any panel to go directly to it. We generate the panel titles with a <xsl:for-each select="panel/title"> element. We'll discuss how we generate the links between panels soon.

9.3.4. Feedback Panel

The Toot-O-Matic generates a Feedback panel automatically. The Feedback panel contains an HTML form that lets readers send in their comments on the tutorial. Code on the server takes reader comments and stores them in a database automatically. Figure 9-6 shows what the generated Feedback panel looks like.

Figure 9-6. A typical Feedback panel

The XML document identifies one panel as the Feedback panel. The Feedback button on the navigation bar of every HTML file points to the HTML file with the feedback form. Again, all of these cross-references are generated automatically through the magic of XSLT and XPath.

9.3.5. Email Panel

Another feature of our tutorials is that you can email the URL of the tutorial to a friend. Clicking the "e-mail it!" icon on any panel displays a new browser window as shown in Figure 9-7.

Figure 9-7. The email panel

In this example, the text beneath the title of the tutorial is derived from the abstract attribute of the <tutorial> element.

9.3.6. Zip File

To help readers who have either occasional or expensive web access, we build a zip file that contains everything they need to use the tutorial on their machines. The contents include all generated HTML files, all of the standard graphics used in the header and footer, and any referenced graphics from the tutorial itself.

9.3.7. PDF Files

For readers who want to print out the tutorial and read it offline, we produce two PDF versions of the tutorial, one for letter-sized paper, and one for A4-sized paper. Although we could provide an HTML file that simply contains the HTML rendering of all the pages of the tutorial, we want higher-quality printable output. The first page of the tutorial, shown in Figure 9-8, features the title of the tutorial and a table of contents.

Figure 9-8. First page of the tutorial PDF file

In the table of contents, both the section titles and the page numbers are hyperlinks. If you view the PDF file online, you can click on those items and go directly to the various parts of the tutorial. Even if you read a printout of the PDF file, the page numbers are still an important navigational tool. Cross-references between panels in the tutorial are similarly converted to hyperlinks and printable page numbers. Best of all, any hyperlinks to web sites are also converted to hyperlinks. If your machine is connected, and you have a recent version of the Adobe Acrobat Reader, you can click on the web site and go directly to it.

Pages in the body of the tutorial feature the text and illustrations of each panel, with a horizontal line between panels. The first panel of each section starts on a new page. Figure 9-9 shows the layout of an individual panel in the PDF file.

Figure 9-9. PDF layout for an individual panel

To accommodate our worldwide audience, we create letter-sized and A4-sized versions of the PDF file. We use the same stylesheet for each PDF file; we simply change the page dimensions and let the Formatting Objects to PDF (FOP) tool generate the line, column, and page breaks for us. We are responsible for creating the formatting objects the FOP tool needs to do its work.

Previous | Home | Next
9.2. Design Goals | Book Index | 9.4. XML Document Design
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XML Document Design (XSLT)

 XSLSearch this book

9.4. XML Document Design

Now that we've covered how our tutorials appear in all their various forms, we'll discuss the structure of the XML documents that become our tutorials. To start with, we used some obvious structural principles:

  • A <tutorial> should contain a single <title> and one or more <section>s.

  • A <section> should contain a single <title> and one or more <panel>s.

  • A <panel> should contain a single <title> and a <body>, which in turn contains the markup for the panel's contents.

This foundation creates an XML structure identical to the layout of our tutorials, so it was an obvious place to start.

9.4.1. Individual Panels

An individual panel has the following structure:

<panel>
  <title>Title of the panel</title>
  <body>
    <image-column>
    <text-column>
      Basic HTML markup (<p>, <ol>, <li>, <b>, <i>, <u>, <a>, etc.)
    </text-column>
  </body>
</panel>

The <image-column> element is optional; it's used to define the image that appears on the left side of the panel. We intentionally kept our markup design simple so that rendering the tutorials would be relatively straightforward. Although this approach is heavy-handed, it does allow us to enforce a certain amount of consistency in the layout of our tutorials. Reusing common HTML tags inside the <text-column> element allows authors to use the tags they already know and love, and it simplifies the XML-to-HTML transformation.

Previous | Home | Next
9.3. Tutorial Layout | Book Index | 9.5. XSLT Source Code
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Source Code (XSLT)

 XSLSearch this book

9.5. XSLT Source Code

Now that we've discussed the design issues we went through as we defined our XML document structure, we'll talk about how our XSLT stylesheets transform XML-tagged tutorials into the files we want.

9.5.1. Stylesheets and Modes

To start with, we use the XSLT mode attribute to process the same set of nodes several times. Our template for the root element is similar to a Java or C++ program whose main() method contains nothing but subroutine calls:

<xsl:template match="/">
  <xsl:apply-templates select="tutorial" mode="build-main-index"/>
  <xsl:apply-templates select="tutorial" mode="build-section-indexes"/>
  <xsl:apply-templates select="tutorial" mode="build-individual-panels"/>
  <xsl:apply-templates select="tutorial" mode="generate-graphics"/>
  <xsl:apply-templates select="tutorial" mode="generate-pdf-file">
    <xsl:with-param name="page-size" select="'letter'"/>
  </xsl:apply-templates>
  <xsl:apply-templates select="tutorial" mode="generate-pdf-file">
    <xsl:with-param name="page-size" select="'a4'"/>
  </xsl:apply-templates>
  <xsl:apply-templates select="tutorial" mode="generate-zip-file"/>
</xsl:template>

If this were a Java program, we might create a main() method that looks like this:

public static void main(String[] argv)
{
  buildMainIndex();
  buildSectionIndexes();
  buildIndividualPanels();
  generateGraphics();
  generatePDFFile("letter");

  generatePDFFile("a4");
  generateZipFile();
}

This style of coding facilitates maintenance; if the PDF files aren't generated correctly, the templates with mode="generate-pdf-file" are the obvious place to start debugging. In addition, we structured the files so that all the templates for a given mode are in a single file that can be included into our main stylesheet:

<xsl:include href="toot-o-matic-variables.xsl"/>

<xsl:include href="xslt-utilities.xsl"/>
<xsl:include href="dw-style.xsl"/>

<xsl:include href="build-main-index.xsl"/>
<xsl:include href="build-section-indexes.xsl"/>
<xsl:include href="build-individual-panels.xsl"/>
<xsl:include href="build-graphics.xsl"/>
<xsl:include href="build-pdf-file.xsl"/>
<xsl:include href="build-zip-file.xsl"/>

In addition to the obviously named files, the file toot-o-matic-variables.xsl defines several global variables used throughout the stylesheets, xslt-utilities.xsl is a library of generic routines (substring replacement, for example) we use, and dw-style.xsl defines the look and feel of our HTML pages.

9.5.2. Initializing Global Variables

It's worth discussing the global variables initialized in toot-o-matic-variables.xsl. All of these variables are used throughout our various stylesheets, and initializing them saves us a significant amount of processing time. The most significant variable is $mouse-effects. This variable is an automatically generated segment of JavaScript code used to process mouseover effects on all the HTML pages we generate. Here's how the generated code looks:

<!-- var emailAbstract="developerWorks is proud to present the Toot-O-Matic,...";
var justTitle="Building tutorials with the Toot-O-Matic";
var tutorialPrereqs="http://www-4.ibm.com/education/tootomatic";
var menu1blurb="Installing and configuring Toot-O-Matic";
var menu2blurb="Creating your first tutorial";
var menu3blurb="developerWorks editorial guidelines";
var menu4blurb="Toot-O-Matic tag guide";
var menu5blurb="Toot-O-Matic tag reference";
var menu6blurb="Troubleshooting";
var menu7blurb="Feedback";

var browser = "x";
if (navigator.userAgent.indexOf("Mozilla/4") != -1) browser = "N3";
else if (navigator.userAgent.indexOf("Mozilla/3") != -1) browser = "N3";
else browser = "x";

if (browser=="N3")
{
  var menu1over=new Image(108,68);
  var menu1out=new Image(108,68);
  var menu2over=new Image(108,68);
  var menu2out=new Image(108,68);
  var menu3over=new Image(108,68);
  var menu3out=new Image(108,68);
  ...
  var topmainover=new Image(77,15);
  var topmainout=new Image(77,15);
  var bottommainover=new Image(77,15);
  var bottommainout=new Image(77,15);
  var topsectionover=new Image(98,15);
  var topsectionout=new Image(98,15);
  var bottomsectionover=new Image(98,15);
  var bottomsectionout=new Image(98,15);
  var topfeedbackover=new Image(80,15);
  var topfeedbackout=new Image(80,15);
  var bottomfeedbackover=new Image(80,15);
  var bottomfeedbackout=new Image(80,15);
  var toppreviousover=new Image(77,15);
  var toppreviousout=new Image(77,15);
  var bottompreviousover=new Image(77,15);
  var bottompreviousout=new Image(77,15);
  var topnextover=new Image(60,15);
  var topnextout=new Image(60,15);
  var bottomnextover=new Image(60,15);
  var bottomnextout=new Image(60,15);
  var topnextsectionover=new Image(108,15);
  var topnextsectionout=new Image(108,15);
  var bottomnextsectionover=new Image(108,15);
  var bottomnextsectionout=new Image(108,15);

  menu1over.src="imagemaster/himenu1.jpg";
  menu1out.src="imagemaster/menu1.jpg";
  menu2over.src="imagemaster/himenu2.jpg";
  menu2out.src="imagemaster/menu2.jpg";
  menu3over.src="imagemaster/himenu3.jpg";
  menu3out.src="imagemaster/menu3.jpg";
  ...
  var mainblurb="Main menu";
  var sectionblurb="Section menu";
  var feedbackblurb="Give feedback on this tutorial";
  var previousblurb="Go to previous panel";
  var nextblurb="Go to next panel";
  var nextsectionblurb="Go to next section";

  topmainover.src="../i/h-main.gif";
  topmainout.src="../i/main.gif";
...
}
function iOut(image)
{
  if (browser=="N3")document[image].src=eval(image + "out.src");
}
function iOver(image)
{
  if (browser=="N3")document[image].src=eval(image + "over.src");
}
// -->

This JavaScript code is used for the mouseover effects on the HTML panels. To streamline processing, we generate this code as a variable. (We've removed roughly half the code here to keep the listing short; most of this code deals with initializing a number of variables.) Whenever we create a new HTML page, we simply insert this variable into the output document:

<script language="javascript">
  <xsl:value-of select="$mouse-effects"/>
</script>

As in traditional programming, storing frequently used values in a variable instead of calculating them each time simplifies the code and improves performance. Notice in the code listing that a significant amount of JavaScript code is generated from the XML source document. This fragment is the XSLT that generates a set of JavaScript variables that contain the titles of all the sections:

<xsl:for-each select="/tutorial/section">
  <xsl:text>var menu</xsl:text>
  <xsl:value-of select="position()"/>
  <xsl:text>blurb="</xsl:text>
  <xsl:value-of select="title"/>
  <xsl:text>"; </xsl:text>
  <xsl:value-of select="$newline"/>
</xsl:for-each>

The code generates seven new variables, one for each <section> element in our tutorial:

var menu1blurb="Installing and configuring Toot-O-Matic";
var menu2blurb="Creating your first tutorial";
var menu3blurb="developerWorks editorial guidelines";
var menu4blurb="Toot-O-Matic tag guide";
var menu5blurb="Toot-O-Matic tag reference";
var menu6blurb="Troubleshooting";
var menu7blurb="Feedback";
NOTE: This is one of many cases when a naming convention is invaluable. If we want the JavaScript variable that contains the title of the fifth section, we know that variable is named menu5blurb. This technique is useful in many other places, as well. If we're creating HTML files for the third <section>, and we're currently processing the fourth <panel> in that <section>, and the root filename we're using is tootomatic, the newly created HTML file will be named tootomatic-3-4.html. Similarly, if we want to create a link to the previous and next HTML files, those files are named tootomatic-3-3.html and tootomatic-3-5.html, respectively. You'll see this technique used throughout this case study.

9.5.3. Generating the Main Menu Panel

The Main menu panel consists of a standard header and footer, with a list of all sections of the tutorial in between. Clicking on any of the section titles takes you to the first panel in that section. To enhance the visual appeal of the panel, generated graphics and mouseover effects are used to display the panel title.

The stylesheet that generates the list of sections is straightforward. The header and footer are generated from boilerplate text; the list of sections is generated with an <xsl:for-each> element:

<xsl:for-each select="section">
  <a border="0">
    <xsl:attribute name="href">
      <xsl:value-of select="$fn"/>
      <xsl:text>-</xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text>-1.html</xsl:text>
    </xsl:attribute>
    <xsl:attribute name="onMouseOver">
      <xsl:text>iOver('menu</xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text>'); self.status=menu</xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text>blurb; return true;</xsl:text>
    </xsl:attribute>
    <xsl:attribute name="onMouseOut">
      <xsl:text>iOut('menu</xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text>'); self.status=''; return true;</xsl:text>
    </xsl:attribute>
    <img width="380" height="20" border="0">
      <xsl:attribute name="name">
        <xsl:text>menu</xsl:text>
        <xsl:value-of select="position()"/>
      </xsl:attribute>
      <xsl:attribute name="src">
        <xsl:text>imagemaster/</xsl:text>
        <xsl:text>menu</xsl:text>
        <xsl:value-of select="position()"/>
        <xsl:text>.jpg</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="alt">
        <xsl:value-of select="position()"/>
        <xsl:text>. </xsl:text>
        <xsl:value-of select="title"/>
      </xsl:attribute>
    </img>
  </a>
  <br/>
</xsl:for-each>

This XSLT produces the following HTML code for our example XML file:

<a border="0" href="tootomatic-1-1.html"
  onMouseOver="iOver('menu1'); self.status=menu1blurb; return true;"
  onMouseOut="iOut('menu1'); self.status=''; return true;">
  <img border="0" height="20" width="380" name="menu1"
    src="imagemaster/menu1.jpg"
    alt="1. Installing and configuring Toot-O-Matic">
</a>
<br>
<a border="0" href="tootomatic-2-1.html"
  onMouseOver="iOver('menu2'); self.status=menu2blurb; return true;"
  onMouseOut="iOut('menu2'); self.status=''; return true;">
  <img border="0" height="20" width="380" name="menu2"
    src="imagemaster/menu2.jpg"
    alt="2. Creating your first tutorial">
</a>
<br>
<a border="0" href="tootomatic-3-1.html"
  onMouseOver="iOver('menu3'); self.status=menu3blurb; return true;"
  onMouseOut="iOut('menu3'); self.status=''; return true;">
  <img border="0" height="20" width="380" name="menu3"
    src="imagemaster/menu3.jpg"
    alt="3. developerWorks editorial guidelines">
</a>
<br>
<a border="0" href="tootomatic-4-1.html"
  onMouseOver="iOver('menu4'); self.status=menu4blurb; return true;"
  onMouseOut="iOut('menu4'); self.status=''; return true;">
  <img border="0" height="20" width="380" name="menu4"
    src="imagemaster/menu4.jpg"
    alt="4. Toot-O-Matic tag guide">
</a>
<br>
<a border="0" href="tootomatic-5-1.html"
  onMouseOver="iOver('menu5'); self.status=menu5blurb; return true;"
  onMouseOut="iOut('menu5'); self.status=''; return true;">
  <img border="0" height="20" width="380" name="menu5"
    src="imagemaster/menu5.jpg"
    alt="5. Toot-O-Matic tag reference">
</a>
<br>
<a border="0" href="tootomatic-6-1.html"
  onMouseOver="iOver('menu6'); self.status=menu6blurb; return true;"
  onMouseOut="iOut('menu6'); self.status=''; return true;">
  <img border="0" height="20" width="380" name="menu6"
    src="imagemaster/menu6.jpg"
    alt="6. Troubleshooting">
</a>
<br>
<a border="0" href="tootomatic-7-1.html"
  onMouseOver="iOver('menu7'); self.status=menu7blurb; return true;"
  onMouseOut="iOut('menu7'); self.status=''; return true;">
  <img border="0" height="20" width="380" name="menu7"
    src="imagemaster/menu7.jpg"
    alt="7. Feedback">
</a>

Notice the number of things that are automatically generated in this list of sections. We know the filename of any given section, thanks to our filenaming convention. The first panel in the fifth section of the tutorial is tootomatic-5-1.html, for example. For the JavaScript mouseover effects we mentioned previously, we name the <img> elements in the list menu1, menu2, etc. Similarly, the name of each graphic is imagemaster/menu1.jpg, imagemaster/menu2.jpg, etc. The onMouseOver attribute uses variables such as menu1blurb and menu2blurb. Generating these items removes the chance for human error (once the stylesheets are correct) and allows us to control the look and feel of the HTML pages in the tutorial.

9.5.4. Generating the Section Indexes

To generate a section index, we create an HTML file with an ordered list of all of the <panel> elements in the current <section>. Retrieving the titles of all the panels can be done with a <xsl:for-each> element in the stylesheet:

<xsl:for-each select="panel">
  <img border="0" src="../i/arrow.gif"/>
  <a>
    <xsl:attribute name="href">
      <xsl:value-of select="$fn"/><xsl:text>-</xsl:text>
      <xsl:value-of select="$sectionNumber"/><xsl:text>-</xsl:text>
      <xsl:value-of select="position()"/><xsl:text>.html</xsl:text>
    </xsl:attribute>
    <xsl:value-of select="position()"/><xsl:text>. </xsl:text>
    <xsl:value-of select="title"/>
  </a>
  <br/>
</xsl:for-each>

In this listing, the variable $fn is defined as the root filename used to generate all HTML filenames for this tutorial. The filename convention used for section indexes is index1.html for the first section index, index2.html for the second section index, etc. This convention makes it easy to generate the section index when we need it, and it makes it easy for the individual panels in a given section to reference the proper section index on each panel.

We use the Xalan Redirect extension to write output to multiple files. Here's how we invoke that extension to begin writing output to another file:

<xsl:for-each select="section">
  <redirect:write select="concat($curDir, $fileSep, 'index', position(),
    '.html')">

The select attribute of the <redirect:write> element defines the name of the output file. To generate this filename, we concatenate the current directory to which we're writing files (a global variable), the file separator character (another global variable), the text index, the position of this section, and the text .html. If we use the tootomatic directory on a Windows machine, the index for the second <section> will be written to the file tootomatic\index2.html. We use the Redirect extension whenever we need to generate an HTML file for a section index or an individual panel.

9.5.5. Generating the Individual Panels

The masthead and footer of each panel are fairly straightforward; both use a predefined format and a series of links common to all pages on IBM sites. This is a perfect use for named templates. We need to create certain HTML markup for the masthead of each HTML page, and we need to create more markup for the footer of each page. In addition, we need to create the title bar at the top of each page and a navigation bar (an area with Previous and Next links, among other things) at the top and bottom of most pages. We use four templates, cleverly named dw-masthead, dw-title-bar, dw-nav-bar, and dw-footer, to do this:

<xsl:call-template name="dw-masthead"/>
<xsl:call-template name="dw-title-bar"/>
<xsl:call-template name="dw-nav-bar">
  <xsl:with-param name="includeMain" select="'youBetcha'"/>
  <xsl:with-param name="sectionNumber" select="$sectionNumber"/>
  <xsl:with-param name="position" select="$pos"/>
  <xsl:with-param name="last" select="$last"/>
  <xsl:with-param name="topOrBottom" select="'top'"/>
  <xsl:with-param name="oneOrTwo" select="'two'"/>
</xsl:call-template>

<!-- Processing for the main body of the page goes here -->

<xsl:call-template name="dw-nav-bar">
  <xsl:with-param name="includeMain" select="'youBetcha'"/>
  <xsl:with-param name="sectionNumber" select="$sectionNumber"/>
  <xsl:with-param name="position" select="$pos"/>
  <xsl:with-param name="last" select="$last"/>
  <xsl:with-param name="topOrBottom" select="'bottom'"/>
  <xsl:with-param name="oneOrTwo" select="'two'"/>
</xsl:call-template>
<xsl:call-template name="dw-footer"/>

Of the four templates, only dw-nav-bar takes any parameters. Depending on the page we're currently generating, we may or may not need the Main menu button (we don't include this button on the Main menu panel). We need the current section number so the navigation bar can create filenames for the links to the section menu, the previous panel, and the next panel. The position parameter defines the position of this particular panel; last defines the position of the last panel. If position is 1, then the Previous button will be disabled. If position is equal to last, then the Next button will be disabled. The parameter topOrBottom defines whether this navigation bar is being created at the top or bottom of the panel (we have to name the images differently so the JavaScript mouseover effects work correctly). Finally, the oneOrTwo parameter determines whether this panel will have two navigation bars or just one. This is also necessary for the mouseover effects.

Now that we've built all these parts of the page, building the actual content of the panel is somewhat anticlimactic. We support a limited set of HTML tags (the 20 or so most-used tags, added sparingly as we've needed to add new functions to the tool), most of which are converted directly into their HTML equivalents.

9.5.6. Generating the PDF Files

Converting the XML document to an XSL Formatting Objects (XSL-FO) stream is fairly straightforward, as well. Our printed layout consists of the graphics and text from the tutorial, combined with page numbers, headers, and footers to create high-quality printed output. We use the Apache XML Project's FOP (Formatting Objects to PDF) tool to do this.

When we invoke the PDF-generating templates with the mode=generate-pdf attribute, we pass in the page-size parameter to set the dimensions of the printed page. We generate PDFs with both letter-sized and A4-sized pages to support our customers around the world.

To create the PDF, we first create the output file of formatting objects, converting the various XML tags from our source document into the various formatting objects we need:

<fo:block font-size="16pt" line-height="19pt" font-weight="bold"
  space-after.optimum="12pt">
  Introduction to JavaServer Pages
</fo:block>
<fo:block space-after.optimum="6pt">
  In today's environment, most web sites want to display dynamic
  content based on the user and the session. Most content, such
  as images, text, and banner ads, is most easily built with
  HTML editors. So we need to mix the "static" content of HTML
  files with "directives" for accessing or generating dynamic
  content.
</fo:block>
<fo:block space-after.optimum="6pt">
  JavaServer Pages meet this need. They provide server-side
  scripting support for generating web pages with combined
  static and dynamic content.
</fo:block>

Currently, the XSL:FO specification is a candidate recommendation at the World Wide Web Consortium (W3C). Because future changes are likely, we won't discuss the formatting objects themselves. It suffices to say that our stylesheet defines page layouts (margins, running headers and footers, etc.) and then creates a number of formatting objects inside those page layouts. The FOP tool handles the details of calculating line, page, and column breaks, page references, and hyperlinks.

Once the file of formatting objects is created, we call an extension function to convert the formatting objects file into a PDF. Here's the exension's main code:

public static void buildPDFFile(String foFilename, String pdfFilename)
{
  try
  {
    XMLReader parser =
      (XMLReader) Class.forName("org.apache.xerces.parsers.SAXParser")
                       .newInstance();
    Driver driver = new Driver();
    driver.setRenderer("org.apache.fop.render.pdf.PDFRenderer",
                       Version.getVersion());
    driver.addElementMapping("org.apache.fop.fo.StandardElementMapping");
    driver.addElementMapping("org.apache.fop.svg.SVGElementMapping");
    driver.addPropertyList("org.apache.fop.fo.StandardPropertyListMapping");
    driver.addPropertyList("org.apache.fop.svg.SVGPropertyListMapping");
    driver.setOutputStream(new FileOutputStream(pdfFilename));
    driver.buildFOTree(parser, new InputSource(foFilename));
    driver.format();
    driver.render();
  }

The code merely creates the FOP Driver object, sets its various properties, and then tells it to render the formatting objects in a PDF file. The main difficulty here is in determining how the various XML elements should be converted to formatting objects; once the conversion is done, we have a tool that generates high-quality printable output from our XML source files. Best of all, this code uses open source tools exclusively.

9.5.7. Generating the JPEG Files

Another thing we need to produce for the tutorial is a series of JPEG files. To have precise control over the appearance of the titles in the tutorial, we create a JPEG file in which the title text is written in a particular font. We discussed this code in Chapter 8, "Extending XSLT", so we won't go over it here. Here's the first significant section of the build-graphics.xsl file:

<xsl:template match="tutorial" mode="generate-graphics">
  <xsl:choose>
    <xsl:when test="function-available('jpeg:buildJPEGFile')">
      <xsl:value-of
        select="jpeg:buildJPEGFile(title,
        concat('master', $fileSep, 'masthead.jpg'),
        concat($curDir, $fileSep, 'imagemaster', $fileSep, 'masthead.jpg'),
        $baseFont, 27, 5, 30, 0, 0, 0)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message terminate="yes">
        Error! JPEG library not available!
      </xsl:message>
    </xsl:otherwise>
  </xsl:choose>

The buildJPEGFile function takes several parameters, including the title text (in the example, our XPath expression passes in the value of the title element), the name of the background JPEG file (we load this file, draw the text on top of it, and then save the new JPEG), the name of the new JPEG file, the name of the font, and other details about the font size, the x- and y-coordinates where the text should start, and the color in which to draw it.

Although neither this extension nor the stylesheet that calls it are rocket science, they save us a tremendous amount of time in the tutorial development process. Before we had the Toot-O-Matic, we had to ask our highly trained, highly talented, and highly overworked graphics staff to create these graphics for us; now we do it automatically and the graphics staff can focus their talents on more important things.

9.5.8. Generating the Zip File

Our last task is to generate a zip file that contains all the files needed to view the tutorial locally. This includes all HTML files, all standard graphics, all JPEGs we generate, and any graphics referenced in the XML source (anything in an <img> tag). We call another Java extension to build the zip file. Determining which files should be loaded into the zip file relies heavily on our naming conventions.

When we invoke the buildZipFile function, we pass in several arguments. The first three are the root filename, the directory to which we write the output files, and the file separator for this platform. The next argument is the <tutorial> node itself; the extension uses DOM functions to determine what files should be added to the zip file. The final argument is a node-set of all the things that reference graphics files in the XML source. That includes the img attribute of any tag and the src attribute of the <img> element. Here's what the function call looks like:

<xsl:template match="tutorial" mode="generate-zip-file">
  <xsl:choose>
    <xsl:when test="function-available('zip:buildZipFile')">
      <xsl:variable name="referencedGraphics"
        select="./@img|//image-column/@img|//img/@src"/>
      <xsl:value-of
        select="zip:buildZipFile($fn, $curDir,
                                 $fileSep, ., $referencedGraphics)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message terminate="yes">
        Error! Zip file library not available!
      </xsl:message>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

In the extension function code itself, we start by creating the ZipOutputStream itself:

ZipOutputStream zipOut =
  new ZipOutputStream(new FileOutputStream(currentDirectory +
                                           fileSeparator +
                                           baseFilename + ".zip"));

Once we've created our ZipOutputStream, we'll see if there's a comment for the zip file in the zip-file-comment attribute of the <tutorial> element:

Node currentNode = tutorialElement.nextNode();

while (currentNode != null)
{
  if (currentNode.getLocalName().equals("tutorial"))
  {
    ElementImpl currentElement = (ElementImpl)currentNode;
    String zipFileComment = currentElement.getAttribute("zip-file-comment");
    if (zipFileComment != null)
      zipOut.setComment(zipFileComment);
    else
    {
      zipFileComment = currentElement.getAttribute("alt");
      if (zipFileComment != null)
        zipOut.setComment(zipFileComment);
    }

With everything we do with the DOM nodes, we'll need to make sure we actually work with the appropriate nodes; that's why we use the function call getLocalName().equals("tutorial"). Once we've found the <tutorial> element, we can work with its children to figure out the names of all the HTML and JPEG files we need to add to the zip file. If the <tutorial> element has five <section> children, and the first <section> contains eleven <panel>s, then we'll need to write the files tootomatic-1-1.html through tootomatic-1-11.html to the zip file. (This assumes that the base filename we use is tootomatic.) Here's an excerpt from the code:

int numKids = currentElement.getChildCount();
int numSections = 0;
for (int i = 0; i < numKids; i++)
{
  Node currentChild = currentElement.getChild(i);
  if (currentChild.getLocalName().equals("section"))
  {
    ElementImpl currentChildElement = (ElementImpl)currentChild
    fileToZip = new File(currentDirectory + fileSeparator + "index" +
                         ++numSections + ".html");
    fis = new FileInputStream(fileToZip);
    entry = new ZipEntry(currentDirectory + fileSeparator +
                                  fileToZip.getName());
    if (zipOut != null)
    {
      zipOut.putNextEntry(entry);
      while((bytes_read = fis.read(buffer)) != -1)
        zipOut.write(buffer, 0, bytes_read);
    }
    fis.close();

    int numGrandkids = currentChildElement.getChildCount();
    int numPanels = 0;
    for (int j = 0; j < numGrandkids; j++)
    {
      Node currentGrandchildElement = currentChildElement.getChild(j);
      if (currentGrandchildElement.getLocalName().equals("panel"))
      {
        fileToZip = new File(currentDirectory + fileSeparator +
                             baseFilename + "-" + numSections + "-" +
                             ++numPanels + ".html");
        fis = new FileInputStream(fileToZip);
        entry = new ZipEntry(currentDirectory + fileSeparator +
                             fileToZip.getName());
        if (zipOut != null)
        {
          zipOut.putNextEntry(entry);
          while((bytes_read = fis.read(buffer)) != -1)
            zipOut.write(buffer, 0, bytes_read);
        }
        fis.close();
      }
    }
  }
}

Now that we know how many <section> elements are in our <tutorial>, we can write all the generated JPEG graphics to the zip file. Our extension function also contains a static array of the filenames of all standard files used by every tutorial:

static String standardFiles[] = {"c.gif", "sw-gold.gif",
                                 "main.gif", "xmain.gif",
                                 "section.gif", "xsection.gif",
                                 "feedback.gif", "xfeedback.gif",
                                 "previous.gif", "xprevious.gif",
                                 "next.gif", "xnext.gif",
                                 "icon-discuss.gif", "icon-email.gif",
                                 "icon-pdf-ltr.gif", "icon-zip.gif",
                                 "icon-pdf-a4.gif",
                                 "mast_logo.gif", "shopibm.gif",
                                 "support.gif", "downloads.gif",
                                 "mast_lnav_sp.gif", "about.gif",
                                 "h-menu.gif", "h-main.gif",
                                 "h-section.gif", "h-feedback.gif",
                                 "h-previous.gif", "h-next.gif",
                                 "nextsection.gif", "h-nextsection.gif",
                                 "arrow.gif", "mgradient.gif",
                                 "email.gif", "dw-logo2.gif",
                                 "btn-send.gif", "btn-close.gif",
                                 "emailfriend.js"};

We store each of these standard files in the zip file for each tutorial. Storing the names of the files in an array makes it easy to add or delete new files from the list. If this list of files changed frequently, we would consider writing an XML-based configuration file that listed all standard files. We could then parse that file, extract the filenames from it, and add those files to the zip file.

Our next task is to use our node-set of graphics elements to add all referenced graphics to the zip file:

currentNode = graphicsElements.nextNode();

HashMap zipEntries = new HashMap();
while (currentNode != null)

{
  String nextGraphicsFile = currentNode.getNodeValue();
  if (!zipEntries.containsKey(nextGraphicsFile))
  {
    fileToZip = new File(currentNode.getNodeValue());
    fis = new FileInputStream(fileToZip);
    entry = new ZipEntry(currentDirectory + fileSeparator +
                             currentNode.getNodeValue());
    zipOut.putNextEntry(entry);
    while ((bytes_read = fis.read(buffer)) != -1)
      zipOut.write(buffer, 0, bytes_read);
    zipEntries.put(nextGraphicsFile, nextGraphicsFile);
  }
  currentNode = graphicsElements.nextNode();
}

As we add a referenced graphics file to the zip file, we put the name of the file into a HashMap. If we attempt to add a file to the zip archive and that file is already in the archive, we'll get an exception. To avoid that problem, we check each filename before we add it to the zip file.

Our last task is to close the ZipOutputStream:

zipOut.flush();
zipOut.close();
Previous | Home | Next
9.4. XML Document Design | Book Index | 9.6. Summary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Summary (XSLT)

 XSLSearch this book

9.6. Summary

The stylesheets and extensions that make up the Toot-O-Matic exercise almost everything in XSLT and XPath. Best of all, it allows us to take the structured content of an XML document and transform it into dozens of HTML, JPEG, PDF, and zip files, all of which are extensively cross-referenced and hyperlinked. Even though developerWorks doesn't actually publish the XML file itself (the XML file never leaves our server), using XML for this purpose saves us a tremendous amount of time and money. If you've got a project that needs to convert structured data into several different kinds of documents, an XSLT-based solution can be a winner.

If you'd like to look at the source code for the Toot-O-Matic, it's available at http://www6.software.ibm.com/dl/devworks/dw-tootomatic-p. Even if you're not writing tutorials, there are a number of useful techniques in the code that you're welcome to use in your own projects. The developerWorks site also has articles and a discussion forum related to the Toot-O-Matic code.

Previous | Home | Next
9.5. XSLT Source Code | Book Index | A. XSLT Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Reference (XSLT)

 XSLTSearch this book

Appendix A. XSLT Reference

This chapter is a complete reference to all the elements defined in the XSLT specification.

<xsl:apply-imports> Allows you to apply any overridden templates to the current node. It is comparable to the super() method in Java.

Category

Instruction

Required Attributes

None.

Optional Attributes

None.

Content

None. <xsl:apply-imports> is an empty element.

Appears in

<xsl:apply-imports> appears inside a template.

Defined in

XSLT section 5.6, Overriding Template Rules.

Example

Here is a short XML file we'll use to illustrate <xsl:apply-imports>:

<?xml version="1.0"?>
<test>

  <p>This is a test XML document used by several
  of our sample stylesheets.</p>
  <question>
    <text>When completed, the Eiffel Tower was the
    tallest building in the world.</text>
    <true correct="yes">You're correct!  The Eiffel
    Tower was the world's tallest building until 1930.</true>
    <false>No, the Eiffel Tower was the world's tallest
    building for over 30 years.</false>
  </question>
  <question>
    <text>New York's Empire State Building knocked the
    Eiffel Tower from its pedestal.</text>
    <true>No, that's not correct.</true>
    <false correct="yes">Correct!  New York's Chrysler
    Building, completed in 1930, became the world's tallest.</false>
  </question>
</test>

Here's the stylesheet we'll import:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:for-each select="//text|//true|//false">
          <p>
            <xsl:apply-templates select="."/>
          </p>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="text">
    <xsl:text>True or False: </xsl:text><xsl:value-of select="."/>
  </xsl:template>

  <xsl:template match="true|false">
    <b><xsl:value-of select="name()"/>:</b>
    <br/>
    <xsl:value-of select="."/>
  </xsl:template>

</xsl:stylesheet>

This template provides basic formatting for the <true> and <false> elements, as shown in Figure A-1.

Figure A-1. Document generated with basic formatting

We'll illustrate <xsl:apply-imports> with this stylesheet, which imports the other stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:import href="imported.xsl"/>
  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>A Brief Test</title>
        <style>
          <xsl:comment>
            p.question {font-size: 125%; font-weight: bold}
            p.right    {color: green}
            p.wrong    {color: red}
          </xsl:comment>
        </style>
      </head>
      <body>
        <h1>A Brief Test</h1>
        <xsl:for-each select="//question">
          <table border="1">
            <xsl:apply-templates select="text"/>
            <xsl:apply-templates select="true|false"/>
          </table>
          <br/>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="text">
    <tr bgcolor="lightslategray">
      <td>
        <p class="question">
          <xsl:apply-imports/>
        </p>
      </td>
    </tr>
  </xsl:template>

  <xsl:template match="true|false">
    <tr>
      <td>
        <xsl:choose>
          <xsl:when test="@correct='yes'">
            <p class="right">
              <xsl:apply-imports/>
            </p>
          </xsl:when>
          <xsl:otherwise>
            <p class="wrong">
              <xsl:apply-imports/>
            </p>
          </xsl:otherwise>
        </xsl:choose>
      </td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

Using <xsl:apply-imports> allows us to augment the behavior of the imported templates. Our new stylesheet produces this document:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>A Brief Test</title>
<style>
<!--
            p.question {font-size: 125%; font-weight: bold}
            p.right    {color: green}
            p.wrong    {color: red}
          -->
</style>
</head>
<body>
<h1>A Brief Test</h1>
<table border="1">
<tr bgcolor="lightslategray">
<td>
<p class="question">True or False: When completed, the Eiffel
Tower was the tallest building in the world.</p>
</td>
</tr>
<tr>
<td>
<p class="right">
<b>true:</b>
<br>You're correct!  The Eiffel Tower was the world's tallest
building until 1930.</p>
</td>
</tr>
<tr>
<td>
<p class="wrong">
<b>false:</b>
<br>No, the Eiffel Tower was the world's tallest building for
over 30 years.</p>
</td>
</tr>
</table>
<br>
<table border="1">
<tr bgcolor="lightslategray">
<td>
<p class="question">True or False: New York's Empire State Building
knocked the Eiffel Tower from its pedestal.</p>
</td>
</tr>
<tr>
<td>
<p class="wrong">
<b>true:</b>
<br>No, that's not correct.</p>
</td>
</tr>

<tr>
<td>
<p class="right">
<b>false:</b>
<br>Correct!  New York's Chrysler Building, completed in 1930,
became the world's tallest.</p>
</td>
</tr>
</table>
<br>
</body>
</html>

When rendered, this stylesheet looks like Figure A-2.

Figure A-2. Document generated with <xsl:apply-imports>

<xsl:apply-templates> Instructs the XSLT processor to apply the appropriate templates to a node-set.

Category

Instruction

Required Attributes

None.

Optional Attributes

select
Contains an XPath expression that selects the nodes to which templates should be applied. Valid values include * to select the entire node-set. Without this attribute, all element children of the current node are selected.

mode
Defines a processing mode, a convenient syntax that lets you write specific templates for specific purposes. For example, I could write an <xsl:template> with mode="toc" to process a node for the table of contents of a document, and other <xsl:template>s with mode="print", mode="online", mode="index", etc. to process the same information for different purposes.

Content

The <xsl:apply-templates> element can contain any number of <xsl:sort> and <xsl:with-param> elements. In most cases, <xsl:apply-templates> is empty.

Appears in

<xsl:apply-templates> appears inside a template.

Defined in

XSLT section 5.4, Applying Template Rules.

Example

In our case study (see Chapter 9, "Case Study: The Toot-O-Matic"), we needed to create several different outputs from the same data. We addressed this need with the mode attribute of the <xsl:apply-templates> element. Here's the main template (match="/"):

<xsl:template match="/">
  <xsl:apply-templates select="tutorial" mode="build-main-index"/>
  <redirect:write select="concat($curDir, $fileSep, 'index.html')">
    <xsl:apply-templates select="tutorial" mode="build-main-index"/>
  </redirect:write>
  <xsl:apply-templates select="tutorial" mode="build-section-indexes"/>
  <xsl:apply-templates select="tutorial" mode="build-individual-panels"/>
  <xsl:apply-templates select="tutorial" mode="generate-graphics"/>
  <xsl:apply-templates select="tutorial" mode="generate-pdf-file">
    <xsl:with-param name="page-size" select="'ltr'"/>
  </xsl:apply-templates>

  <xsl:apply-templates select="tutorial" mode="generate-pdf-file">
    <xsl:with-param name="page-size" select="'a4'"/>
  </xsl:apply-templates>
  <xsl:apply-templates select="tutorial" mode="generate-zip-file"/>
</xsl:template>

Notice that this example selects the <tutorial> element eight times, but applies templates with a different mode (or different parameters for the same mode) each time.

<xsl:attribute> Allows you to create an attribute in the output document. The advantage of <xsl:attribute> is that it allows you to build the attribute's value from parts of the input document, hardcoded text, values returned by functions, and any other value you can access from your stylesheet.

Category

Instruction

Required Attributes

name
The name attribute defines the name of the attribute created by the <xsl:attribute> element. (No matter how you try to say this, talking about the attributes of the <xsl:attribute> element is confusing, isn't it?)

Optional Attributes

namespace
The namespace attribute defines the namespace URI that should be used for this attribute in the output document. You don't have control over the namespace prefix used; the only thing you specify with the namespace attribute is the URI of the namespace.

Content

An XSLT template. In other words, you can build the contents of an attribute with <xsl:choose> elements, <xsl:text>, and <xsl:value-of> elements.

Appears in

<xsl:attribute> appears inside a template.

Defined in

XSLT section 7.1.3, Creating Attributes with xsl:attribute.

Example

For this example, we want to create an HTML table from the following XML document:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

We'll create a table that has each <listitem> in a separate row in the right column of the table, and a single cell with rowspan equal to the number of <listitem> elements in the XML document on the left. Clearly we can't hardcode a value for the rowspan attribute because the number of <listitem>s can change. This stylesheet uses <xsl:attribute> to do what we want:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="list/title"/></title>
      </head>
      <body>
        <xsl:apply-templates select="list"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="list">
    <table border="1" width="75%">
      <tr>
        <td bgcolor="lightslategray" width="100" align="right">
          <xsl:attribute name="rowspan">
            <xsl:value-of select="count(listitem)"/>
          </xsl:attribute>
          <p style="font-size: 125%">
            <xsl:value-of select="title"/>
          </p>
        </td>
        <td>
          <xsl:value-of select="listitem[1]"/>
        </td>
      </tr>
      <xsl:for-each select="listitem">
        <xsl:if test="position() > 1">
          <tr>
            <td>
              <xsl:value-of select="."/>
            </td>
          </tr>
        </xsl:if>
      </xsl:for-each>
    </table>
  </xsl:template>

</xsl:stylesheet>

Here is the generated HTML document:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Albums I've bought recently:</title>
</head>
<body>
<table width="75%" border="1">
<tr>
<td align="right" width="100" rowspan="7" bgcolor="lightslategray">
<p style="font-size: 125%">Albums I've bought recently:</p>
</td><td>The Sacred Art of Dub</td>
</tr>
<tr>
<td>Only the Poor Man Feel It</td>
</tr>
<tr>
<td>Excitable Boy</td>
</tr>
<tr>
<td>Aki Special</td>
</tr>
<tr>
<td>Combat Rock</td>
</tr>
<tr>
<td>Talking Timbuktu</td>
</tr>
<tr>
<td>The Birth of the Cool</td>
</tr>
</table>
</body>
</html>

Notice that the <td> element had several attributes hardcoded on it; those attributes are combined with the attribute we created with <xsl:attribute>. You can have as many <xsl:attribute> elements as you want, but they must appear together as the first thing inside the element to which you add attributes. Figure A-3 shows how our generated HTML document looks.

Figure A-3. Document with generated Attributes

Be aware that in this instance, we could have used an attribute-value template. You could generate the value of the rowspan attribute like this:

<td bgcolor="lightslategray" rowspan="{count(listitem)}"
  width="100" align="right">

The expression in curly braces ({}) is evaluated and replaced with whatever its value happens to be. In this case, count(listitem) returns the number 7, which becomes the value of the rowspan attribute.

<xsl:attribute-set> Allows you to define a group of attributes for the output document. You can then reference the entire attribute set with its name, rather than create all attributes individually.

Category

Top-level element

Required Attributes

name
Defines the name of this attribute set.

Optional Attributes

use-attribute-sets
Lists one or more attribute sets that should be used by this attribute set. If you specify more than one set, separate their names with whitespace characters. You can use this attribute to embed other <xsl:attribute-set>s in this one, but be aware that an <xsl:attribute-set> that directly or indirectly embeds itself results in an error. In other words, if attribute set A embeds attribute set B, which in turn embeds attribute set C, which in turn embeds attribute set A, the XSLT processor will signal an error.

Content

One or more <xsl:attribute> elements.

Appears in

<xsl:stylesheet>. <xsl:attribute-set> is a top-level element and can only appear as a child of <xsl:stylesheet>.

Defined in

XSLT section 7.1.4, Named Attribute Sets.

Example

For this example, we'll create a stylesheet that defines attribute sets for regular text, emphasized text, and large text. Just for variety's sake, we'll use the Extensible Stylesheet Language Formatting Objects (XSL-FO) specification to convert our XML document into a PDF file. Here's our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <xsl:output method="html"/>

  <xsl:attribute-set name="regular-text">
    <xsl:attribute name="font-size">12pt</xsl:attribute>
    <xsl:attribute name="font-family">sans-serif</xsl:attribute>
  </xsl:attribute-set>

  <xsl:attribute-set name="emphasized-text" use-attribute-sets="regular-text">
    <xsl:attribute name="font-style">italic</xsl:attribute>
  </xsl:attribute-set>

  <xsl:attribute-set name="large-text" use-attribute-sets="regular-text">
    <xsl:attribute name="font-size">18pt</xsl:attribute>
    <xsl:attribute name="font-weight">bold</xsl:attribute>
    <xsl:attribute name="space-after.optimum">21pt</xsl:attribute>
  </xsl:attribute-set>

  <xsl:template match="/">
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
      <fo:layout-master-set>
        <fo:simple-page-master margin-right="75pt" margin-left="75pt"
          page-height="11in" page-width="8.5in"
          margin-bottom="25pt" margin-top="25pt" master-name="main">
          <fo:region-before extent="25pt"/>
          <fo:region-body margin-top="50pt" margin-bottom="50pt"/>
          <fo:region-after extent="25pt"/>
        </fo:simple-page-master>
        <fo:page-sequence-master master-name="standard">
          <fo:repeatable-page-master-alternatives>
            <fo:conditional-page-master-reference master-name="main"
              odd-or-even="any"/>
          </fo:repeatable-page-master-alternatives>
        </fo:page-sequence-master>
      </fo:layout-master-set>

      <fo:page-sequence master-name="standard">
        <fo:flow flow-name="xsl-region-body">
          <xsl:apply-templates select="list"/>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>
  </xsl:template>

  <xsl:template match="list">
    <fo:block xsl:use-attribute-sets="large-text">
      <xsl:value-of select="title"/>
    </fo:block>
    <fo:list-block provisional-distance-between-starts="0.4cm"
      provisional-label-separation="0.15cm">
      <xsl:for-each select="listitem">
        <fo:list-item start-indent="0.5cm" space-after.optimum="17pt">
          <fo:list-item-label>
            <fo:block xsl:use-attribute-sets="regular-text">*</fo:block>
          </fo:list-item-label>
          <fo:list-item-body>
            <fo:block xsl:use-attribute-sets="emphasized-text">
              <xsl:value-of select="."/>
            </fo:block>
          </fo:list-item-body>
        </fo:list-item>
      </xsl:for-each>
    </fo:list-block>
  </xsl:template>

</xsl:stylesheet>

Notice that both the emphasized-text and large-text attribute sets use the regular-text attribute set as a base. In the case of large-text, the font-size attribute defined in the large-text attribute set overrides the font-size attribute included from the regular-text attribute set. We'll apply our stylesheet to the following XML document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

The stylesheet generates this messy-looking file of formatting objects, which describe how the text of our XML source document should be rendered:

<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="main" margin-top="25pt"
margin-bottom="25pt" page-width="8.5in" page-height="11in"
margin-left="75pt" margin-right="75pt">
<fo:region-before extent="25pt"/>
<fo:region-body margin-bottom="50pt" margin-top="50pt"/>
<fo:region-after extent="25pt"/>
</fo:simple-page-master>
<fo:page-sequence-master master-name="standard">
<fo:repeatable-page-master-alternatives>
<fo:conditional-page-master-reference odd-or-even="any" master-name="main"/>
</fo:repeatable-page-master-alternatives>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-name="standard">
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="18pt" font-family="sans-serif"
font-weight="bold" space-after.optimum="21pt">A few of my
favorite albums</fo:block>
<fo:list-block provisional-label-separation="0.15cm"
provisional-distance-between-starts="0.4cm">
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">A Love Supreme</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">Beat Crazy</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">Here Come the Warm Jets</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">Kind of Blue</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">London Calling</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">Remain in Light</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">The Joshua Tree</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item space-after.optimum="17pt" start-indent="0.5cm">
<fo:list-item-label>
<fo:block font-size="12pt" font-family="sans-serif">*</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block font-size="12pt" font-family="sans-serif"
font-style="italic">The Indestructible Beat of Soweto</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:flow>
</fo:page-sequence>
</fo:root>

Be aware that as of this writing (May 2001), the XSL-FO specification isn't final, so there's no guarantee that these formatting objects will work correctly with future XSL-FO tools. Here's how we invoke the Apache XML Project's FOP (Formatting Objects to PDF translator) tool to create a PDF:

java org.apache.fop.apps.CommandLine test.fo test.pdf

The FOP tool creates a PDF that looks like Figure A-4.

Figure A-4. PDF generated from an XSL-FO file

<xsl:call-template> Lets you invoke a particular template by name. This invocation is a convenient way to create commonly used output. For example, if you create an HTML page and all your HTML pages have the same masthead and footer, you could define templates named masthead and footer, then use <xsl:call-template> to invoke those templates as needed.

Category

Instruction

Required Attributes

name
The name of the template you're invoking.

Optional Attributes

None.

Content

This element can contain any number of optional <xsl:with-param> elements.

Appears in

<xsl:call-template> appears inside a template.

Defined in

XSLT section 6, Named Templates.

Example

The <xsl:call-template> element gives you an excellent way to create modular stylesheets. In our case study (see Chapter 9, "Case Study: The Toot-O-Matic"), we need to generate common items at the top and bottom of every HTML page we generate. We build a navigation bar and title bar at the top of each panel in a similar way. Rather than intermingle these templates with the rest of our stylesheets, we put the templates for the common sections of the HTML pages in a separate stylesheet, then reference them when needed.

<xsl:call-template name="dw-masthead"/>
<xsl:call-template name="dw-title-bar"/>
<xsl:call-template name="dw-nav-bar">
  <xsl:with-param name="includeMain" select="'youBetcha'"/>
  <xsl:with-param name="sectionNumber" select="$sectionNumber"/>
  <xsl:with-param name="position" select="$pos"/>
  <xsl:with-param name="last" select="$last"/>
  <xsl:with-param name="topOrBottom" select="'top'"/>
  <xsl:with-param name="oneOrTwo" select="'two'"/>
</xsl:call-template>

<!-- Processing for the main body of the page goes here -->

<xsl:call-template name="dw-nav-bar">
  <xsl:with-param name="includeMain" select="'youBetcha'"/>
  <xsl:with-param name="sectionNumber" select="$sectionNumber"/>
  <xsl:with-param name="position" select="$pos"/>
  <xsl:with-param name="last" select="$last"/>
  <xsl:with-param name="topOrBottom" select="'bottom'"/>
  <xsl:with-param name="oneOrTwo" select="'two'"/>
</xsl:call-template>
<xsl:call-template name="dw-footer"/>

In this code fragment, we've invoked four templates to generate the look and feel we want our HTML pages to have. If we decide to change the look and feel of our tutorials, changing those four named templates lets us change the look and feel by simply transforming the XML document again. See Section 9.5.5, "Generating the Individual Panels" in Chapter 9, "Case Study: The Toot-O-Matic" for details on how this works.

<xsl:choose> The <xsl:choose> element is XSLT's version of the switch or case statement found in many procedural programming languages.

Category

Instruction

Required Attributes

None.

Optional Attributes

None.

Content

Contains one or more <xsl:when> elements and might contain a single <xsl:otherwise> element. Any <xsl:otherwise> elements must be the last element inside <xsl:choose>.

Appears in

<xsl:choose> appears inside a template.

Defined in

XSLT section 9.2, Conditional Processing with xsl:choose.

Example

Here's an example that uses <xsl:choose> to select the background color for the rows of an HTML table. We cycle among four different values, using <xsl:choose> to determine the value of the bgcolor attribute in the generated HTML document. Here's the XML document we'll use:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

And here's our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="list/title"/>
        </title>
      </head>
      <body>
        <h1><xsl:value-of select="list/title"/></h1>
        <table border="1">
          <xsl:for-each select="list/listitem">
            <tr>
              <td>
                <xsl:attribute name="bgcolor">
                  <xsl:choose>
                    <xsl:when test="position() mod 4 = 0">
                      <xsl:text>papayawhip</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 1">
                      <xsl:text>mintcream</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 2">
                      <xsl:text>lavender</xsl:text>
                    </xsl:when>
                    <xsl:otherwise>
                      <xsl:text>whitesmoke</xsl:text>
                    </xsl:otherwise>
                  </xsl:choose>
                </xsl:attribute>
                <xsl:value-of select="."/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

We use <xsl:choose> to determine the background color of each generated <td> element. Here's the generated HTML document, which cycles through the various background colors:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Albums I've bought recently:</title>
</head>
<body>
<h1>Albums I've bought recently:</h1>
<table border="1">
<tr>
<td bgcolor="mintcream">The Sacred Art of Dub</td>
</tr>
<tr>
<td bgcolor="lavender">Only the Poor Man Feel It</td>
</tr>
<tr>
<td bgcolor="whitesmoke">Excitable Boy</td>
</tr>
<tr>
<td bgcolor="papayawhip">Aki Special</td>
</tr>
<tr>
<td bgcolor="mintcream">Combat Rock</td>
</tr>
<tr>
<td bgcolor="lavender">Talking Timbuktu</td>
</tr>
<tr>
<td bgcolor="whitesmoke">The Birth of the Cool</td>
</tr>
</table>
</body>
</html>

When rendered, our HTML document looks like Figure A-5.

Figure A-5. Document cycling among different background colors

<xsl:comment> Allows you to create a comment in the output document. Comments are sometimes used to add legal notices, disclaimers, or information about when the output document was created. Another useful application of the <xsl:comment> element is the generation of CSS definitions or JavaScript code in an HTML document.

Category

Instruction

Required Attributes

None.

Optional Attributes

None.

Content

An XSLT template.

Appears in

<xsl:comment> appears in a template.

Defined in

XSLT section 7.4, Creating Comments.

Example

Here's a stylesheet that generates a comment to define CSS styles in an HTML document:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>XSLT and CSS Demo</title>
        <style>
          <xsl:comment>
            p.big      {font-size: 125%; font-weight: bold}
            p.green    {color: green; font-weight: bold}
            p.red      {color: red; font-style: italic}
          </xsl:comment>
        </style>
      </head>
      <body>
        <xsl:apply-templates select="list/title"/>
        <xsl:apply-templates select="list/listitem"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="title">
    <p class="big"><xsl:value-of select="."/></p>
  </xsl:template>

  <xsl:template match="listitem">
    <xsl:choose>
      <xsl:when test="position() mod 2">
        <p class="green"><xsl:value-of select="."/></p>
      </xsl:when>
      <xsl:otherwise>
        <p class="red"><xsl:value-of select="."/></p>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

This stylesheet creates three CSS styles inside an HTML comment. We'll apply the stylesheet to this document:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

The stylesheet will apply one CSS style to the <title> element and will alternate between two CSS styles for the <listitem>s. Here's the generated HTML:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>XSLT and CSS Demo</title>
<style>
<!--
            p.big      {font-size: 125%; font-weight: bold}
            p.green    {color: green; font-weight: bold}
            p.red      {color: red; font-style: italic}
          -->
</style>
</head>
<body>
<p class="big">Albums I've bought recently:</p>
<p class="green">The Sacred Art of Dub</p>
<p class="red">Only the Poor Man Feel It</p>
<p class="green">Excitable Boy</p>
<p class="red">Aki Special</p>
<p class="green">Combat Rock</p>
<p class="red">Talking Timbuktu</p>
<p class="green">The Birth of the Cool</p>
</body>
</html>

When rendered, the document looks like Figure A-6.

Figure A-6. Document with generated comment nodes

<xsl:copy> Makes a shallow copy of an element to the result tree. This element only copies the current node and its namespace nodes. The children of the current node and any attributes it has are not copied.

Category

Instruction

Required Attributes

None.

Optional Attributes

use-attribute-sets
Lists one or more attribute sets that should be used by this element. If you specify more than one attribute set, separate their names with whitespace characters. See the description of the <xsl:attribute-set> element for more information.

Content

An XSLT template.

Appears in

<xsl:copy> appears in a template.

Defined in

XSLT section 7.5, Copying.

Example

We'll demonstrate <xsl:copy> with an example that copies an element to the result tree. Notice that we do not specifically request that the attribute nodes of the source document be processed, so the result tree will not contain any attributes. Here is our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="*">
    <xsl:copy>
      <xsl:apply-templates/>

    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

We'll test our stylesheet with the following XML document:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here are the results:

<?xml version="1.0" encoding="UTF-8"?>
<report>
  <title>Miles Flown in 2001</title>
  <month>
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month>
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month>
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month>
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

The <xsl:copy> does a shallow copy, which gives you more control over the output than the <xsl:copy-of> element does. However, you must explicitly specify any child nodes or attribute nodes you would like copied to the result tree. The <xsl:apply-templates> element selects all text, element, comment, and processing-instruction children of the current element; without this element, the result tree would contain only a single, empty <report> element. Compare this approach with the example in the <xsl:copy-of> element.

<xsl:copy-of> Copies things to the result tree. The select attribute defines the content to be copied. If the select attribute identifies a result-tree fragment, the complete fragment is copied to the result tree. If select identifies a node-set, all nodes in the node-set are copied to the result tree in document order; unlike <xsl:copy>, the node is copied in its entirety, including any namespace nodes, attribute nodes, and child nodes. If the select attribute identifies something other than a result-tree fragment or a node-set, it is converted to a string and inserted into the result tree.

Category

Instruction

Required Attributes

select
Contains an XPath expression that defines the nodes to be copied to the output document.

Optional Attributes

None.

Content

None. <xsl:copy-of> is an empty element.

Appears in

<xsl:copy-of> appears inside a template.

Defined in

XSLT section 11.3, Using Values of Variables and Parameters with xsl:copy-of.

Example

We'll demonstrate <xsl:copy-of> with a simple stylesheet that copies the input document to the result tree. Here is our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="/">
    <xsl:copy-of select="."/>
  </xsl:template>

</xsl:stylesheet>

We'll test our stylesheet with the following document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

When we transform the XML document, the results are strikingly similar to the input document:

<?xml version="1.0" encoding="UTF-8"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

The only difference between the two documents is that the stylesheet engine has added an encoding to the XML declaration. Compare this to the example in the <xsl:copy> element.

<xsl:decimal-format> Defines a number format to be used when writing numeric values to the output document. If the <decimal-format> does not have a name, it is assumed to be the default number format used for all output. On the other hand, if a number format is named, it can be referenced from the format-number() function.

Category

Top-level element

Required Attributes

None.

Optional Attributes

name
Gives a name to this format.

decimal-separator
Defines the character (usually either a period or comma) used as the decimal point. This character is used both in the format string and in the output. The default value is the period character (.).

grouping-separator
Defines the character (usually either a period or comma) used as the thousands separator. This character is used both in the format string and in the output. The default value is the comma (,).

infinity
Defines the string used to represent infinity. Be aware that XSLT's number facilities support both positive and negative infinity. This string is used only in the output. The default value is the string "Infinity".

minus-sign
Defines the character used as the minus sign. This character is used only in the output. The default value is the hyphen character (-, #x2D).

NaN
Defines the string displayed when the value to be formatted is not a number. This string is used only in the output; the default value is the string "NaN".

percent
Defines the character used as the percent sign. This character is used both in the format string and in the output. The default value is the percent sign (%).

per-mille
Defines the character used as the per-mille sign. This character is used both in the format string and in the output. The default value is the Unicode per-mille character (#x2030).

zero-digit
Defines the character used for the digit zero. This character is used both in the format string and in the output. The default is the digit zero (0).

digit
Defines the character used in the format string to stand for a digit. The default is the number sign character (#).

pattern-separator
Defines the character used to separate the positive and negative subpatterns in a pattern. The default value is the semicolon (;). This character is used only in the format string.

Content

None. <xsl:decimal-format> is an empty element.

Appears in

<xsl:decimal-format> is a top-level element and can only appear as a child of <xsl:stylesheet>.

Defined in

XSLT section 12.3, Number Formatting.

Example

Here is a stylesheet that defines two <decimal-format>s:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <xsl:output method="text"/>

  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:decimal-format name="f1"
    decimal-separator=":"
    grouping-separator="/"/>

  <xsl:decimal-format name="f2"
    infinity="Really, really big"
    NaN="[not a number]"/>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the <decimal-format> element:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(1528.3, '#/###:00', 'f1')=</xsl:text>
    <xsl:value-of select="format-number(1528.3, '#/###:00;-#/###:00', 'f1')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(1 div 0, '###,###.00', 'f2')=</xsl:text>
    <xsl:value-of select="format-number(1 div 0, '###,###.00', 'f2')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(blue div orange, '#.##', 'f2')=</xsl:text>
    <xsl:value-of select="format-number(blue div orange, '#.##', 'f2')"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=current()/@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>     (</xsl:text>
      <xsl:value-of
        select="format-number(miles-flown div sum(//miles-flown), '##%')"/>
      <xsl:text> of all miles flown, </xsl:text>
      <xsl:value-of
        select="format-number(miles-earned div sum(//miles-earned), '##%')"/>
      <xsl:text> of all miles earned.)</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:text>   Total miles flown: </xsl:text>
    <xsl:value-of select="format-number(sum(//miles-flown), '##,###')"/>
    <xsl:text>, total miles earned: </xsl:text>
    <xsl:value-of select="format-number(sum(//miles-earned), '##,###')"/>
  </xsl:template>

</xsl:stylesheet>

We'll use this stylesheet against the following document:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

When we process this document with the stylesheet, here are the results:

Tests of the <decimal-format> element:

   format-number(1528.3, '#/###:00', 'f1')=1/528:30
   format-number(1 div 0, '###,###.00', 'f2')=Really, really big
   format-number(blue div orange, '#.##', 'f2')=[not a number]

   January - 12,379 miles flown, 35,215 miles earned.
     (15% of all miles flown, 15% of all miles earned.)

   February - 32,857 miles flown, 92,731 miles earned.
     (39% of all miles flown, 39% of all miles earned.)

   March - 19,920 miles flown, 76,725 miles earned.
     (24% of all miles flown, 32% of all miles earned.)

   April - 18,903 miles flown, 31,781 miles earned.
     (22% of all miles flown, 13% of all miles earned.)

   Total miles flown: 84,059, total miles earned: 236,452
<xsl:element> Allows you to create an element in the output document. It works similarly to the <xsl:attribute> element.

Category

Instruction

Required Attributes

name
Defines the name of this element. A value of name="fred" will produce a <fred> element in the output document.

Optional Attributes

namespace
Defines the namespace used for this attribute.

use-attribute-sets
Lists one or more attribute sets that should be used by this element. If you specify more than one attribute set, separate their names with whitespace characters.

Content

An XSLT template.

Appears in

<xsl:element> appears inside a template.

Defined in

XSLT section 7.1.2, Creating Elements with xsl:element.

Example

We'll use a generic stylesheet that copies the input document to the result tree, with one exception: all attributes in the original documents are converted to child elements in the result tree. The name of the new element will be the name of the format attribute, and its text will be the value of the attribute. Because we don't know the name of the attribute until we process the XML source document, we must use the <xsl:element> element to create the result tree. Here's how our stylesheet looks:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="*">
    <xsl:element name="{name()}">
      <xsl:for-each select="@*">
        <xsl:element name="{name()}">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:apply-templates select="*|text()"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

This stylesheet uses the <xsl:element> element in two places: first to create a new element with the same name as the original element, and second to create a new element with the same name as each attribute. We'll apply the stylesheet to this document:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here are our results:

<?xml version="1.0" encoding="UTF-8"?>
<report>
  <title>Miles Flown in 2001</title>
  <month><sequence>01</sequence>
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month><sequence>02</sequence>
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month><sequence>03</sequence>
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month><sequence>04</sequence>
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

The <xsl:element> element created all the elements in the output document, including the <sequence> elements that were created from the sequence attributes in the original document.

<xsl:fallback> Defines a template that should be used when an extension element can't be found.

Category

Instruction

Required Attributes

None.

Optional Attributes

None.

Content

An XSLT template.

Appears in

<xsl:fallback> appears inside a template.

Defined in

XSLT section 15, Fallback.

Example

Here is a stylesheet that uses <xsl:fallback> to terminate the transformation if an extension element can't be found:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:db="xalan://DatabaseExtension"
  extension-element-prefixes="db">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="report/title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="report/title"/></h1>
        <xsl:for-each select="report/section">
          <h2><xsl:value-of select="title"/></h2>
          <xsl:for-each select="dbaccess">
            <db:accessDatabase>
              <xsl:fallback>
                <xsl:message terminate="yes">
                  Database library not available!
                </xsl:message>
              </xsl:fallback>
            </db:accessDatabase>
          </xsl:for-each>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

When we use this stylesheet to transform a document, the <xsl:fallback> element is processed if the extension element can't be found:

Database library not available!

Processing terminated using xsl:message

In this case, the extension element is the Java class DatabaseExtension. If, for whatever reason, that class can't be loaded, the <xsl:fallback> element is processed. Note that the <xsl:fallback> element is processed only when the extension element can't be found; if the code that implements that extension element is found, but fails, it must be handled some other way. Also be aware that the exact format of the message and the gracefulness of stylesheet termination will vary from one XSLT processor to the next.

<xsl:for-each> Acts as XSLT's iteration operator. This element has a select attribute that selects some nodes from the current context.

Category

Instruction

Required Attributes

select
Contains an XPath expression that selects nodes from the current context.

Optional Attributes

None.

Content

<xsl:for-each> contains a template that is evaluated against each of the selected nodes. The <xsl:for-each> element can contain one or more <xsl:sort> elements to order the selected nodes before they are processed. All <xsl:sort> elements must appear first, before the template begins.

Appears in

<xsl:for-each> appears inside a template.

Defined in

XSLT section 8, Repetition.

Example

We'll demonstrate the <xsl:for-each> element with the following stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:variable name="complicatedVariable">
    <xsl:choose>
      <xsl:when test="count(//listitem) > 10">
        <xsl:text>really long list</xsl:text>
      </xsl:when>
      <xsl:when test="count(//listitem) > 5">
        <xsl:text>moderately long list</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>fairly short list</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Here is a </xsl:text>
    <xsl:value-of select="$complicatedVariable"/>
    <xsl:text>:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:variable name="listitems" select="list/listitem"/>
    <xsl:call-template name="processListitems">
      <xsl:with-param name="items" select="$listitems"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="processListitems">
    <xsl:param name="items"/>
    <xsl:for-each select="$items">
      <xsl:value-of select="position()"/>
      <xsl:text>.  </xsl:text>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

In this stylesheet, we use an <xsl:param> named items to illustrate the <xsl:for-each> element. The items parameter contains some number of <listitem> elements from the XML source document; the <xsl:for-each> element iterates through all those elements and processes each one. We'll use our stylesheet with the following XML document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

When we run the transformation, here are the results:

Here is a moderately long list:
1.  A Love Supreme
2.  Beat Crazy
3.  Here Come the Warm Jets
4.  Kind of Blue
5.  London Calling
6.  Remain in Light
7.  The Joshua Tree
8.  The Indestructible Beat of Soweto

The <xsl:for-each> element has iterated through all the <listitem> elements from the XML source document and has processed each one.

<xsl:if> Implements an if statement. It contains a test attribute and an XSLT template. If the test attribute evaluates to the boolean value true, the XSLT template is processed. This element implements an if statement only; if you need an if-then-else statement, use the <xsl:choose> element with a single <xsl:when> and a single <xsl:otherwise>.

Category

Instruction

Required Attributes

test
The test attribute contains a boolean expression. If it evaluates to the boolean value true, then the XSLT template inside the <xsl:if> element is processed.

Optional Attributes

None.

Content

An XSLT template.

Appears in

<xsl:if> appears inside a template.

Defined in

XSLT section 9.1, Conditional Processing with xsl:if.

Example

We'll illustrate the <xsl:if> element with the following stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Here are the odd-numbered items from the list:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="list/listitem">
      <xsl:if test="(position() mod 2) = 1">
        <xsl:number format="1. "/>
        <xsl:value-of select="."/>
        <xsl:value-of select="$newline"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

This stylesheet uses the <xsl:if> element to see if a given <listitem>'s position is an odd number. If it is, we write it to the result tree. We'll test our stylesheet with this XML document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

When we run this transformation, here are the results:

Here are the odd-numbered items from the list:
1. A Love Supreme
3. Here Come the Warm Jets
5. London Calling
7. The Joshua Tree
<xsl:import> Allows you to import the templates found in another XSLT stylesheet. Unlike <xsl:include>, all templates imported with <xsl:import> have a lower priority than those in the including stylesheet. Another difference between <xsl:include> and <xsl:import> is that <xsl:include> can appear anywhere in a stylesheet, while <xsl:import> can appear only at the beginning.

Category

Top-level element

Required Attributes

href
Defines the URI of the imported stylesheet.

Optional Attributes

None.

Content

None. <xsl:import> is an empty element.

Appears in

<xsl:import> is a top-level element and can appear only as a child of <xsl:stylesheet>.

Defined in

XSLT section 2.6.2, Stylesheet Import.

Example

Here is a simple stylesheet that we'll import:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:apply-templates select="list/title"/>
    <xsl:apply-templates select="list/listitem"/>
  </xsl:template>

  <xsl:template match="title">
    <xsl:value-of select="."/>
    <xsl:text>: </xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="listitem">
    <xsl:text>HERE IS LISTITEM NUMBER </xsl:text>
    <xsl:value-of select="position()"/>
    <xsl:text>:  </xsl:text>
    <xsl:value-of select="."/>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

We'll test both this stylesheet and the one that imports it with this XML document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

When we process our XML source document with this stylesheet, here are the results:

A few of my favorite albums:

HERE IS LISTITEM NUMBER 1:  A Love Supreme
HERE IS LISTITEM NUMBER 2:  Beat Crazy
HERE IS LISTITEM NUMBER 3:  Here Come the Warm Jets
HERE IS LISTITEM NUMBER 4:  Kind of Blue
HERE IS LISTITEM NUMBER 5:  London Calling
HERE IS LISTITEM NUMBER 6:  Remain in Light
HERE IS LISTITEM NUMBER 7:  The Joshua Tree
HERE IS LISTITEM NUMBER 8:  The Indestructible Beat of Soweto

Now we'll use <xsl:import> in another stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:import href="listitem.xsl"/>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:apply-templates select="list/title"/>
    <xsl:apply-templates select="list/listitem"/>
  </xsl:template>

  <xsl:template match="listitem">
    <xsl:value-of select="position()"/>
    <xsl:text>.  </xsl:text>
    <xsl:value-of select="."/>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

Here are the results created by our second stylesheet:

A few of my favorite albums:

1.  A Love Supreme
2.  Beat Crazy
3.  Here Come the Warm Jets
4.  Kind of Blue
5.  London Calling
6.  Remain in Light
7.  The Joshua Tree
8.  The Indestructible Beat of Soweto

Notice that both stylesheets had a template with match="listitem". The template in the imported stylesheet has a lower priority, so it is not used. Only the imported stylesheet has a template with match="title", so the imported template is used for the <title> element.

<xsl:include> Allows you to include another XSLT stylesheet. This element allows you to put common transformations in a separate stylesheet, then include the templates from that stylesheet at any time. Unlike <xsl:import>, all templates included with <xsl:include> have the same priority as those in the including stylesheet. Another difference is that <xsl:include> can appear anywhere in a stylesheet, while <xsl:import> must appear at the beginning.

Category

Top-level element

Required Attributes

href
Defines the URI of the included stylesheet.

Optional Attributes

None.

Content

None. <xsl:include> is an empty element.

Appears in

<xsl:include> is a top-level element and can appear only as a child of <xsl:stylesheet>.

Defined in

XSLT section 2.6.1, Stylesheet Inclusion.

Example

The <xsl:include> element is a good way to break your stylesheets into smaller pieces. (Those smaller pieces are often easier to reuse.) In our case study (see Chapter 9, "Case Study: The Toot-O-Matic"), we had a number of different stylesheets, each of which contained templates for a particular purpose. Here's how our <xsl:include> elements look:

<xsl:include href="toot-o-matic-variables.xsl"/>

<xsl:include href="xslt-utilities.xsl"/>
<xsl:include href="dw-style.xsl"/>

<xsl:include href="build-main-index.xsl"/>
<xsl:include href="build-section-indexes.xsl"/>
<xsl:include href="build-individual-panels.xsl"/>
<xsl:include href="build-graphics.xsl"/>
<xsl:include href="build-pdf-file.xsl"/>
<xsl:include href="build-zip-file.xsl"/>

Segmenting your stylesheets this way can make debugging simpler, as well. In our example here, all the rules for creating a PDF file are in the stylesheet build-pdf-file.xsl. If the PDF files are not built correctly, build-pdf-file.xsl is most likely the source of the problem. All visual elements of our generated HTML pages are created in the stylesheet dw-style.xsl. If we need to change the look of all the HTML pages, changing the templates in dw-style.xsl will do the trick.

<xsl:key> Defines an index against the current document. The element is defined with three attributes: a name, which names this index; a match, an XPath expression that describes the nodes to be indexed; and a use attribute, an XPath expression that defines the property used to create the index.

Category

Top-level element

Required Attributes

name
Defines a name for this key.

match
Represents an XPath expression that defines the nodes to be indexed by this key.

use
Represents an XPath expression that defines the property of the indexed nodes that will be used to retrieve nodes from the index.

Optional Attributes

None.

Content

None. <xsl:key> is an empty element.

Appears in

<xsl:key> is a top-level element and can only appear as a child of <xsl:stylesheet>.

Defined in

XSLT section 12.2, Keys.

Example

Here is a stylesheet that defines two <xsl:key> relations against an XML document:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*"/>

  <xsl:key name="language-index" match="defn" use="@language"/>
  <xsl:key name="term-ids"       match="term" use="@id"/>

  <xsl:param name="targetLanguage"/>

  <xsl:template match="/">
    <xsl:apply-templates select="glossary"/>
  </xsl:template>

  <xsl:template match="glossary">
    <html>
      <head>
        <title>
          <xsl:text>Glossary Listing: </xsl:text>
          <xsl:value-of select="key('language-index',
        $targetLanguage)[1]/preceding-sibling::term"/>
          <xsl:text> - </xsl:text>
          <xsl:value-of select="key('language-index',
        $targetLanguage)[last()]/preceding-sibling::term"/>
        </title>
      </head>
      <body>
        <h1>
          <xsl:text>Glossary Listing: </xsl:text>
          <xsl:value-of select="key('language-index',
        $targetLanguage)[1]/ancestor::glentry/term"/>
          <xsl:text> - </xsl:text>
          <xsl:value-of select="key('language-index',
        $targetLanguage)[last()]/ancestor::glentry/term"/>
        </h1>
        <xsl:for-each select="key('language-index', $targetLanguage)">
          <xsl:apply-templates select="ancestor::glentry"/>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  ...

</xsl:stylesheet>

For a complete discussion of this stylesheet, illustrating how the <xsl:key> relations are used, see Section 5.2.3, "Stylesheets That Use the key() Function" in Chapter 5, "Creating Links and Cross-References".

<xsl:message> Sends a message. How the message is sent can vary from one XSLT processor to the next, but it's typically written to the standard output device. This element is useful for debugging stylesheets.

Category

Instruction

Required Attributes

None.

Optional Attributes

terminate="yes"|"no"
If this attribute has the value yes, the XSLT processor stops execution after issuing this message. The default value for this attribute is no; if the <xsl:message> doesn't terminate the processor, the message is sent and processing continues.

Content

An XSLT template.

Appears in

<xsl:message> appears inside a template.

Defined in

XSLT section 13, Messages.

Example

Here's a stylesheet that uses the <xsl:message> element to trace the transformation of an XML document. We'll use our list of recently purchased albums again:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

We'll list all of the purchased albums in an HTML table, with the background color of each row alternating through various colors. Our stylesheet uses an <xsl:choose> element inside an <xsl:attribute> element to determine the value of the bgcolor attribute. If a given <listitem> is converted to an HTML <tr> with a background color of lavender, we'll issue a celebratory message:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="list/title"/>
        </title>
      </head>
      <body>
        <h1><xsl:value-of select="list/title"/></h1>
        <table border="1">
          <xsl:for-each select="list/listitem">
            <tr>
              <td>
                <xsl:attribute name="bgcolor">
                  <xsl:choose>
                    <xsl:when test="position() mod 4 = 0">
                      <xsl:text>papayawhip</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 1">
                      <xsl:text>mintcream</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 2">
                      <xsl:text>lavender</xsl:text>
                      <xsl:message terminate="no">
                        <xsl:text>Table row #</xsl:text>
                        <xsl:value-of select="position()"/>
                        <xsl:text> is lavender!</xsl:text>
                      </xsl:message>
                    </xsl:when>
                    <xsl:otherwise>
                      <xsl:text>whitesmoke</xsl:text>
                    </xsl:otherwise>
                  </xsl:choose>
                </xsl:attribute>
                <xsl:value-of select="."/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Note that the XSLT specification doesn't define how the message is issued. When we use this stylesheet with Xalan 2.0.1, we get these results:

file:///D:/O'Reilly/XSLT/bookSamples/AppendixA/message.xsl; Line 32; Column 51;
Table row #2 is lavender!
file:///D:/O'Reilly/XSLT/bookSamples/AppendixA/message.xsl; Line 32; Column 51;
Table row #6 is lavender!

Xalan gives us feedback on the part of the stylesheet that generated each message. Saxon, on the other hand, keeps things short and sweet:

Table row #2 is lavender!
Table row #6 is lavender!

For variety's sake, here's how XT processes the <xsl:message> element:

file:/D:/O'Reilly/XSLT/bookSamples/AppendixA/test4.xml:5: Table row #2 is lavender!
file:/D:/O'Reilly/XSLT/bookSamples/AppendixA/test4.xml:9: Table row #6 is lavender!

XT gives information about the line in the XML source document that generated the message.

<xsl:namespace-alias> Allows you to define an alias for a namespace when using the namespace directly would complicate processing. This seldom-used element is the simplest way to write a stylesheet that generates another stylesheet.

Category

Top-level element

Required Attributes

stylesheet-prefix
Defines the prefix used in the stylesheet to refer to the namespace.

result-prefix
Defines the prefix for the namespace referred to by the alias. This prefix must be declared in the stylesheet, regardless of whether any elements in the stylesheet use it.

Optional Attributes

None.

Content

None. <xsl:namespace-alias> is an empty element.

Appears in

<xsl:stylesheet>. <xsl:namespace-alias> is a top-level element and can appear only as a child of <xsl:stylesheet>.

Defined in

XSLT section 7.1.1, Literal Result Elements.

Example

This element is not used frequently, and the reasons for its existence are based on the somewhat obscure case of an XSLT stylesheet that needs to generate another XSLT stylesheet. Our test case here creates a stylesheet that generates the identity transform, a stylesheet that simply copies any input document to the result tree. Here's our original stylesheet that uses the namespace alias:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xslout="(the namespace URI doesn't matter here)">

  <xsl:output method="xml" indent="yes"/>

  <xsl:namespace-alias stylesheet-prefix="xslout"
    result-prefix="xsl"/>

  <xsl:template match="/">
    <xslout:stylesheet version="1.0">
      <xslout:output method="xml"/>
      <xslout:template match="/">
        <xslout:copy-of select="."/>
      </xslout:template>
    </xslout:stylesheet>
  </xsl:template>

</xsl:stylesheet>

When we run this stylesheet with any XML document at all, we get a new stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xslout:stylesheet xmlns:xslout="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
<xslout:output method="xml"/>
<xslout:template match="/">
<xslout:copy-of select="."/>
</xslout:template>
</xslout:stylesheet>

You can take this generated stylesheet and use it to copy any XML document. In our original stylesheet, we use an <xsl:namespace-alias> because we have no other way of identifying to the XSLT processor with which XSLT elements should be processed and which ones should be treated as literals passed to the output. Using the namespace alias lets us generate the XSLT elements we need in our output. Notice in the result document that the correct namespace value was declared automatically on the <xslout:stylesheet> element.

<xsl:number> Counts something. It is most often used to number parts of a document, although it can also be used to format a numeric value.

Category

Instruction

Required Attributes

None.

Optional Attributes

count
The count attribute is an XPath expression that defines what should be counted.

level
This attribute defines what levels of the source tree should be considered when numbering elements. The three valid values for this attribute are single, multiple, and any:

single
Counts items at one level only. The XSLT processor goes to the first node in the ancestor-or-self axis that matches the count attribute, then counts that node plus all its preceding siblings that also match the count attribute.

multiple
Counts items at multiple levels. The XSLT processor looks at all ancestors of the current node and the current node itself, then it selects all of those nodes that match the count attribute.

any
Includes all of the current node's ancestors (as level="multiple" does) as well as all elements in the preceding axis.

In all of these cases, if the from attribute is used, the only ancestors that are examined are descendants of the nearest ancestor that matches the from attribute. In other words, with from="h1", the only nodes considered for counting are those that appear under the nearest <h1> attribute.

from
The from attribute is an XPath expression that defines where counting starts. For example, you can use the from attribute to say that counting should begin at the previous <h1> element.

value
An expression that is converted to a number. Using this attribute is a quick way to format a number; the element <xsl:number value="7" format="i:"/> returns the string "vii:".

format
The format attribute defines the format of the generated number:

format="1"
Formats a sequence of numbers as 1 2 3 4 5 6 7 8 9 10 11 ....

format="01"
Formats a sequence of numbers as 01 02 03 04 ... 09 10 11 ... 99 100 101 ....

format="a"
Formats a sequence of numbers as a b c d e f ... x y z aa ab ac ....

format="A"
Formats a sequence of numbers as A B C D E F ... X Y Z AA AB AC ....

format="i"
Formats a sequence of numbers as i ii iii iv v vi vii viii ix x ....

format="I"
Formats a sequence of numbers as I II III IV V VI VII VIII IX X ....

format="anything else"
How this works is depends on the XSLT processor you're using. The XSLT specification lists several other numbering schemes (Thai digits, Katakana numbering, traditional Hebrew numbering, etc.); check your XSLT processor's documentation to see which formats it supports. If the XSLT processor doesn't support the numbering scheme you requested, the XSLT spec requires that it use format="1" as the default.

lang
The lang attribute defines the language whose alphabet should be used. Different XSLT processors support different language values, so check the documentation of your favorite XSLT processor for more information.

letter-value
This attribute has the value alphabetic or traditional. There are a number of languages in which two letter-based numbering schemes are used; one assigns numeric values in alphabetic sequence, while the other uses a tradition native to that language. (Roman numerals -- a letter-based numbering scheme that doesn't use an alphabetic order -- are one example.) The default for this attribute is alphabetic.

grouping-separator
This attribute is the character that should be used between groups of digits in a generated number. The default is the comma (,).

grouping-size
This attribute defines the number of digits that appear in each group; the default is 3.

Content

None. <xsl:number> is an empty element.

Appears in

<xsl:number> appears inside a template.

Defined in

XSLT section 7.7, Numbering.

Example

To fully illustrate how <xsl:number> works, we'll need an XML document with many things to count. Here's the document we'll use:

<?xml version="1.0"?>
<book>
  <chapter>
    <title>Alfa Romeo</title>
    <sect1>
      <title>Bentley</title>
    </sect1>
    <sect1>
      <title>Chevrolet</title>
      <sect2>
        <title>Dodge</title>
        <sect3>
          <title>Eagle</title>
        </sect3>
      </sect2>
    </sect1>
  </chapter>
  <chapter>
    <title>Ford</title>
    <sect1>
      <title>GMC</title>
      <sect2>
        <title>Honda</title>
        <sect3>
          <title>Isuzu</title>
        </sect3>
        <sect3>
          <title>Javelin</title>
        </sect3>
        <sect3>
          <title>K-Car</title>
        </sect3>
        <sect3>
          <title>Lincoln</title>
        </sect3>
      </sect2>
      <sect2>
        <title>Mercedes</title>
      </sect2>
      <sect2>
        <title>Nash</title>
        <sect3>
          <title>Opel</title>
        </sect3>
        <sect3>
          <title>Pontiac</title>
        </sect3>
      </sect2>
      <sect2>
        <title>Quantum</title>
        <sect3>
          <title>Rambler</title>
        </sect3>
        <sect3>
          <title>Studebaker</title>
        </sect3>
      </sect2>
    </sect1>
    <sect1>
      <title>Toyota</title>
      <sect2>
        <title>Um, is there a car that starts with "U"?</title>
      </sect2>
    </sect1>
    <sect1>
      <title>Volkswagen</title>
    </sect1>
  </chapter>
</book>

We'll use <xsl:number> in several different ways to illustrate the various options we have in numbering things. We'll look at the stylesheet and the results, then we'll discuss them. Here's the stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:apply-templates select="book" mode="number-1"/>
    <xsl:apply-templates select="book" mode="number-2"/>
    <xsl:apply-templates select="book" mode="number-3"/>
    <xsl:apply-templates select="book" mode="number-4"/>
    <xsl:apply-templates select="book" mode="number-5"/>
    <xsl:apply-templates select="book" mode="number-6"/>

    <xsl:apply-templates select="book" mode="number-7"/>
  </xsl:template>

  <xsl:template match="book" mode="number-1">
    <xsl:text>Test #1: level="multiple",
         count="chapter|sect1|sect2|sect3",
         format="1.1.1.1. "</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3">
      <xsl:number level="multiple" count="chapter|sect1|sect2|sect3"
        format="1.1.1.1. "/>
        <xsl:value-of select="title"/>
        <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="book" mode="number-2">
    <xsl:text>Test #2: level="any",
         count="chapter|sect1|sect2|sect3",
         format="1. "</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3">
      <xsl:number level="any" count="chapter|sect1|sect2|sect3"
        format="1. "/>
        <xsl:value-of select="title"/>
        <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="book" mode="number-3">
    <xsl:text>Test #3: level="single",
         count="chapter|sect1|sect2|sect3",
         format="1.1.1.1. "</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3">
      <xsl:number level="single" count="chapter|sect1|sect2|sect3"
        format="1.1.1.1. "/>
        <xsl:value-of select="title"/>
        <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="book" mode="number-4">
    <xsl:text>Test #4: level="multiple",
         select=".//sect2",
         count="chapter|sect1|sect2",
         format="I-A-i: "</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select=".//sect2">
      <xsl:number level="multiple" count="chapter|sect1|sect2"
        format="I-A-i: "/>
        <xsl:value-of select="title"/>
        <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="book" mode="number-5">
    <xsl:text>Test #5: level="any",
         count="[various elements]"
         from="[various elements]"
         format="1.1.1.1. "</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select=".//sect3">
      <xsl:number level="any" from="book" count="chapter" format="1."/>
      <xsl:number level="any" from="chapter" count="sect1" format="1."/>
      <xsl:number level="any" from="sect1" count="sect2" format="1."/>
      <xsl:number level="any" from="sect2" count="sect3" format="1. "/>
      <xsl:value-of select="title"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="book" mode="number-6">
    <xsl:text>Test #6: level="any",
         count="chapter|sect1|sect2|sect3",
         grouping-separator=",",
         using a variable to start counting at 1000.</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3">
      <xsl:variable name="value1">
        <xsl:number level="any" count="chapter|sect1|sect2|sect3"/>
      </xsl:variable>
      <xsl:number value="$value1 + 999"
        grouping-separator="." grouping-size="3"/>
      <xsl:text>. </xsl:text>
      <xsl:value-of select="title"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template match="book" mode="number-7">
    <xsl:text>Test #7: level="multiple",
         count="chapter|sect1|sect2|sect3",
         format="1.1.1.1. ",
         selecting up to the first two <sect1> elements from chapter 2.</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="chapter[2]/sect1[position() < 3]">
      <xsl:for-each select="chapter|.//sect1|.//sect2|.//sect3">
        <xsl:number level="multiple" count="chapter|sect1|sect2|sect3"
          format="1.1.1.1. "/>
        <xsl:value-of select="title"/>
        <xsl:value-of select="$newline"/>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here are our results:

Test #1: level="multiple",
         count="chapter|sect1|sect2|sect3",
         format="1.1.1.1. "

1. Alfa Romeo
1.1. Bentley
1.2. Chevrolet
1.2.1. Dodge
1.2.1.1. Eagle
2. Ford
2.1. GMC
2.1.1. Honda
2.1.1.1. Isuzu
2.1.1.2. Javelin
2.1.1.3. K-Car
2.1.1.4. Lincoln
2.1.2. Mercedes
2.1.3. Nash
2.1.3.1. Opel
2.1.3.2. Pontiac
2.1.4. Quantum
2.1.4.1. Rambler
2.1.4.2. Studebaker
2.2. Toyota

2.2.1. Um, is there a car that starts with "U"?
2.3. Volkswagen

Test #2: level="any",
         count="chapter|sect1|sect2|sect3",
         format="1. "

1. Alfa Romeo
2. Bentley
3. Chevrolet
4. Dodge
5. Eagle
6. Ford
7. GMC
8. Honda
9. Isuzu
10. Javelin
11. K-Car
12. Lincoln
13. Mercedes
14. Nash
15. Opel
16. Pontiac
17. Quantum
18. Rambler
19. Studebaker
20. Toyota
21. Um, is there a car that starts with "U"?
22. Volkswagen

Test #3: level="single",
         count="chapter|sect1|sect2|sect3",
         format="1.1.1.1. "

1. Alfa Romeo
1. Bentley
2. Chevrolet
1. Dodge
1. Eagle
2. Ford
1. GMC
1. Honda
1. Isuzu
2. Javelin
3. K-Car
4. Lincoln
2. Mercedes
3. Nash
1. Opel
2. Pontiac
4. Quantum
1. Rambler
2. Studebaker
2. Toyota
1. Um, is there a car that starts with "U"?
3. Volkswagen

Test #4: level="multiple",
         select=".//sect2",
         count="chapter|sect1|sect2",
         format="I-A-i: "

I-B-i: Dodge
II-A-i: Honda
II-A-ii: Mercedes
II-A-iii: Nash
II-A-iv: Quantum
II-B-i: Um, is there a car that starts with "U"?

Test #5: level="any",
         count="[various elements]"
         from="[various elements]"
         format="1.1.1.1. "

1.2.1.1. Eagle
2.1.1.1. Isuzu
2.1.1.2. Javelin
2.1.1.3. K-Car
2.1.1.4. Lincoln
2.1.3.1. Opel
2.1.3.2. Pontiac
2.1.4.1. Rambler
2.1.4.2. Studebaker

Test #6: level="any",
         count="chapter|sect1|sect2|sect3",
         grouping-separator=",",
         using a variable to start counting at 1000.

1,000. Alfa Romeo
1,001. Bentley
1,002. Chevrolet
1,003. Dodge
1,004. Eagle
1,005. Ford
1,006. GMC
1,007. Honda
1,008. Isuzu
1,009. Javelin
1,010. K-Car
1,011. Lincoln
1,012. Mercedes
1,013. Nash
1,014. Opel
1,015. Pontiac
1,016. Quantum
1,017. Rambler
1,018. Studebaker
1,019. Toyota
1,020. Um, is there a car that starts with "U"?
1,021. Volkswagen

Test #7: level="multiple",
         count="chapter|sect1|sect2|sect3",
         format="1.1.1.1. ",
         selecting up to the first two <sect1> elements from chapter 2.

2.1. GMC
2.1.1. Honda
2.1.1.1. Isuzu
2.1.1.2. Javelin
2.1.1.3. K-Car
2.1.1.4. Lincoln
2.1.2. Mercedes
2.1.3. Nash
2.1.3.1. Opel
2.1.3.2. Pontiac
2.1.4. Quantum
2.1.4.1. Rambler
2.1.4.2. Studebaker
2.2. Toyota
2.2.1. Um, is there a car that starts with "U"?

In Test 1, we used level="multiple" to count the <chapter>, <sect1>, <sect2>, and <sect3> elements. Numbering these at multiple levels gives us a dotted-decimal number for each element. We can look at the number next to Studebaker and know that it is the second <sect3> element inside the fourth <sect2> element inside the first <sect1> element inside the second <chapter> element.

Test 2 uses level="any" to count all of the <chapter>, <sect1>, <sect2>, and <sect3> elements in order.

Test 3 uses level="single" to count the elements at each level. This means that the fourth <sect3> element inside a given <sect2> element will be numbered with a 4 (or iv or D or whatever the appropriate value would be). Notice that the number used for each element is the same as the last number beside each element in Test 1.

Test 4 does a couple of things differently: first, it uses the uppercase-alpha and lowercase-roman numbering styles. Second, it counts elements at multiple levels (for the <chapter>, <sect1>, and <sect2> elements), but we only process the <sect2> elements. Even though we only output the title text for the <sect2> elements, we can still generate the appropriate multilevel numbers.

Test 5 generates numbers similarly to Test 4, except that it uses the from attribute. We generate numbers for <sect3> elements in four stages; first, we count the <chapter> ancestors, starting at the first <book> ancestor; then we count the <sect1> ancestors, starting at the first <chapter> ancestor, etc.

Test 6 starts counting at 1000 instead of 1. To do this, we have to store the value generated by <xsl:number> in a variable, then output the value of the variable plus 1000. Notice that we can use an expression in the value attribute of the <xsl:number> element. We also used the grouping-separator attribute to use a comma to separate groups of three digits.

Last but not least, Test 7 only numbers items from the first and second <sect1> elements (<sect1> elements whose position() is less than 3) in the second <chapter> element. Even though we're only processing these sections, we can still use <xsl:number> to generate the correct numbers for the elements.

<xsl:otherwise> Defines the else or default case in an <xsl:choose> element. This element always appears inside an <xsl:choose> element, and it must always appear last.

Category

Subinstruction (<xsl:otherwise> always appears as part of an <xsl:choose> element).

Required Attributes

None.

Optional Attributes

None.

Content

A template.

Appears in

The <xsl:choose> element.

Defined in

XSLT section 9.2, Conditional Processing with xsl:choose.

Example

As an example, we'll use an <xsl:choose> element that cycles through a set of values for the background color of a cell in an HTML table. We'll use this XML document as our input:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

Here is our stylesheet, which uses <xsl:choose> inside an <xsl:attribute> element to determine the correct value for the bgcolor attribute. We have an <xsl:otherwise> element that generates the value whitesmoke for every fourth <listitem> in our source document:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="list/title"/>
        </title>
      </head>
      <body>
        <h1><xsl:value-of select="list/title"/></h1>
        <table border="1">
          <xsl:for-each select="list/listitem">
            <tr>
              <td>
                <xsl:attribute name="bgcolor">
                  <xsl:choose>
                    <xsl:when test="@bgcolor">
                      <xsl:value-of select="@bgcolor"/>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 0">
                      <xsl:text>papayawhip</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 1">
                      <xsl:text>mintcream</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 2">
                      <xsl:text>lavender</xsl:text>
                    </xsl:when>
                    <xsl:otherwise>
                      <xsl:text>whitesmoke</xsl:text>
                    </xsl:otherwise>
                  </xsl:choose>
                </xsl:attribute>
                <xsl:value-of select="."/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Here is our generated HTML document. Notice that every fourth row has a background color of whitesmoke; that value was generated by the <xsl:otherwise> element:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Albums I've bought recently:</title>
</head>
<body>
<h1>Albums I've bought recently:</h1>
<table border="1">
<tr>
<td bgcolor="mintcream">The Sacred Art of Dub</td>
</tr>
<tr>
<td bgcolor="lavender">Only the Poor Man Feel It</td>
</tr>
<tr>
<td bgcolor="whitesmoke">Excitable Boy</td>
</tr>
<tr>
<td bgcolor="papayawhip">Aki Special</td>
</tr>
<tr>
<td bgcolor="mintcream">Combat Rock</td>
</tr>
<tr>
<td bgcolor="lavender">Talking Timbuktu</td>
</tr>
<tr>
<td bgcolor="whitesmoke">The Birth of the Cool</td>
</tr>
</table>
</body>
</html>

When rendered, our HTML document looks like Figure A-7.

Figure A-7. Document cycling among different background colors

<xsl:output> Defines the characteristics of the output document.

Category

Top-level element

Required Attributes

None.

Optional Attributes

method
Typically has one of three values: xml, html, or text. This value indicates the type of document that is generated. An XSLT processor can add other values to this list; how those values affect the generated document is determined by the XSLT processor.

version
Defines the value of the version attribute of the XML or HTML declaration in the output document. This attribute is used only when method="html" or method="xml".

encoding
Defines the value of the encoding attribute of the XML declaration in the output document.

omit-xml-declaration
Defines whether the XML declaration is omitted in the output document. Allowable values are yes and no. This attribute is used only when method="xml".

standalone
Defines the value of the standalone attribute of the XML declaration in the output document. Valid values are yes and no. This attribute is used only when method="xml".

doctype-public
Defines the value of the PUBLIC attribute of the DOCTYPE declaration in the output document. This attribute defines the public identifier of the output document's DTD. It is used only when method="html" or method="xml".

doctype-system
Defines the value of the SYSTEM attribute of the DOCTYPE declaration in the output document. It defines the system identifier of the output document's DTD. This attribute is used only when method="html" or method="xml".

cdata-section-elements
Lists the elements that should be written as CDATA sections in the output document. All restrictions and escaping conventions of CDATA sections are handled by the XSLT processor. If you need to list more than one element, separate the element names with one or more whitespace characters. This attribute is used only when method="xml".

indent
Specifies whether the tags in the output document should be indented. Allowable values are yes and no. This attribute is used only when method="xml" or method="html", and the XSLT processor is not required to honor it.

media-type
Defines the MIME type of the output document.

Content

None. <xsl:output> is an empty element.

Appears in

<xsl:output> is a top-level element and can only appear as a child of <xsl:stylesheet>.

Defined in

XSLT section 16, Output.

Example

To illustrate the three output methods defined in the XSLT specification, we'll create three stylesheets, each of which uses one of the three methods. We'll use the following XML document in all three examples:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

We'll now look at our three stylesheets and the results produced by each. First, let's look at the method="xml" stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output
    method="xml"
    doctype-public="-//W3C/DTD XHTML 1.0//EN"
    doctype-system="file:///d:/xhtml.dtd"
    encoding="ISO-8859-1"
    indent="no"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="/list/title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="/list/title"/></h1>
        <p>
          <xsl:for-each select="/list/listitem">
            <xsl:number format="1. "/>
            <xsl:value-of select="."/>
            <br/>
          </xsl:for-each>
        </p>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

This stylesheet generates the following results:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C/DTD XHTML 1.0//EN" "file:///d:/xhtml.dtd">
<html><head><title>A few of my favorite albums</title>
</head><body><h1>A few of my favorite albums</h1>
<p>1. A Love Supreme<br/>2. Beat Crazy<br/>3. Here Come the
Warm Jets<br/>4. Kind of Blue<br/>5. London Calling<br/>6.
Remain in Light<br/>7. The Joshua Tree<br/>8. The Indestructible
Beat of Soweto<br/></p></body></html>

(We actually added line breaks to this listing; the original output put everything from <html> through </html> on a single line.)

The output document has the encoding we specified in our stylesheet, and the DOCTYPE declaration includes the PUBLIC and SYSTEM identifiers we requested as well. Even with the line breaks we added, it's still obvious that this document has not been formatted with any extra whitespace whatsoever. We also have empty <br/> elements in our output document; those elements will be handled differently when we specify method="html". Speaking of which, here is our method="html" stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output
    method="html"
    encoding="ISO-8859-1"
    doctype-public="-//W3C/DTD HTML 1.0 Transitional//EN"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="/list/title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="/list/title"/></h1>
        <p>
          <xsl:for-each select="/list/listitem">
            <xsl:number format="1. "/>
            <xsl:value-of select="."/>
            <br/>
          </xsl:for-each>
        </p>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Here is the HTML document generated by this stylesheet:

<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML 1.0 Transitional//EN">
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>A few of my favorite albums</title>
</head>
<body>
<h1>A few of my favorite albums</h1>
<p>1. A Love Supreme<br>2. Beat Crazy<br>3. Here Come
the Warm Jets<br>4. Kind of Blue<br>5. London Calling<br>6.
Remain in Light<br>7. The Joshua Tree<br>8. The Indestructible
Beat of Soweto<br>
</p>
</body>
</html>

(As before, we added line breaks to make the listing legible.) Notice that the XSLT processor has automatically inserted a <META> element in the <head> of our HTML document. The <br> elements that were empty in our previous stylesheet are now old-fashioned <br> tags. Even though this style of XSLT output results in a document that is not valid XML (or XHTML), the document will work with existing HTML browsers.

Our final stylesheet will use method="text":

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="/list/title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="/list/title"/></h1>
        <p>
          <xsl:for-each select="/list/listitem">
            <xsl:number format="1. "/>
            <xsl:value-of select="."/>
            <br/>
          </xsl:for-each>
        </p>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Here are the results, such as they are, from this stylesheet:

A few of my favorite albumsA few of my favorite albums1. A Love Supreme2. Beat
Crazy3. Here Come the Warm Jets4. Kind of Blue5. London Calling6. Remain in
Light7. The Joshua Tree8. The Indestructible Beat of Soweto

(As before, we inserted line breaks so the document would fit on the page.) These results are basically worthless. Why weren't our carefully coded HTML elements output to the text document? The reason is that the text output method only outputs text nodes to the result tree. Even though we requested that various HTML elements be generated along the way, they're ignored because we specified method="text".

<xsl:param> Defines the name and value of a parameter to be used by a template. This element can appear as a top-level element or inside the <xsl:template> element. If the <xsl:param> appears as a top-level element, it is a global parameter, visible to all areas of the stylesheet. The value of the parameter can be defined in one of two ways: specified in the select attribute, or defined in an XSLT template inside the <xsl:param> element itself.

Category

Instruction

Required Attributes

name
Defines the name of this parameter.

Optional Attributes

select
Contains an XPath expression that defines the value of this parameter.

Content

If the select attribute is used, <xsl:param> should be empty. Otherwise, it contains an XSLT template.

Appears in

<xsl:stylesheet> and <xsl:template>. If an <xsl:param> appears as a child of <xsl:stylesheet>, then it is a global parameter visible throughout the stylesheet. XSLT doesn't define the way global parameters are passed to the XSLT processor, so check the documentation for your processor to see how this is done. (See Section 4.4.3, "Global Parameters" in Chapter 4, "Branching and Control Elements" for an overview of how to pass parameters to the most popular XSLT processors.)

Defined in

XSLT section 11, Variables and Parameters.

Example

Here is a stylesheet that defines several <xsl:param> elements, both global and local. Notice that one of the parameters is a node-set; parameters can be of any XPath or XSLT datatype:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:param name="favoriteNumber" select="23"/>
  <xsl:param name="favoriteColor"/>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="list/title"/>
    <xsl:value-of select="$newline"/>
    <xsl:variable name="listitems" select="list/listitem"/>
    <xsl:call-template name="processListitems">
      <xsl:with-param name="items" select="$listitems"/>
      <xsl:with-param name="color" select="'yellow'"/>
      <xsl:with-param name="number" select="$favoriteNumber"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="processListitems">
    <xsl:param name="items"/>
    <xsl:param name="color" select="'blue'"/>

    <xsl:for-each select="$items">
      <xsl:value-of select="position()"/>
      <xsl:text>.  </xsl:text>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>

    <xsl:value-of select="$newline"/>

    <xsl:text>Your favorite color is </xsl:text>
    <xsl:value-of select="$favoriteColor"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>The color passed to this template is </xsl:text>
    <xsl:value-of select="$color"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

We'll use this stylesheet to transform the following document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

Here are the results:

A few of my favorite albums
1.  A Love Supreme
2.  Beat Crazy
3.  Here Come the Warm Jets
4.  Kind of Blue
5.  London Calling
6.  Remain in Light
7.  The Joshua Tree
8.  The Indestructible Beat of Soweto

Your favorite color is purple.
The color passed to this template is yellow.

To generate these results, we passed the value purple to the XSLT processor. With Xalan, the value is passed like this:

java org.apache.xalan.xslt.Process -in test4.xml -xsl param.xsl
  -param favoriteColor purple

(The command should be entered on a single line.) See Section 4.4.3, "Global Parameters" in Chapter 4, "Branching and Control Elements" for a more complete discussion of global parameters and how they can be set for various XSLT processors.

<xsl:preserve-space> Defines the source document elements for which whitespace should be preserved.

Category

Top-level element

Required Attributes

elements
This attribute defines the elements for which whitespace should be preserved. If you need to define more than one element, separate the element names with one or more whitespace characters.

Optional Attributes

None.

Content

None. <xsl:preserve-space> is an empty element.

Appears in

<preserve-space> is a top-level element and can only appear as a child of <xsl:stylesheet> .

Defined in

XSLT section 3.4, Whitespace Stripping.

Example

We'll illustrate how <preserve-space> works with the following stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>
  <xsl:preserve-space elements="listing"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="/code-sample/title"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/code-sample/listing">
      <xsl:value-of select="."/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

We'll use this stylesheet to process the following document:

<?xml version="1.0"?>
<code-sample>
  <title>Conditional variable initialization</title>
  <listing>
  <type>int</type> <variable>y</variable> = <constant>23</constant>;
  <type>int</type> <variable>x</variable>;
    <keyword>if</keyword> (<variable>y</variable> > <constant>10</constant>)
    <variable>x</variable> = <constant>5</constant>;
  <keyword>else</keyword>
    <keyword>if</keyword> (<variable>y</variable> > <constant>5</constant>)
      <variable>x</variable> = <constant>3</constant>;
  <keyword>else</keyword>
    <variable>x</variable> = <constant>1</constant>;
  </listing>
</code-sample>

When we process this document with our stylesheet, we get these results:

Conditional variable initialization

  int y = 23;
  int x;
    if (y > 10)
    x = 5;
  else
    if (y > 5)
      x = 3;
  else
    x = 1;

Compare this example to the one for the <strip-space> element.

<xsl:processing-instruction> Creates a processing instruction in the output document.

Category

Instruction

Required Attributes

name
Defines the name of this processing instruction.

Optional Attributes

None.

Content

An XSLT template. The contents of the template become the data of the processing instruction.

Appears in

<xsl:processing-instruction> appears inside a template.

Defined in

XSLT section 7.3, Creating Processing Instructions.

Example

We'll demonstrate a stylesheet that adds a processing instruction to an XML document. The processing instruction will associate the stylesheet template.xsl with this XML document. Here is our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="/">
    <xsl:processing-instruction name="xml-stylesheet">href="docbook/html/docbook.xsl"
       type="text/xsl"</xsl:processing-instruction>
    <xsl:copy-of select="."/>
  </xsl:template>

</xsl:stylesheet>

This stylesheet simply uses the <xsl:copy-of> element to copy the input document to the result tree, adding a processing instruction along the way. We'll use our stylesheet with this XML document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

When we run this transformation, here are the results:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="docbook/html/docbook.xsl" type="text/xsl"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

Note that the contents of a processing instruction are text. Even though the processing instruction we just generated looks like it contains two attributes, you can't create the processing instruction like this:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="/">
    <xsl:processing-instruction name="xml-stylesheet">

      <!-- This doesn't work!  You can't put <xsl:attribute>
           elements inside a <xsl:processing-instruction> element. -->

      <xsl:attribute name="href">
        <xsl:text>docbook/html/docbook.xsl</xsl:text>
      </xsl:attribute>
      <xsl:attribute name="type">
        <xsl:text>text/xsl</xsl:text>
      </xsl:attribute>
    </xsl:processing-instruction>
    <xsl:copy-of select="."/>
  </xsl:template>

</xsl:stylesheet>

If you try this, you'll get an exception from the XSLT processor.

<xsl:sort> Defines a sort key for the current context. This element appears as a child of the <xsl:apply-templates> or <xsl:for-each> elements. Within those elements, the first <xsl:sort> defines the primary sort key, the second <xsl:sort> defines the secondary sort key, etc.

Category

Subinstruction (<xsl:sort> always appears as a child of the <xsl:apply-templates> or <xsl:for-each> elements)

Required Attributes

None.

Optional Attributes

select
An XPath expression that defines the nodes to be sorted.

lang
A string that defines the language used by the sort. The language codes are defined in RFC1766, available at http://www.ietf.org/rfc/rfc1766.txt.

data-type
An attribute that defines the type of the items to be sorted. Allowable values are number and text; the default is text. An XSLT processor has the option of supporting other values as well. Sorting the values 32 10 120 with data-type="text" returns 10 120 32, while data-type="number" returns 10 32 120.

order
An attribute that defines the order of the sort. Allowable values are ascending and descending.

case-order
An attribute that defines the order in which upper- and lowercase letters are sorted. Allowable values are upper-first and lower-first.

Content

None.

Appears in

<xsl:apply-templates> and <xsl:for-each>.

Defined in

XSLT section 10, Sorting.

Example

We'll illustrate <xsl:sort> with this stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:call-template name="ascending-alpha-sort">
      <xsl:with-param name="items" select="/sample/textlist/listitem"/>
    </xsl:call-template>
    <xsl:call-template name="ascending-alpha-sort">
      <xsl:with-param name="items" select="/sample/numericlist/listitem"/>
    </xsl:call-template>
    <xsl:call-template name="ascending-numeric-sort">
      <xsl:with-param name="items" select="/sample/numericlist/listitem"/>
    </xsl:call-template>
    <xsl:call-template name="descending-alpha-sort">
      <xsl:with-param name="items" select="/sample/textlist/listitem"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="ascending-alpha-sort">
    <xsl:param name="items"/>
    <xsl:text>Ascending text sort:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="$items">
      <xsl:sort select="."/>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template name="descending-alpha-sort">
    <xsl:param name="items"/>
    <xsl:text>Descending text sort:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="$items">
      <xsl:sort select="." order="descending"/>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

  <xsl:template name="ascending-numeric-sort">
    <xsl:param name="items"/>
    <xsl:text>Ascending numeric sort:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="$items">
      <xsl:sort select="." data-type="number"/>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

Our stylesheet defines three named templates, each of which sorts <listitem>s in a different order or with a different data-type. We'll use this stylesheet against this document:

<?xml version="1.0"?>
<sample>
  <numericlist>
    <listitem>1</listitem>
    <listitem>3</listitem>
    <listitem>23</listitem>
    <listitem>120</listitem>
    <listitem>2</listitem>
  </numericlist>
  <textlist>
    <listitem>3</listitem>
    <listitem>apple</listitem>
    <listitem>orange</listitem>
    <listitem>dragonfruit</listitem>
    <listitem>carambola</listitem>
  </textlist>
</sample>

Here are the results:

Ascending text sort:
3
apple
carambola
dragonfruit
orange

Ascending text sort:
1
120
2
23
3

Ascending numeric sort:
1
2
3
23
120

Descending text sort:
orange
dragonfruit
carambola
apple
3

Notice that the data-type="numeric" attribute causes data to be sorted in numeric order.

<xsl:strip-space> Defines the source-document elements for which whitespace should be removed.

Category

Top-level element

Required Attributes

elements
Contains a space-separated list of source document elements for which nonsignificant whitespace should be removed. Nonsignificant whitespace typically means text nodes that contain nothing but whitespace; whitespace that appears in and around text is preserved.

Optional Attributes

None.

Content

None. <xsl:strip-space> is an empty element.

Appears in

<xsl:strip-space> is a top-level element, and can only appear as a child of <xsl:stylesheet> .

Defined in

XSLT section 3.4, Whitespace Stripping.

Example

We'll illustrate the <xsl:strip-space> element with the following stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>
  <xsl:strip-space elements="listing"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="/code-sample/title"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/code-sample/listing">
      <xsl:value-of select="."/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

We'll use this stylesheet to process the following document:

<?xml version="1.0"?>
<code-sample>
  <title>Conditional variable initialization</title>
  <listing>
  <type>int</type> <variable>y</variable> = <constant>23</constant>;
  <type>int</type> <variable>x</variable>;

    <keyword>if</keyword> (<variable>y</variable> > <constant>10</constant>)
    <variable>x</variable> = <constant>5</constant>;
  <keyword>else</keyword>
    <keyword>if</keyword> (<variable>y</variable> > <constant>5</constant>)
      <variable>x</variable> = <constant>3</constant>;
  <keyword>else</keyword>
    <variable>x</variable> = <constant>1</constant>;
  </listing>
</code-sample>

Here are the results:

Conditional variable initialization
inty = 23;
  intx;
    if (y > 10)
    x = 5;
  elseif (y > 5)
      x = 3;
  elsex = 1;

Notice that all the extra whitespace from the <listing> element has been removed. This includes the space between the various elements contained inside <listing>, such as <keyword>, <constant>, and <variable>. Compare this example to the one for the <preserve-space> element.

<xsl:stylesheet> The root element of an XSLT stylesheet. It is identical to the <xsl:transform> element, which was included in the XSLT specification for historical purposes.

Category

Contains the entire stylesheet

Required Attributes

version
Indicates the version of XSLT that the stylesheet requires. For XSLT version 1.0, its value should always be "1.0". As later versions of the XSLT specification are defined, the required values for the version attribute will be defined along with them.

xmlns:xsl
Defines the URI for the XSL namespace. For XSLT Version 1.0, this attribute's value should be http://www.w3.org/1999/XSL/Transform. Note that most XSLT processors will give you a warning message if your xmlns:xsl declaration does not have the proper value.

Optional Attributes

id
Defines an ID for this stylesheet.

extension-element-prefixes

Defines any namespace prefixes used to invoke extension elements. Multiple namespace prefixes are separated by whitespace.

exclude-result-prefixes
Defines namespace prefixes that should not be sent to the output document. Multiple namespace prefixes are separated by whitespace.

Content

This element contains the entire stylesheet. The following items can be children of <xsl:stylesheet>:

  • <xsl:import>

  • <xsl:include>

  • <xsl:strip-space>

  • <xsl:preserve-space>

  • <xsl:output>

  • <xsl:key>

  • <xsl:decimal-format>

  • <xsl:namespace-alias>

  • <xsl:attribute-set>

  • <xsl:variable>

  • <xsl:param>

  • <xsl:template>

Appears in

None. <xsl:stylesheet> is the root element of the stylesheet.

Defined in

XSLT section 2.2, Stylesheet Element.

Example

For the sake of completeness, we'll include an example here. We'll use the Hello World document from the XML 1.0 specification for our example:

<?xml version="1.0"?>
<greeting>
  Hello, World!
</greeting>

We'll transform our document with this stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:apply-templates select="greeting"/>
  </xsl:template>

  <xsl:template match="greeting">
    <html>
      <body>
        <h1>
          <xsl:value-of select="."/>
        </h1>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

When we transform our document with this stylesheet, here are the results:

<html>
<body>
<h1>
  Hello, World!
</h1>
</body>
</html>
<xsl:template> Defines an output template. For templates that begin <xsl:template match="x", the template defines a transformation for a given element. Templates that begin <xsl:template name="x" define a set of output elements that are processed whenever the template is invoked. All <xsl:template> elements must have either the match or the name attribute defined. Although not common, it is also possible to create <xsl:template> elements that have both a match and a name.

Category

Top-level element

Required Attributes

None.

Optional Attributes

match
A pattern that defines the elements for which this template should be invoked. For example, <xsl:template match="xyz"> defines a template for processing <xyz> elements.

name
An attribute that names this template. Named templates are invoked with the <xsl:call-template> element.

mode
An attribute that defines a mode for this template. A mode is a convenient syntax that allows you to write specific templates for specific purposes. For example, I could write an <xsl:template> with mode="toc" to process a node for the table of contents of a document and other <xsl:template>s with mode="print", mode="online", mode="index", etc. to process the same information for different purposes.

priority
An attribute that assigns a numeric priority to this template. The value can be any numeric value except Infinity. If the XSLT processor cannot determine which template to use (in other words, more than one template has the same default priority), the priority attribute allows you to define a tiebreaker.

Content

An XSLT template.

Appears in

<xsl:stylesheet>. <xsl:template> is a top-level element and can only appear as a child of <xsl:stylesheet>.

Defined in

XSLT section 5.3, Defining Template Rules.

Example

We'll use a template that copies all nodes from the input document to the output document, with one important difference: all attributes in the original document are converted to elements in the output document. The name of each generated element is the name of the original attribute, and the text of each element is the attribute's value. Here's our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>
  <xsl:template match="*">
    <xsl:element name="{name()}">
      <xsl:for-each select="@*">
        <xsl:element name="{name()}">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:apply-templates select="*|text()"/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Our stylesheet contains a single <xsl:template> that transforms every node in the original document. We'll use our stylesheet to transform the following XML document:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here are the results of our transformation:

<?xml version="1.0" encoding="UTF-8"?>
<report>
  <title>Miles Flown in 2001</title>
  <month><sequence>01</sequence>
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month><sequence>02</sequence>
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month><sequence>03</sequence>
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month><sequence>04</sequence>
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>
<xsl:text> Allows you to write literal text to the output document.

Category

Instruction

Required Attributes

None.

Optional Attributes

disable-output-escaping
Defines whether special characters are escaped when they are written to the output document. For example, if the literal text contains the character >, it is normally written to the output document as &gt;. If you code disable-output-escaping="yes", the character > is written instead. The XSLT processor uses this attribute only if you're using the html or xml output methods. If you're using <xsl:output method="text">, the attribute is ignored because output escaping is not done for the text output method.

Content

#PCDATA, literal text, and entity references.

Appears in

<xsl:text> appears inside a template.

Defined in

XSLT section 7.2, Creating Text.

Example

This sample stylesheet generates text with <xsl:text>. We intermingle <xsl:text> elements and <xsl:value-of> elements to create a coherent sentence. In this case, we simply generate a text document, but this technique works equally well to create the text of an HTML or XML element. Here is the stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:text>Your document contains </xsl:text>
    <xsl:value-of select="count(//*)"/>
    <xsl:text> elements and </xsl:text>
    <xsl:value-of select="count(//@*)"/>
    <xsl:text> attributes. </xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text disable-output-escaping="yes"><Have a great day!></xsl:text>
  </xsl:template>
</xsl:stylesheet>

Also notice our use of <xsl:variable> to generate line breaks. The <xsl:text> element inside the <xsl:variable> element contains a line break, so writing the value of that variable to the result tree gives us the line break we want. Given this XML document:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

Our stylesheet produces these results:

Your document contains 9 elements and 5 attributes.
<Have a great day!>

Since we use the text output method, the disable-output-escaping attribute has no effect. If you change the stylesheet to use <xsl:output method="html"/> or <xsl:output method="xml"/>, then disable-output-escaping is used. Here are the results for disable-output-escaping="yes":

Your document contains 10 elements and 2 attributes.
<Have a great day!>

And here are the results for disable-output-escaping="no", the default:

Your document contains 10 elements and 2 attributes.
&lt;Have a great day!&gt;
<xsl:transform> This is a synonym for <xsl:stylesheet>. It was included in the XSLT 1.0 spec for historical purposes. Its attributes, content, and all other properties are the same as those for <xsl:stylesheet>. See <xsl:stylesheet> for more information.
<xsl:value-of> Calculates the value of an XPath expression, converts that value to a string, and then writes the value to the result tree.

Category

Instruction

Required Attributes

select
The XPath expression that is evaluated and written to the output document.

Optional Attributes

disable-output-escaping
An attribute that defines whether special characters are escaped when written to the output document. For example, if the literal text contains the character >, it is normally written to the output document as &gt;. If you code disable-output-escaping="yes", the character > is written instead. The XSLT processor uses this attribute only if you use the html or xml output methods. If you use <xsl:output method="test">, the attribute is ignored becasue output escaping is not done for the text output method. See Reference A.30 for a more thorough discussion of the disable-output-escaping attribute.

Content

None. <xsl:value-of> is an empty element.

Appears in

<xsl:value-of> appears inside a template.

Defined in

XSLT section 7.6.1, Generating Text with xsl:value-of.

Example

We'll use the <xsl:value-of> element to generate some text. Here is our stylesheet:

<?xsl version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:text>Your document contains</xsl:text>
    <xsl:value-of select="count(//*)"/>
    <xsl:text> elements and </xsl:text>
    <xsl:value-of select="count(//@*)"/>
    <xsl:text> attributes.  </xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>Have a great day!</xsl:text>
  </xsl:template>
</xsl:stylesheet>

We'll use this XML document as input:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here are the results:

Your document contains 14 elements and 4 attributes.
Have a great day!
<xsl:variable> Defines a variable. If <xsl:variable> occurs as a top-level element, it is a global variable that is accessible throughout the stylesheet. Otherwise, the variable is local and exists only in the element that contains the <xsl:variable>. The value of the variable can be defined in one of two ways: specified in the select attribute or defined in an XSLT template inside the <xsl:variable> element itself. If neither method is used, the value of the variable is an empty string.

Category

Either a top-level element or an instruction

Required Attributes

name
An attribute that names this variable.

Optional Attributes

select
An XPath expression that defines the value of this variable.

Content

The <xsl:variable> element can be empty, or it can contain an XSLT template. If it contains an XSLT template, the value of the select attribute (if any exists) is ignored.

Appears in

<xsl:stylesheet> as a top-level element or in a template.

Defined in

XSLT section 11, Variables and Parameters.

Example

Here is a stylesheet that defines a number of variables:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:variable name="favoriteNumber" select="23"/>
  <xsl:variable name="favoriteColor" select="'blue'"/>
  <xsl:variable name="complicatedVariable">
    <xsl:choose>
      <xsl:when test="count(//listitem) > 10">
        <xsl:text>really long list</xsl:text>
      </xsl:when>
      <xsl:when test="count(//listitem) > 5">
        <xsl:text>moderately long list</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>fairly short list</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:text>Hello!  Your favorite number is </xsl:text>
    <xsl:value-of select="$favoriteNumber"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>Your favorite color is </xsl:text>
    <xsl:value-of select="$favoriteColor"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Here is a </xsl:text>
    <xsl:value-of select="$complicatedVariable"/>
    <xsl:text>:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:variable name="listitems" select="list/listitem"/>
    <xsl:call-template name="processListitems">
      <xsl:with-param name="items" select="$listitems"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="processListitems">
    <xsl:param name="items"/>
    <xsl:variable name="favoriteColor">
      <xsl:text>chartreuse</xsl:text>
    </xsl:variable>

    <xsl:text>    (Your favorite color is now </xsl:text>
    <xsl:value-of select="$favoriteColor"/>
    <xsl:text>.)</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="$items">
      <xsl:value-of select="position()"/>
      <xsl:text>.  </xsl:text>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

We'll use our stylesheet to transform the following document:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

Here are the results of our transformation:

Hello!  Your favorite number is 23.
Your favorite color is blue.

Here is a moderately long list:
    (Your favorite color is now chartreuse.)
1.  The Sacred Art of Dub
2.  Only the Poor Man Feel It
3.  Excitable Boy
4.  Aki Special
5.  Combat Rock
6.  Talking Timbuktu
7.  The Birth of the Cool

Several things are worth mentioning in our stylesheet. First, notice that when we defined values for the first two variables (favoriteNumber and favoriteColor), we had to quote the string "blue", but didn't have to quote 23. If we don't quote blue, the XSLT processor assumes we mean all the <blue> elements in the current context. We don't have to quote 23 because XML element names can't start with a number. It's a good idea to always quote literals, even those that can't be element names; chances are good that you'll forget this process at some point.

Also notice that we have two variables named favoriteColor. One is a global variable because its parent is the <xsl:stylesheet> element; the other is a local variable because it is defined in a <xsl:template>. When we access favoriteColor in the match="/" template, it has one value; when we access it inside the name="processListitems" template, it has another. Having two variables at the same level with the same name is an error. It's also an error to define an <xsl:variable> and an <xsl:param> with the same name at the same level.

Using an <xsl:choose> element to initialize an <xsl:variable> is a common technique. This technique is the equivalent of this procedural programming construct:

String complicatedVariable;
if (count(listitems) > 10)
  complicatedVariable = "really long list";
else if (count(listitems)) > 5)
  complicatedVariable = "moderately long list";
else
  complicatedVariable = "fairly short list";

The last point we'll make is that a variable can be any of the XPath or XSLT variable types, including a node-set. When we call the processListitems template, the parameter we pass to it is a variable containing the node-set of all the <listitem> elements in our document. Inside the processListitems template, our variable (which is now technically a parameter) can be used inside an <xsl:for-each> element.

<xsl:when> Defines one branch of an <xsl:choose> element. It is equivalent to the Java case statement.

Category

Subinstruction (<xsl:when> always appears as a child of an <xsl:choose> element)

Required Attributes

test
Contains a boolean expression that is evaluated. If the expression evaluates to true, the contents of the <xsl:when> element are processed; otherwise, the contents of the <xsl:when> are ignored.

Optional Attributes

None.

Content

An XSLT template.

Appears in

The <xsl:choose> element only.

Defined in

XSLT section 9.2, Conditional Processing with xsl:choose.

Example

This example uses an <xsl:choose> element and three <xsl:when> elements to cycle through a set of values. Now we will generate rows of an HTML table for each <listitem>:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

In our stylesheet, we'll generate table rows with the background colors of mintcream, lavender, whitesmoke, and papayawhip. For each <listitem> in our source document, one of the <xsl:when> elements (or the <xsl:otherwise> element) generates the appropriate color.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="list/title"/>
        </title>
      </head>
      <body>
        <h1><xsl:value-of select="list/title"/></h1>
        <table border="1">
          <xsl:for-each select="list/listitem">
            <tr>
              <td>
                <xsl:attribute name="bgcolor">
                  <xsl:choose>
                    <xsl:when test="@bgcolor">
                      <xsl:value-of select="@bgcolor"/>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 0">
                      <xsl:text>papayawhip</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 1">
                      <xsl:text>mintcream</xsl:text>
                    </xsl:when>
                    <xsl:when test="position() mod 4 = 2">
                      <xsl:text>lavender</xsl:text>
                    </xsl:when>
                    <xsl:otherwise>
                      <xsl:text>whitesmoke</xsl:text>
                    </xsl:otherwise>
                  </xsl:choose>
                </xsl:attribute>
                <xsl:value-of select="."/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

When we process our XML source document with this stylesheet, here are the results:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Albums I've bought recently:</title>
</head>
<body>
<h1>Albums I've bought recently:</h1>
<table border="1">
<tr>
<td bgcolor="mintcream">The Sacred Art of Dub</td>
</tr>
<tr>
<td bgcolor="lavender">Only the Poor Man Feel It</td>
</tr>
<tr>
<td bgcolor="whitesmoke">Excitable Boy</td>
</tr>
<tr>
<td bgcolor="papayawhip">Aki Special</td>
</tr>
<tr>
<td bgcolor="mintcream">Combat Rock</td>
</tr>
<tr>
<td bgcolor="lavender">Talking Timbuktu</td>
</tr>
<tr>
<td bgcolor="whitesmoke">The Birth of the Cool</td>
</tr>
</table>
</body>
</html>

All <td> elements with a background color of papayawhip, mintcream, or lavender were generated by one of the <xsl:when> elements.

<xsl:with-param> Defines a parameter to be passed to a template. When the template is invoked, values can be passed in for the parameter.

Category

Subinstruction (<xsl:with-param> always appears inside an <xsl:apply-templates> or <xsl:call-template> element)

Description

<xsl:with-param> defines a parameter to be passed to a template. When the template is invoked, values can be passed in for the parameter. The value of the parameter can be defined in one of three ways:

  • If the <xsl:with-param> element is empty and does not contain a select attribute, then no value is passed to the template.

  • If the <xsl:with-param> element is empty and has a select attribute, the value of the parameter is the value of the select attribute.

  • If the <xsl:with-param> element contains an XSLT template, the value of the parameter is the result of processing the template.

If no value is passed to the template (<xsl:with-param name="x"/>), then the default value of the parameter, if any, is used instead. The default value of the parameter is defined on the <xsl:param> element inside the <xsl:template> itself; see the description of the <xsl:param> element for more details.

Required Attributes

name
Names this parameter.

Optional Attributes

select
An XPath expression that defines the value of this parameter.

Content

The <xsl:with-param> element can be empty, or it can contain an XSLT template. If it contains an XSLT template, the value of the select attribute (if any exists) is ignored.

Appears in

<xsl:apply-templates> and <xsl:call-template>.

Defined in

XSLT section 11.6, Passing Parameters to Templates.

Example

Here is a stylesheet with a number of parameters. Notice that some parameters are global and defined outside the stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:param name="favoriteNumber" select="23"/>
  <xsl:param name="favoriteColor"/>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="list/title"/>
    <xsl:value-of select="$newline"/>
    <xsl:variable name="listitems" select="list/listitem"/>
    <xsl:call-template name="processListitems">
      <xsl:with-param name="items" select="$listitems"/>
      <xsl:with-param name="color" select="'yellow'"/>
      <xsl:with-param name="number" select="$favoriteNumber"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="processListitems">
    <xsl:param name="items"/>
    <xsl:param name="color" select="'blue'"/>

    <xsl:for-each select="$items">
      <xsl:value-of select="position()"/>
      <xsl:text>.  </xsl:text>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>

    <xsl:value-of select="$newline"/>

    <xsl:text>Your favorite color is </xsl:text>
    <xsl:value-of select="$favoriteColor"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>The color passed to this template is </xsl:text>
    <xsl:value-of select="$color"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

We'll use this stylesheet to transform this document:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

Our stylesheet contains two global parameters, favoriteNumber and favoriteColor, and defines a default value for favoriteNumber. The stylesheet also passes a parameter from the match="/" template to the name="processListitems" template; that parameter contains a node-set. Here are the results of the transformation:

Albums I've bought recently:
1.  The Sacred Art of Dub
2.  Only the Poor Man Feel It
3.  Excitable Boy
4.  Aki Special
5.  Combat Rock
6.  Talking Timbuktu
7.  The Birth of the Cool

Your favorite color is orange.
The color passed to this template is yellow.

To generate these results with Xalan, we use this command:

java org.apache.xalan.xslt.Process -in test4.xml -xsl with-param.xsl
  -param favoriteColor orange

The command should appear on a single line. See Section 4.4.3, "Global Parameters" in Chapter 4, "Branching and Control Elements" for a complete discussion of global parameters and how you define them for various XSLT processors.

Previous | Home | Next
9.6. Summary | Book Index | B. XPath Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Reference (XSLT)

 XSLTSearch this book

Appendix B. XPath Reference

Contents:

XPath Node Types
XPath Node Tests
XPath Axes
XPath Operators
Datatypes
The XPath Context

This appendix contains reference information from the XPath specification. The XPath node types, axes, and operators are defined here. The datatypes used in XSLT stylesheets, including the result tree fragment type (technically defined in the XSLT specification, not in XPath), are defined here as well. This appendix concludes with a definition of the XPath context.

B.1. XPath Node Types

There are seven types of nodes in XPath. We'll stick to the reference material here; for more information on the different node types, see our earlier discussion of the XPath data model.

B.1.1. The Root Node

The root node is the root of the tree. Unlike all other nodes, it does not have a parent. Its children are the element node for the document, along with any comments or processing instructions that appear outside the document element. The root node does not have an expanded name.

B.1.2. Element Nodes

Each element in the original XML document is represented by an element node. The expanded name of the element is its local name, combined with any namespace that is in effect for the element. You can access the different parts of the element name with the name(), local-name(), and namespace-uri() functions. Here is an element from an XML document:

<xyz:report xmlns:xyz="http://www.xyz.com/">

The values of the three functions for this element node are:

name()
xyz:report

local-name()
report

namespace-uri()
http://www.xyz.com/

B.1.3. Attribute Nodes

Attributes of elements in the XML document become XPath attribute nodes. An attribute has an expanded name, just as an element node has. The attribute nodes of a given element node are the attributes explicitly coded on the XML element and any attributes defined with default values in the DTD.

Taking a different approach from the Document Object Model, an element node is the parent of its attributes, although the attributes are not the children of the element. In other words, selecting all the children of an element node does not select any attribute nodes that the element node might have.

B.1.4. Text Nodes

Text nodes simply contain text from an element. If the original text in the XML document contained character or entity references, they are resolved before the XPath text node is created. Similarly, any existing CDATA sections appear as text nodes. You have no way of knowing if a given portion of a text node was originally a character or entity reference or a CDATA section.

B.1.5. Comment Nodes

A comment node is also very simple; it contains some text. Every comment in the source document (except for any comments in the DTD) becomes a comment node. The text of the comment node (returned with the text() node test) contains everything inside the comment except the opening <!-- and the closing -->.

B.1.6. Processing-Instruction Nodes

A processing-instruction node has two parts: a name (returned by the name() function) and a string value. The string value is everything after the name, including the whitespace, but not including the ?> that closes the processing instruction.

B.1.7. Namespace Nodes

Namespace nodes are almost never used in XSLT stylesheets; they exist primarily for the XSLT processor's benefit. One thing to keep in mind is that the declaration of a namespace (such as xmlns:auth="http://www.authors.net"), even though it is technically an attribute in the XML source, becomes a namespace node and not an attribute node. Namespace nodes exist for both the namespace prefixes that are defined and any default namespaces.

Previous | Home | Next
A. XSLT Reference | Book Index | B.2. XPath Node Tests
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Node Tests (XSLT)

 XSLSearch this book

B.2. XPath Node Tests

XPath defines several node tests that can be used to select nodes from the source tree. Strictly speaking, any XPath expression can be considered a node test; the expression para, for example, selects all <para> elements from the context node. Several special node tests allow you to select nodes that can't be selected any other way. (Although they look and work like functions, they are technically node tests.) These special node tests are described here:

text()
Selects all the text-node children of the context node.

comment()
Selects all the comment-node children of the context node.

processing-instruction()
Selects all the processing-instruction children of the context node. Unlike the other node tests defined here, processing-instruction() can have an optional argument; processing-instruction('xml-stylesheet') selects all processing instructions with a name of xml-stylesheet.

node()
Is true for all nodes, regardless of type. Using this node test selects all element nodes, attribute nodes, processing-instruction nodes, etc.

Previous | Home | Next
B. XPath Reference | Book Index | B.3. XPath Axes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Axes (XSLT)

 XSLSearch this book

B.3. XPath Axes

The XPath specification defines thirteen different axes; each axis contains various nodes. The nodes that are in a given axis depend on the context node. All 13 axes, excerpted from our more involved discussion in Section 3.1, "The XPath Data Model" in Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks", are listed here.

child axis
Contains the children of the context node. As we've already mentioned, the XPath expressions child::lines/child::line and lines/line are equivalent. If an XPath expression (such as /sonnet) doesn't have an axis specifier, the child axis is used by default.

parent axis
Contains the parent of the context node, if there is one. (If the context node is the root node, the parent axis returns an empty node-set.) This axis can be abbreviated with a double period (..). The expressions parent::sonnet and ../sonnet are equivalent. If the context node does not have a <sonnet> element as its parent, these XPath expressions return an empty node-set.

self axis
Contains the context node itself. The self axis can be abbreviated with a single period (.).

attribute axis
Contains the attributes of the context node. If the context node is not an element node, this axis is empty. The attribute axis can be abbreviated with the at sign (@). The expressions attribute::type and @type are equivalent.

ancestor axis
Contains the parent of the context node, the parent's parent, and so on. The ancestor axis always contains the root node, unless the context node is the root node.

ancestor-or-self axis
Contains the context node, its parent, its parent's parent, and so on. This axis always includes the root node.

descendant axis
Contains all children of the context node, all children of all the children of the context node, and so on. Be aware that the descendant axis does not include any attribute or namespace nodes. (As we discussed earlier, an attribute node has an element node as its parent, even though the attribute node is not considered a child of its parent.)

descendant-or-self axis
Contains the context node and all children of the context node, all children of all the children of the context node, and so on.

preceding-sibling axis
Contains all of the preceding siblings of the context node -- in other words, all nodes that have the same parent as the context node and appear before the context node in the XML document. If the context node is an attribute node or a namespace node, the preceding-sibling axis is empty.

following-sibling axis
Contains all of the following siblings of the context node -- in other words, all nodes that have the same parent as the context node and appear after the context node in the XML document. If the context node is an attribute node or a namespace node, the following-sibling axis is empty.

preceding axis
Contains all nodes that appear before the context node in the document, except any ancestors, attribute nodes, and namespace nodes.

following axis
Contains all nodes that appear after the context node in the document, except any descendants, attribute nodes, and namespace nodes.

namespace axis
Contains the namespace nodes of the context node. If the context node is not an element node, this axis is empty.

The five axes ancestor, descendant, following, preceding, and self partition everything in the XML document (with the exception of any attribute or namespace nodes). Any node in the XPath tree appears in one of these five axes, and the five axes do not overlap.

Previous | Home | Next
B.2. XPath Node Tests | Book Index | B.4. XPath Operators
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XPath Operators (XSLT)

 XSLSearch this book

B.4. XPath Operators

All operators defined by XPath are listed here.

B.4.1. Mathematical Operators

+ (plus)
Adds one number to another

- (minus)
Subtracts one number from another

* (multiplication)
Multiplies one number by another

div
Performs a floating-point division between two numbers

mod
Returns the floating-point remainder of dividing one number by another

B.4.2. Boolean Operators

=
Tests whether two expressions are equal.

<
Tests whether the first expression is less than the second. Within an attribute, this operator must be coded &lt;.

<=
Tests whether the first expression is less than or equal to the second. Within an attribute, this operator must be coded &lt;=.

>
Tests whether the first expression is greater than the second. Within an attribute, this operator can be coded &gt;.

>=
Tests whether the first expression is greater than or equal to the second. Within an attribute, this operator can be coded &gt;=.

!=
Tests whether the two expressions are not equal.

and
Tests whether both the first and second expressions are true. If the first expression is false, the second is not evaluated.

or
Tests whether either the first or second expressions are true. If the first expression is true, the second is not evaluated.

B.4.2.1. Comparing values of various datatypes

For the first six boolean operators, comparing values of various datatypes is complicated. We explain the various possibilities here:

If both objects are boolean values
Then they are equal if they have the same value. For less-than and greater-than comparisons, false is considered less than true (the function call number(false()) returns 0, while number(true()) returns 1).

If both objects are numbers
Then the operators work just the way you'd think they would.

If both objects are strings
Then they are equal if their Unicode characters are identical. For less-than and greater-than comparisons, the character codes are compared.

If neither object is a node-set and the operator is = or !=
Then the two objects are converted to the same object type, and the comparison works as described previously. If one of the objects is a boolean, then the objects are converted to boolean values as if by a call to the boolean() function. If none of the objects are boolean, the next attempted conversion is to a number. If one of the objects is a number, then the objects are converted to numeric values as if by a call to the number() function. Otherwise, all objects are converted to strings as if by a call to the string() function.

If neither object is a node-set and the operator is <, >, or >=
Then the objects are converted to numbers and compared.

If one or both of the objects is a node-set
Then things really get complicated. If both objects are node-sets, a comparison is true when the string value of at least one node in the first node-set is equal to the string value of at least one node in the second node-set. If one object is a node-set and the other is a number, string, or boolean, the comparison is true when there is at least one node in the node set whose number, string, or boolean value is equal to that number, string, or boolean value.

B.4.3. Expression Operators

/
The stepping operator, which is used to separate steps in a location path. If an XPath expression begins with /, it represents the root of the document.

//
The abbreviated syntax for the descendant-or-self axis.

.
The abbreviated syntax for the self axis.

..
The abbreviated syntax for the parent axis.

@
The abbreviated syntax for the attribute axis.

|
The union operator. For example, the match attribute in the element <xsl:template match="a|b"> matches all <a> and <b> elements.

*
A wildcard that represents any node of the principal node type. child::* selects all element children of the context node, attribute::* selects all attributes of the context node, etc. Using the abbreviated syntax, * selects all element children of the context node, and @* selects all attributes of the context node. Contrast the wildcard operator with the node() node test, which matches any node, regardless of type.

[]
The predicate operator, used to contain a predicate expression that filters a group of nodes.

$
The variable operator, used to indicate that a given symbol is a variable name.

Previous | Home | Next
B.3. XPath Axes | Book Index | B.5. Datatypes
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Datatypes (XSLT)

 XSLSearch this book

B.5. Datatypes

XPath and XSLT define five datatypes, listed here. The result tree fragment type is defined by XSLT and is specific to transformations; the other four are defined by XPath and are generic to any technology that uses XPath. The four XPath datatypes are tersely defined in Section 1 of the XPath specification; section 11.1 of the XSLT specification defines result tree fragments.

node-set
A set of nodes. The set can be empty, or it can contain any number of nodes.

boolean
The value true or false. Be aware that the strings true and false have no special meaning or value in XPath. If you need to use the boolean values themselves, use the functions true() and false().

number
A floating-point number. All numbers in XPath and XSLT are implemented as floating-point numbers; the integer or int datatype does not exist in XPath and XSLT. To be specific, all numbers are implemented as IEEE 754 floating-point numbers, the same standard used by the Java float and double primitive types. In addition to ordinary numbers, there are five special values for numbers: positive and negative infinity, positive and negative zero, and NaN, the special symbol for anything that is not a number.

string
Zero or more characters, as defined in the XML specification.

result tree fragment
A temporary tree. You can create one with an <xsl:variable> element that uses content (instead of the select attribute) to initialize its value. A result tree fragment can be copied to the result tree with the <xsl:copy-of> element. It may also be converted to a string with the <xsl:value-of> element.

Previous | Home | Next
B.4. XPath Operators | Book Index | B.6. The XPath Context
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


The XPath Context (XSLT)

 XSLSearch this book

B.6. The XPath Context

The context in an XPath expression consists of several things:

Context node
The node currently being evaluated.

Context position
A nonzero positive integer that indicates the position of the context node within the set of context nodes.

Context size
A nonzero positive integer that indicates the number of nodes in the current context.

Variable bindings
A set of variables that are in scope for the current context. Each one is represented by a variable name and an object that represents its value. The object might be one of the four XPath datatypes, some additional type defined by an extension, or some other entity.

Functions
A set of functions visible to the current context. Each function is represented by a mapping between a function name and the actual code to be invoked. Each function takes zero or more arguments and returns a single result. XPath defines a number of core functions that are always available; XSLT defines additional functions that go beyond those defined in the XPath specification.

Namespace declarations
The set of namespace declarations visible to the current context. Each one consists of a namespace prefix and the URI with which it is associated.

Previous | Home | Next
B.5. Datatypes | Book Index | C. XSLT and XPath Function Reference
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT and XPath Function Reference (XSLT)

 XSLTSearch this book

Appendix C. XSLT and XPath Function Reference

This section lists all functions defined by XSLT and XPath.

boolean() Function Converts its argument to a boolean value.

Synopsis

boolean boolean(object)

Inputs

An object. The object is converted to a boolean value. This conversion is described in the following subsection.

Output

The boolean value corresponding to the input object. Objects are converted to boolean values as follows:

  • A number is true if and only if it is not zero, negative zero, or NaN (not a number).

  • A node-set is true if and only if it is not empty.

  • A string is true if and only if its length is greater than zero.

  • All other datatypes are converted in a way specific to those datatypes.

Defined in

XPath section 4.3, Boolean Functions.

Example

The following example demonstrates the results of invoking the boolean() function against a variety of argument types. Here's our XML document:

<?xml version="1.0"?>
<test>
<p>This is a test XML document used by several
of our sample stylesheets.</p>
<question>
<text>When completed, the Eiffel Tower was the
tallest building in the world.</text>
<true>Yes!  The Eiffel Tower was the world's
tallest building until 1932, when
New York's Empire State Building opened. </true>
<false>No, the Eiffel Tower was the world's tallest
building for over 30 years.</false>
</question>
</test>

We'll process this document with the following stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the boolean() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="boolean(true())">
        <xsl:text>   "boolean(true())"   returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "boolean(true())"   returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="boolean(true)">

        <xsl:text>   "boolean(true)"     returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "boolean(true)"     returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="boolean('false')">
        <xsl:text>   "boolean('false')"  returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "boolean('false')"  returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="boolean('7')">
        <xsl:text>   "boolean('7')"      returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "boolean('7')"      returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="boolean(/true)">
        <xsl:text>   "boolean(/true)"    returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "boolean(/true)"    returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="boolean(//true)">
        <xsl:text>   "boolean(//true)"   returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "boolean(//true)"   returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Here are the results:

Tests of the boolean() function:

   "boolean(true())"   returned true!
   "boolean(true)"     returned false!
   "boolean('false')"  returned true!
   "boolean('7')"      returned true!
   "boolean(/true)"    returned false!
   "boolean(//true)"   returned true!

See Section 4.2.1.2, "Boolean examples" in Chapter 4, "Branching and Control Elements" for more examples and information.

ceiling() Function Returns the smallest integer that is not less than the argument.

Synopsis

number ceiling(number)

Inputs

A number. If the argument is not a number, it is transformed into a number as if it had been processed by the number() function. If the argument cannot be transformed into a number, the ceiling() function returns the value NaN (not a number).

Output

The smallest integer that is not less than the argument, or NaN if the argument cannot be converted to a number.

Defined in

XPath section 4.4, Number Functions.

Example

The following stylesheet shows the results of invoking the ceiling() function against a variety of values. We'll use this XML document as input:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here's the stylesheet that uses the ceiling() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>
  <months:name sequence="12">December</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the ceiling() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   "ceiling('7.983')" = </xsl:text>
    <xsl:value-of select="ceiling('7.983')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "ceiling('-7.893')" = </xsl:text>
    <xsl:value-of select="ceiling('-7.893')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "ceiling(/report/month[@sequence='01']/miles-flown)" = </xsl:text>
    <xsl:value-of select="ceiling(/report/month[@sequence='01']/miles-flown)"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "ceiling(document('')/*/</xsl:text>
    <xsl:text>months:name[@sequence='02'])" = </xsl:text>
    <xsl:value-of select="ceiling(document('')/*/months:name[@sequence='02'])"/>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=current()/@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>      (Averaged </xsl:text>
      <xsl:value-of select="ceiling(miles-earned div miles-flown)"/>
      <xsl:text> miles earned for each mile flown.)</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

When we transform the XML document with our stylesheet, here are the results:

Tests of the ceiling() function:

   "ceiling('7.983')" = 8
   "ceiling('-7.893')" = -7
   "ceiling(/report/month[@sequence='01']/miles-flown)" = 12379
   "ceiling(document('')/*/months:name[@sequence='02'])" = NaN

   January - 12,379 miles flown, 35,215 miles earned.
      (Averaged 3 miles earned for each mile flown.)

   February - 32,857 miles flown, 92,731 miles earned.
      (Averaged 3 miles earned for each mile flown.)

   March - 19,920 miles flown, 76,725 miles earned.
      (Averaged 4 miles earned for each mile flown.)

   April - 18,903 miles flown, 31,781 miles earned.
      (Averaged 2 miles earned for each mile flown.)

Notice that when we invoked the ceiling() function against the string "February" (what document('')/*/months:name[@sequence='02'] resolves to), the function returned NaN. You can compare these results to those from the floor() function and the round() function.

concat() Function Takes all of its arguments and concatenates them. Any arguments that are not strings are converted to strings as if processed by the string() function.

Synopsis

string concat(stringstringstring*)

Inputs

Two or more strings.

Output

The concatenation of all of the input strings.

Defined in

XPath section 4.2, String Functions.

Example

We'll use this XML file to demonstrate how concat() works:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

In our stylesheet, we'll use the concat() function to create filenames for various JPEG files. The filenames are composed from several pieces of information, concatenated by the concat() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="list/listitem">
      <xsl:text>See the file </xsl:text>
      <xsl:value-of select="concat('album', position(), '.jpg')"/>
      <xsl:text> to see the title of album #</xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Our stylesheet generates these results:

See the file album1.jpg to see the title of album #1
See the file album2.jpg to see the title of album #2
See the file album3.jpg to see the title of album #3
See the file album4.jpg to see the title of album #4
See the file album5.jpg to see the title of album #5
See the file album6.jpg to see the title of album #6
See the file album7.jpg to see the title of album #7
See the file album8.jpg to see the title of album #8
contains() Function Determines if the first argument string contains the second.

Synopsis

boolean contains(stringstring)

Inputs

Two strings. If the first string contains the second string, the function returns the boolean value true.

Output

The boolean value true if the first argument contains the second; false otherwise.

Defined in

XPath section 4.2, String Functions.

Example

This stylesheet uses the replace-substring named template. It passes three arguments to the replace-substring template: the original string, the substring to be searched for in the original string, and the substring to replace the target substring in the original string. The replace-substring template uses the contains(), substring-after(), and substring-before() functions extensively.

Here is our sample stylesheet. It replaces all occurrences of World with the string "Mundo":

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:variable name="test">
      <xsl:call-template name="replace-substring">
        <xsl:with-param name="original">Hello World!</xsl:with-param>
        <xsl:with-param name="substring">World</xsl:with-param>
        <xsl:with-param name="replacement">Mundo</xsl:with-param>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$test"/>
  </xsl:template>

  <xsl:template name="replace-substring">
    <xsl:param name="original"/>
    <xsl:param name="substring"/>
    <xsl:param name="replacement" select="''"/>
    <xsl:variable name="first">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:value-of select="substring-before($original, $substring)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$original"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="middle">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:value-of select="$replacement"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text></xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="last">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:choose>
            <xsl:when test="contains(substring-after($original,
                                        $substring), $substring)">
              <xsl:call-template name="replace-substring">
                <xsl:with-param name="original">
                  <xsl:value-of
                    select="substring-after($original, $substring)"/>
                </xsl:with-param>
                <xsl:with-param name="substring">
                  <xsl:value-of select="$substring"/>
                </xsl:with-param>
                <xsl:with-param name="replacement">
                  <xsl:value-of select="$replacement"/>
                </xsl:with-param>
              </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of
                select="substring-after($original, $substring)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text></xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="concat($first, $middle, $last)"/>
  </xsl:template>

</xsl:stylesheet>

The stylesheet produces these results, regardless of the XML document used as input:

Hello Mundo!
count() Function Counts the number of nodes in a given node-set.

Synopsis

number count(node-set)

Inputs

A node-set.

Output

The number of nodes in the node-set.

Defined in

XPath section 4.1, Node Set Functions.

Examples

Here's the XML document we'll use to illustrate the count() function:

<?xml version="1.0"?>
<test>
  <p>This is a test XML document used by
  several of our sample stylesheets.</p>
  <question>
    <text>When completed, the Eiffel Tower was the
    tallest building in the world.</text>
    <true>You're correct!  The Eiffel Tower was the
    world's tallest building until 1930.</true>
    <false>No, the Eiffel Tower was the world's
    tallest building for over 30 years.</false>
  </question>
  <question>
    <text>New York's Empire State Building knocked
    the Eiffel Tower from its pedestal.</text>
    <true>No, that's not correct.</true>
    <false>Correct!  New York's Chrysler Building,
    completed in 1930, became the world's tallest.</false>
  </question>
</test>

Here's a stylesheet that illustrates the count() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the count() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   count(/test)=</xsl:text>
    <xsl:value-of select="count(/test)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   count(/true)=</xsl:text>
    <xsl:value-of select="count(/true)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   count(//true)=</xsl:text>
    <xsl:value-of select="count(//true)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   count(//test|//true|//text)=</xsl:text>
    <xsl:value-of select="count(//test|//true|//text)"/>
    <xsl:value-of select="$newline"/>
    <xsl:variable name="numberOfQuestions" select="count(/test/question)"/>
    <xsl:for-each select="/test/question">
      <xsl:text>   This is question number </xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text> of </xsl:text>
      <xsl:value-of select="$numberOfQuestions"/>

      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here are the results of our stylesheet:

Tests of the count() function:

   count(/test)=1
   count(/true)=0
   count(//true)=2
   count(//test|//true|//text)=5
   This is question number 1 of 2
   This is question number 2 of 2

The first four invocations of the count() function merely use XPath expressions to count something in the XML document. The last use of count() counts the number of <question> elements in our document and stores that value in a variable. Generating text like "item x of y" is a common technique; our use of the count() and position() is how this generation is commonly done.

current() Function Returns a node-set that has the current node as its only member.

Synopsis

node-set current()

Inputs

None.

Output

A node-set that has the current node as its only member. Most of the time, the current node is no different than the context node. These two XSLT elements have the same meaning:

<xsl:value-of select="current()"/>
<xsl:value-of select="."/>

Within a predicate expression, however, the current node and the context node are usually different. The example section that follows illustrates when you need to use the current() function.

Defined in

XSLT section 12.4, Miscellaneous Additional Functions.

Example

We'll use the current() function along with a lookup table. Here's the document we'll transform:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here's our stylesheet. We'll do the same transform twice, one time with the current() function and one time without it:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="12">December</months:name>
  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the current() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=current()/@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>      (Averaged </xsl:text>
      <xsl:value-of
        select="format-number(miles-earned div miles-flown, '##.#')"/>
      <xsl:text> miles earned for each mile flown.)</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>

    <xsl:text>Let's try it again, without using current() this time:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>

    <xsl:for-each select="/report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=./@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>      (Averaged </xsl:text>
      <xsl:value-of
        select="format-number(miles-earned div miles-flown, '##.#')"/>
      <xsl:text> miles earned for each mile flown.)</xsl:text>

      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here are the results:

A test of the current() function:

   January - 12,379 miles flown, 35,215 miles earned.
      (Averaged 2.8 miles earned for each mile flown.)

   February - 32,857 miles flown, 92,731 miles earned.
      (Averaged 2.8 miles earned for each mile flown.)

   March - 19,920 miles flown, 76,725 miles earned.
      (Averaged 3.9 miles earned for each mile flown.)

   April - 18,903 miles flown, 31,781 miles earned.
      (Averaged 1.7 miles earned for each mile flown.)

Let's try it again, without using current() this time:

   December - 12,379 miles flown, 35,215 miles earned.
      (Averaged 2.8 miles earned for each mile flown.)

   December - 32,857 miles flown, 92,731 miles earned.
      (Averaged 2.8 miles earned for each mile flown.)

   December - 19,920 miles flown, 76,725 miles earned.
      (Averaged 3.9 miles earned for each mile flown.)

   December - 18,903 miles flown, 31,781 miles earned.
      (Averaged 1.7 miles earned for each mile flown.)

The second time around, our stylesheet matched each <month> element to the month December. The difference is that the dot syntax (.) represents the current node at that point in the XPath expression, while the current() function represents the current node before the XSLT processor began evaluating the XPath expression.

In other words, the XSLT processor starts with the first <months:name> element, attempting to find the element whose sequence attribute matches another sequence attribute we're examining. If we specify the other sequence attribute with ./@sequence, it indicates the sequence attribute of the current node at this point in the expression, which is the first <months:name> element. That always returns the value of the first <months:name> element. Using the current() function, on the other hand, returns the node that was current when we started to evaluate this expression; current() gives us the behavior we want.

document() Function Allows you to process multiple source documents in a single stylesheet. This extremely powerful and flexible function is the subject of , so we'll only include a brief overview of the function here.

Synopsis

node-set document(objectnode-set?)

Inputs

The document() function most commonly takes a string as its argument; that string is treated as a URI, and the XSLT processor attempts to open that URI and parse it. If the string is empty (the function call is document('')), the document() function parses the stylesheet itself. See Section 7.3, "Invoking the document() Function" in Chapter 7, "Combining XML Documents" for all the details on the parameters to the document() function.

Output

A node-set containing the nodes identified by the input argument. Again, Chapter 7, "Combining XML Documents" has all the details, so we won't rehash them here.

Defined in

XSLT section 12.1, Multiple Source Documents.

Example

The following example uses the document() function with an empty string to implement a lookup table. Here is our XML document:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

We can use the document() function to convert the sequence attribute of the <month> element into the name of the corresponding month. Here is our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>
  <months:name sequence="12">December</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the document() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=current()/@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>      (Averaged </xsl:text>
      <xsl:value-of
        select="format-number(miles-earned div miles-flown, '##.#')"/>
      <xsl:text> miles earned for each mile flown.)</xsl:text>

      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here are the results, with the correct month names included in the output:

A test of the document() function:

   January - 12,379 miles flown, 35,215 miles earned.
      (Averaged 2.8 miles earned for each mile flown.)

   February - 32,857 miles flown, 92,731 miles earned.
      (Averaged 2.8 miles earned for each mile flown.)

   March - 19,920 miles flown, 76,725 miles earned.
      (Averaged 3.9 miles earned for each mile flown.)

   April - 18,903 miles flown, 31,781 miles earned.
      (Averaged 1.7 miles earned for each mile flown.)
element-available() Function Determines if a given element is available to the XSLT processor. This function allows you to design stylesheets that react gracefully if a particular element is not available to process an XML document.

Synopsis

boolean element-available(string)

Inputs

The element's name. The name should be qualified with a namespace; if the namespace URI is the same as the XSLT namespace URI, then the element name refers to an element defined by XSLT. Otherwise, the name refers to an extension element. If the element name has a null namespace URI, then the element-available function returns false.

Output

The boolean value true if the element is available; false otherwise.

Defined in

XSLT section 15, Fallback.

Example

We'll use the following example to test the element-available() function:

<?xml version="1.0"?>
<book>
  <title>XSLT</title>
  <chapter>
    <title>Getting Started</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>The Hello World Example</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>XPath</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Stylesheet Basics</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Branching and Control Elements</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Functions</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Creating Links and Cross-References</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Sorting and Grouping Elements</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
  <chapter>
    <title>Combining XML Documents</title>
    <para>If this chapter had any text, it would appear here.</para>
  </chapter>
</book>

Here is our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"

  xmlns:saxon="http://icl.com/saxon"
  extension-element-prefixes="redirect saxon">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:choose>
      <xsl:when test="element-available('redirect:write')">
        <xsl:for-each select="/book/chapter">
          <redirect:write select="concat('chapter', position(), '.html')">
            <html>
              <head>
                <title><xsl:value-of select="title"/></title>
              </head>
              <body>
                <h1><xsl:value-of select="title"/></h1>
                <xsl:apply-templates select="para"/>
                <xsl:if test="not(position()=1)">
                  <p>
                    <a href="chapter{position()-1}.html">Previous</a>
                  </p>
                </xsl:if>
                <xsl:if test="not(position()=last())">
                  <p>
                    <a href="chapter{position()+1}.html">Next</a>
                  </p>
                </xsl:if>
              </body>
            </html>
          </redirect:write>
        </xsl:for-each>
      </xsl:when>
      <xsl:when test="element-available('saxon:output')">
        <xsl:for-each select="/book/chapter">
          <saxon:output file="chapter{position()}.html">
            <html>
              <head>
                <title><xsl:value-of select="title"/></title>
              </head>
              <body>
                <h1><xsl:value-of select="title"/></h1>
                <xsl:apply-templates select="para"/>
                <xsl:if test="not(position()=1)">
                  <p>
                    <a href="chapter{position()-1}.html">Previous</a>
                  </p>
                </xsl:if>
                <xsl:if test="not(position()=last())">

                  <p>
                    <a href="chapter{position()+1}.html">Next</a>
                  </p>
                </xsl:if>
              </body>
            </html>
          </saxon:output>
        </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
        <html>
          <head>
            <title><xsl:value-of select="/book/title"/></title>
          </head>
          <xsl:for-each select="/book/chapter">
            <h1><xsl:value-of select="title"/></h1>
            <xsl:apply-templates select="para"/>
          </xsl:for-each>
        </html>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="not(element-available('write'))">
      <xsl:message terminate="no">
        The <write> element is not available!
      </xsl:message>
    </xsl:if>
  </xsl:template>

  <xsl:template match="para">
    <p><xsl:apply-templates select="*|text()"/></p>
  </xsl:template>

</xsl:stylesheet>

This stylesheet attempts to take the content in the XML file and write portions of it out to different HTML files. The first <chapter> element is written to the file chapter1.html, the second <chapter> element is written to the file chapter2.html, and so on. Our stylesheet attempts to use Xalan's <redirect:write> element first; if that element is not available, it checks for Saxon's <saxon:output> element. If neither of those elements is available, it writes the contents of all <chapter> elements to the same output stream. The stylesheet also calls the element-available() function with the nonqualified element name write; this call always returns false because the element name is not namespace qualified.

When we use Xalan to process the XML file with our stylesheet, here are the results on the console:

file:///D:/O'Reilly/XSLT/bookSamples/AppendixC/elementavailable.xsl; Line 66;
Column 35; The <write> element is not available!

The stylesheet generates the files chapter1.html through chapter9.html, with each file containing data from one of the <chapter> elements in the original file. Our stylesheet also generates hyperlinks between the chapter files; here's what chapter3.html looks like:

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

      <title>XPath</title>
   </head>
   <body>
      <h1>XPath</h1>
      <p>If this chapter had any text, it would appear here.</p>
      <p><a href="chapter2.html">Previous</a></p>
      <p><a href="chapter4.html">Next</a></p>
   </body>
</html>

When rendered in a browser, the file looks like Figure C-1.

Figure C-1. Sample HTML output file

Clicking on the Previous link takes you to the file chapter2.html, while clicking on the Next link takes you to chapter4.html.

Using our stylesheet with Saxon (using the command java com.icl.saxon.StyleSheet chapterlist.xml elementavailable.xsl) produces similar results on the console:

The <write> element is not available!

Although the format of the message is slightly different, the output in the multiple HTML files is the same.

Finally, if we use the Oracle XML parser, none of the elements we query will be available, so all the output is written to a single file. We'll invoke the processor with this command. (The command should be on one line.)

java oracle.xml.parser.v2.oraxsl chapterlist.xml
  elementavailable.xsl chapters.html

Here's the console output:

Message: The <write> element is not available!

The output file, chapters.html, looks like this:

<html xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
  xmlns:saxon="http://icl.com/saxon">
   <head>
      <META http-equiv="Content-Type" content="text/html">
      <title>XSLT</title>
   </head>
   <h1>Getting Started</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>The Hello World Example</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>XPath</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>Stylesheet Basics</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>Branching and Control Elements</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>Functions</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>Creating Links and Cross-References</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>Sorting and Grouping Elements</h1>
   <p>If this chapter had any text, it would appear here.</p>
   <h1>Combining XML Documents</h1>
   <p>If this chapter had any text, it would appear here.</p>
</html>

When rendered, our output looks like Figure C-2.

Figure C-2. HTML document listing all chapters

In this example, the element-available() function allows us to determine what processing capabilities are available and respond gracefully to whatever we find.

false() Function Always returns the boolean value false. Remember that the strings "true" and "false" don't have any special significance in XSLT. This function (and the true() function) allow you to generate boolean values directly when you need them.

Synopsis

boolean false()

Inputs

None.

Output

The boolean value false.

Defined in

XPath section 4.3, Boolean Functions.

Example

Here's a brief example that uses the false() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the false() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="false()">
        <xsl:text>   "false()"   returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "false()"   returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

When using this stylesheet against any XML document, it generates this less-than-exciting result:

A test of the false() function:

   "false()"   returned false!
floor() Function Returns the largest integer that is not greater than the argument.

Synopsis

number floor(number)

Inputs

A number. If the argument is not a number, it is transformed into a number as if it had been processed by the number() function. If the argument cannot be transformed into a number, the floor() function returns NaN (not a number).

Output

The largest integer that is not greater than the argument, or NaN if the argument cannot be converted into a number.

Defined in

XPath section 4.4, Number Functions.

Example

The following stylesheet shows the results of invoking the floor() function against a variety of values. We'll use this XML document as input:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here's the stylesheet that uses the floor() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>
  <months:name sequence="12">December</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the floor() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   "floor('7.983')" = </xsl:text>
    <xsl:value-of select="floor('7.983')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "floor('-7.893')" = </xsl:text>
    <xsl:value-of select="floor('-7.893')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "floor(/report/month[@sequence='01']</xsl:text>
    <xsl:text>/miles-flown)" = </xsl:text>
    <xsl:value-of select="floor(/report/month[@sequence='01']/miles-flown)"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "floor(document('')/*/months:name</xsl:text>
    <xsl:text>[@sequence='02'])" = </xsl:text>
    <xsl:value-of select="floor(document('')/*/months:name[@sequence='02'])"/>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=current()/@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>      (Averaged </xsl:text>
      <xsl:value-of select="floor(miles-earned div miles-flown)"/>
      <xsl:text> miles earned for each mile flown.)</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here is the output of our stylesheet:

Tests of the floor() function:

   "floor('7.983')" = 7
   "floor('-7.893')" = -8
   "floor(/report/month[@sequence='01']/miles-flown)" = 12379
   "floor(document('')/*/months:name[@sequence='02'])" = NaN

   January - 12,379 miles flown, 35,215 miles earned.
      (Averaged 2 miles earned for each mile flown.)

   February - 32,857 miles flown, 92,731 miles earned.
      (Averaged 2 miles earned for each mile flown.)

   March - 19,920 miles flown, 76,725 miles earned.
      (Averaged 3 miles earned for each mile flown.)

   April - 18,903 miles flown, 31,781 miles earned.
      (Averaged 1 miles earned for each mile flown.)

Notice that when we invoked the ceiling() function against the string "February" (that's what document('')/*/months:name[@sequence='02'] resolves to), the function returned NaN. You can compare these results to those from the ceiling() function and the round() function.

format-number() Function Takes a number and formats it as a string.

Synopsis

string format-number(numberstringstring?)

Inputs

The number to be formatted and the format pattern string are required. The third argument is the optional name of a decimal format; if the third argument is not supplied, the default decimal format is used.

Output

The number, formatted according to the rules supplied by the other arguments. The special characters used in the second argument are:

#
Represents a digit. Trailing or leading zeroes are not displayed. Formatting the number 4.0 with the string "#.##" returns the string "4".

0
Represents a digit. Unlike the # character, the 0 always displays a zero. Formatting the number 4.1 with the string "#.00" returns the string "4.10".

.
Represents the decimal point.

-
Represents the minus sign.

,
Is the grouping separator.

;
Separates the positive-number pattern from the negative-number pattern.

%
Indicates that a number should be displayed as a percentage. The value will be multiplied by 100, then displayed as a percentage. Formatting the number .76 with the string "##%" returns the string "76%".

\u2030
Is the Unicode character for the per-thousand (per-mille) sign. The value will be multiplied by 1000, then displayed as a per mille. Formatting the number .768 with the string "###\u2030" returns the string "768 Figure C.11 ".

The third argument, if given, must be the name of an <xsl:decimal-format> element. The <xsl:decimal-format> element lets you define the character that should be used for the decimal point and the grouping separator, the string used to represent infinity, and other formatting options. See Reference A.10 for more information.

Defined in

XSLT section 12.3, Number Formatting.

Example

The following stylesheet uses the format-number() function in various ways:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <xsl:output method="text"/>

  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:decimal-format name="f1"
    decimal-separator=":"
    grouping-separator="/"/>

  <xsl:decimal-format name="f2"
    infinity="Really, really big"
    NaN="[not a number]"/>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the format-number() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(528.3, '#.#;-#.#')=</xsl:text>
    <xsl:value-of select="format-number(528.3, '#.#;-#.#')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(528.3, '0,000.00;-0,000.00')=</xsl:text>
    <xsl:value-of select="format-number(528.3, '0,000.00;-0,000.00')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(-23528.3, '$#,###.00;($#,###.00)')=</xsl:text>
    <xsl:value-of select="format-number(-23528.3, '$#,###.00;($#,###.00)')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(1528.3, '#/###:00', 'f1')=</xsl:text>
    <xsl:value-of select="format-number(1528.3, '#/###:00;-#/###:00', 'f1')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(1 div 0, '###,###.00', 'f2')=</xsl:text>
    <xsl:value-of select="format-number(1 div 0, '###,###.00', 'f2')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   format-number(blue div orange, '#.##', 'f2')=</xsl:text>
    <xsl:value-of select="format-number(blue div orange, '#.##', 'f2')"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=current()/@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>     (</xsl:text>
      <xsl:value-of
        select="format-number(miles-flown div sum(//miles-flown), '##%')"/>
      <xsl:text> of all miles flown, </xsl:text>
      <xsl:value-of
        select="format-number(miles-earned div sum(//miles-earned), '##%')"/>
      <xsl:text> of all miles earned.)</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:text>   Total miles flown: </xsl:text>
    <xsl:value-of select="format-number(sum(//miles-flown), '##,###')"/>
    <xsl:text>, total miles earned: </xsl:text>
    <xsl:value-of select="format-number(sum(//miles-earned), '##,###')"/>
  </xsl:template>

</xsl:stylesheet>

We'll use this XML document with our stylesheet:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

When we run this stylesheet, here are the results:

Tests of the format-number() function:

   format-number(528.3, '#.#;-#.#')=528.3
   format-number(528.3, '0,000.00;-0,000.00')=0,528.30
   format-number(-23528.3, '$#,###.00;($#,###.00)')=($23,528.30)
   format-number(1528.3, '#/###:00', 'f1')=1/528:30
   format-number(1 div 0, '###,###.00', 'f2')=Really, really big
   format-number(blue div orange, '#.##', 'f2')=[not a number]

   January - 12,379 miles flown, 35,215 miles earned.
     (15% of all miles flown, 15% of all miles earned.)

   February - 32,857 miles flown, 92,731 miles earned.
     (39% of all miles flown, 39% of all miles earned.)

   March - 19,920 miles flown, 76,725 miles earned.
     (24% of all miles flown, 32% of all miles earned.)

   April - 18,903 miles flown, 31,781 miles earned.
     (22% of all miles flown, 13% of all miles earned.)

   Total miles flown: 84,059, total miles earned: 236,452

The first few examples illustrate some of the more complicated formatting options available, along with references to the <xsl:decimal-format> elements in the stylesheet. The last section is a more typical use of the format-number function: formatting values selected or calculated from an XML document.

function-available() Function Determines if a given function is available to the XSLT processor. This function allows you to design stylesheets that react gracefully if a particular function is not available to process an XML document.

Synopsis

boolean function-available(string)

Inputs

The name function's name. The name is usually qualified with a namespace; if the namespace of the function name is non-null, the function is an extension function. Otherwise, the function is one of the functions defined in the XSLT or XPath specifications.

Output

The boolean value true if the function is available, false otherwise.

Defined in

XSLT section 15, Fallback.

Example

We'll use the following XML document to test the function-available() function:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>

  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

Here's our stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:jpeg="class:JPEGWriter"
  extension-element-prefixes="jpeg">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="list/listitem">
      <xsl:choose>
        <xsl:when test="function-available('jpeg:createJPEG')">
          <xsl:value-of
            select="jpeg:createJPEG(., 'bg.jpg',
            concat('album', position(), '.jpg'),
            'Swiss 721 Bold Condensed', 'BOLD', 22, 52, 35)"/>
          <xsl:text>See the file </xsl:text>
          <xsl:value-of select="concat('album', position(), '.jpg')"/>
          <xsl:text> to see the title of album #</xsl:text>
          <xsl:value-of select="position()"/>
          <xsl:value-of select="$newline"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="position()"/>
          <xsl:text>. </xsl:text>
          <xsl:value-of select="."/>
          <xsl:value-of select="$newline"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

In our stylesheet, if the createJPEG() function is available, we'll invoke it to create JPEG files for the titles of all our favorite albums. If the function is not available, we'll simply write those titles to the output stream. Here are the results we get when the createJPEG() function is available:

See the file album1.jpg to see the title of album #1
See the file album2.jpg to see the title of album #2
See the file album3.jpg to see the title of album #3
See the file album4.jpg to see the title of album #4
See the file album5.jpg to see the title of album #5
See the file album6.jpg to see the title of album #6
See the file album7.jpg to see the title of album #7
See the file album8.jpg to see the title of album #8

All album titles (the text of the <listitem> elements) are converted to JPEG graphics. In this example, the file album8.jpg looks like Figure C-3.

Figure C-3. Generated graphic for the eighth <listitem> element

If we delete the file JPEGWriter.class (if the .class file is missing, the function isn't available), we get these results instead:

1. A Love Supreme
2. Beat Crazy
3. Here Come the Warm Jets
4. Kind of Blue
5. London Calling
6. Remain in Light
7. The Joshua Tree
8. The Indestructible Beat of Soweto
generate-id() Function Generates a unique ID (an XML name) for a given node. If no node-set is given, generate-id() generates an ID for the context node.

Synopsis

string generate-id(node-set?)

Inputs

An optional node-set. If no node-set is given, this function generates an ID for the context node. If the node-set is empty, generate-id() returns an empty string.

Output

A unique ID, or an empty string if an empty node-set is given. Several things about the generate-id() function are important to know:

  • For a given transformation, every time you invoke generate-id() against a given node, the XSLT processor must return the same ID. The ID can't change while you're doing a transformation. If you ask the XSLT processor to transform your document with this stylesheet tomorrow, there's no guarantee that generate-id() will generate the same ID the second time around. All of tomorrow's calls to generate-id() will generate the same ID, but that ID might not be the one generated today.

  • The generate-id() function is not required to check if its generated ID duplicates an ID that's already in the document. In other words, if an element in your document has an attribute of type ID with the value sdk3829a, there's a remote possibility that an ID returned by generate-id() would have the value sdk3829a. It's not likely, but it could happen.

  • If you invoke generate-id() against two different nodes, the two generated IDs must be different.

  • Given a node-set, generate-id() returns an ID for the node in the node-set that occurs first in document order.

  • If the node-set you pass to the function is empty (you invoke generate-id(fleeber), but there are no <fleeber> elements in the current context), generate-id() returns an empty string.

Defined in

XSLT section 12.4, Miscellaneous Additional Functions.

Example

Here's a simple stylesheet that uses the document('') function to access all of its own <xsl:text> nodes. It then uses generate-id() to generate a unique ID for each of those nodes, then calls generate-id() again to illustrate that the function generates the same ID for a given node. Here's the stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the generate-id() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="document('')//xsl:text">
      <xsl:text>Node name: </xsl:text>
      <xsl:value-of select="name()"/>
      <xsl:text> - generated id: </xsl:text>
      <xsl:value-of select="generate-id()"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Now we'll try it again...</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="document('')//xsl:text">
      <xsl:text>Node name: </xsl:text>
      <xsl:value-of select="name()"/>
      <xsl:text> - generated id: </xsl:text>
      <xsl:value-of select="generate-id()"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Our stylesheet generates these results:

A test of the generate-id() function:

Node name: xsl:text - generated id: NC
Node name: xsl:text - generated id: N16
Node name: xsl:text - generated id: N22
Node name: xsl:text - generated id: N28
Node name: xsl:text - generated id: N38
Node name: xsl:text - generated id: N44
Node name: xsl:text - generated id: N4A

Now we'll try it again...

Node name: xsl:text - generated id: NC
Node name: xsl:text - generated id: N16
Node name: xsl:text - generated id: N22
Node name: xsl:text - generated id: N28
Node name: xsl:text - generated id: N38
Node name: xsl:text - generated id: N44
Node name: xsl:text - generated id: N4A

The IDs generated each time are the same.

id() Function Returns the node in the source tree whose ID attribute matches the value passed in as input.

Synopsis

node-set id(object)

Inputs

An object. If the input object is a node-set, the result is a node-set that contains the result of applying the id() function to the string value of each node in the argument node-set. Usually, the argument is some other node type, which is (or is converted to) a string. That string is then used as the search value while all attributes of type ID are searched.

Remember that a limitation of the XML ID datatype is that a single set of names across all attributes is declared to be of type ID. The XSLT key() function and the associated <xsl:key> element address this and other limitations; see the key() function and <xsl:key> for more information.

Output

A node-set containing all nodes whose attributes of type ID match the string values of the input node-set. In practice, this node-set is a single node, the node whose attribute of type ID matches a string value.

Defined in

XPath section 4.1, Node Set Functions.

Example

For our example, we'll take this shortened version of the glossary we discussed earlier:

<?xml version="1.0" ?>
<!DOCTYPE glossary SYSTEM "glossary.dtd">
<glossary>
  <glentry>
    <term id="applet">applet</term>
    <defn>
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.
      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the filesystem of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <xref refid="servlet"/>.
    </defn>
  </glentry>

  <glentry>
    <term id="servlet">servlet</term>
    <defn>
      An application program, written in the Java programming language,
      that is executed on a web server. A reference to a servlet
      appears in the markup for a web page, in the same way that a
      reference to a graphics file appears. The web server executes
      the servlet and sends the results of the execution (if there are
      any) to the web browser. Contrast with <xref refid="applet" />.
    </defn>
  </glentry>
</glossary>

Here's the stylesheet we'll use to resolve the references:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <xsl:apply-templates select="glossary"/>
  </xsl:template>

  <xsl:template match="glossary">
    <html>
      <head>
        <title>
          <xsl:text>Glossary Listing </xsl:text>
        </title>
      </head>
      <body>
        <h1>
          <xsl:text>Glossary Listing </xsl:text>
        </h1>
        <xsl:apply-templates select="glentry"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="glentry">
    <p>
      <b>
        <a>
          <xsl:attribute name="name">
            <xsl:value-of select="term/@id" />
          </xsl:attribute>
        </a>

        <xsl:value-of select="term"/>
        <xsl:text>: </xsl:text>
      </b>
      <xsl:apply-templates select="defn"/>
    </p>
  </xsl:template>

  <xsl:template match="defn">
    <xsl:apply-templates
     select="*|comment()|processing-instruction()|text()"/>
  </xsl:template>

  <xsl:template match="xref">
    <a>
      <xsl:attribute name="href">
        <xsl:text>#</xsl:text><xsl:value-of select="@refid"/>
      </xsl:attribute>
      <xsl:choose>
        <xsl:when test="id(@refid)/@xreftext">
          <xsl:value-of select="id(@refid)/@xreftext"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="id(@refid)"/>
        </xsl:otherwise>
      </xsl:choose>
    </a>
  </xsl:template>

</xsl:stylesheet>

Our stylesheet generates these results:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Glossary Listing </title>
</head>
<body>
<h1>Glossary Listing </h1>
<p>
<b><a name="applet"></a>applet: </b>
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.

      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the filesystem of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <a href="#servlet">servlet</a>.
    </p>
<p>
<b><a name="servlet"></a>servlet: </b>
      An application program, written in the Java programming language,
      that is executed on a web server. A reference to a servlet
      appears in the markup for a web page, in the same way that a
      reference to a graphics file appears. The web server executes
      the servlet and sends the results of the execution (if there are
      any) to the web browser. Contrast with <a href="#applet">applet</a>.
    </p>
</body>
</html>

When rendered in a browser, our hyperlinked document looks like Figure C-4.

Figure C-4. Generated HTML glossary

key() Function References a relation defined with an <xsl:key> element. Conceptually, the key() function works similarly to the id() function, although keys are more flexible than IDs.

Synopsis

node-set key(stringobject)

Inputs

The name of the key (defined by an <xsl:key> element) and an object. If the object is a node-set, then the key() function applies itself to the string value of each node in the node-set and returns the node-set of the result of all those key() function invocations. If the object is any other type, it is converted to a string as if by a call to the string() function.

Output

A node-set containing the nodes in the same document as the context node whose values for the requested key match the search argument(s). In other words, if our stylesheet has an <xsl:key> element that defines a key named postalcodes based on the <postalcode> child of all <address> elements in the current document, the function call key(postalcodes, '34829') returns a node-set containing all the <address> elements with a <postalcode> element whose value is 34829.

Defined in

XSLT section 12.2, Keys.

Example

To illustrate the power of the key() function, we'll use this document -- a truncated version of the glossary we discussed in Chapter 5, "Creating Links and Cross-References":

<?xml version="1.0" ?>
<glossary>
  <glentry>
    <term id="applet">applet</term>
    <defn topic="Java" language="en">
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.
      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the filesystem of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <xref refid="servlet"/>.
    </defn>

    <defn topic="Java" language="it">
      [Pretend this is an Italian definition of applet.]
    </defn>
    <defn topic="Java" language="es">
      [Pretend this is a Spanish definition of applet.]
    </defn>
  </glentry>

  <glentry>
    <term id="DMZlong" xreftext="demilitarized zone">demilitarized
      zone (DMZ)</term>
    <defn topic="security" language="en">
      In network security, a network that is isolated from, and
      serves as a neutral zone between, a trusted network (for example,
      a private intranet) and an untrusted network (for example, the
      Internet). One or more secure gateways usually control access
      to the DMZ from the trusted or the untrusted network.
    </defn>
    <defn topic="security" language="it">
      [Pretend this is an Italian definition of DMZ.]
    </defn>
    <defn topic="security" language="es">
      [Pretend this is a Spanish definition of DMZ.]
    </defn>
    <defn topic="security" language="jp">
      [Pretend this is a Japanese definition of DMZ.]
    </defn>
    <defn topic="security" language="de">
      [Pretend this is a German definition of DMZ.]
    </defn>
  </glentry>

  <glentry>
    <term id="servlet">servlet</term>
    <defn topic="Java" language="en">
      An application program, written in the Java programming language,
      that is executed on a web server. A reference to a servlet
      appears in the markup for a web page, in the same way that a
      reference to a graphics file appears. The web server executes
      the servlet and sends the results of the execution (if there are
      any) to the web browser. Contrast with <xref refid="applet" />.
    </defn>
    <defn topic="Java" language="es">
      [Pretend this is a Spanish definition of servlet.]
    </defn>
    <defn topic="Java" language="it">
      [Pretend this is an Italian definition of servlet.]
    </defn>

    <defn topic="Java" language="de">
      [Pretend this is a German definition of servlet.]
    </defn>
    <defn topic="Java" language="jp">
      [Pretend this is a Japanese definition of servlet.]
    </defn>
  </glentry>
</glossary>

Here's the stylesheet we'll use to process this document. Notice that we define two <xsl:key> elements to index the XML document in two different ways:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*"/>

  <xsl:key name="language-index" match="defn" use="@language"/>
  <xsl:key name="term-ids"       match="term" use="@id"/>

  <xsl:param name="targetLanguage"/>

  <xsl:template match="/">
    <xsl:apply-templates select="glossary"/>
  </xsl:template>

  <xsl:template match="glossary">
    <html>
      <head>
        <title>
          <xsl:text>Glossary Listing: </xsl:text>
        </title>
      </head>
      <body>
        <h1>
          <xsl:text>Glossary Listing: </xsl:text>
        </h1>
        <xsl:for-each select="key('language-index', $targetLanguage)">
          <xsl:apply-templates select="ancestor::glentry"/>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="glentry">
    <p>
      <b>
        <a>
          <xsl:attribute name="name">
            <xsl:value-of select="term/@id" />
          </xsl:attribute>
        </a>
        <xsl:value-of select="term"/>
        <xsl:text>: </xsl:text>
      </b>
      <xsl:apply-templates select="defn[@language=$targetLanguage]"/>
    </p>
  </xsl:template>

  <xsl:template match="defn">
    <xsl:apply-templates
     select="*|comment()|processing-instruction()|text()"/>
  </xsl:template>

  <xsl:template match="xref">
    <a>
      <xsl:attribute name="href">
        <xsl:text>#</xsl:text><xsl:value-of select="@refid"/>
      </xsl:attribute>
      <xsl:choose>
        <xsl:when test="key('term-ids', @refid)[1]/@xreftext">
          <xsl:value-of select="key('term-ids', @refid)[1]/@xreftext"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="key('term-ids', @refid)[1]"/>
        </xsl:otherwise>
      </xsl:choose>
    </a>
  </xsl:template>

</xsl:stylesheet>

Transforming the glossary with a targetLanguage of en gives these results:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Glossary Listing: </title>
</head>
<body>
<h1>Glossary Listing: </h1>
<p>
<b><a name="applet"></a>applet: </b>
      An application program,
      written in the Java programming language, that can be
      retrieved from a web server and executed by a web browser.
      A reference to an applet appears in the markup for a web
      page, in the same way that a reference to a graphics
      file appears; a browser retrieves an applet in the same
      way that it retrieves a graphics file.
      For security reasons, an applet's access rights are limited
      in two ways: the applet cannot access the filesystem of the
      client upon which it is executing, and the applet's
      communication across the network is limited to the server
      from which it was downloaded.
      Contrast with <a href="#servlet">servlet</a>.
    </p>
<p>
<b><a name="DMZlong"></a>demilitarized
      zone (DMZ): </b>
      In network security, a network that is isolated from, and
      serves as a neutral zone between, a trusted network (for example,
      a private intranet) and an untrusted network (for example, the
      Internet). One or more secure gateways usually control access
      to the DMZ from the trusted or the untrusted network.
    </p>
<p>
<b><a name="servlet"></a>servlet: </b>
      An application program, written in the Java programming language,
      that is executed on a web server. A reference to a servlet
      appears in the markup for a web page, in the same way that a
      reference to a graphics file appears. The web server executes
      the servlet and sends the results of the execution (if there are
      any) to the web browser. Contrast with <a href="#applet">applet</a>.
    </p>
</body>
</html>

Figure C-5 shows how this document looks when it's rendered in a browser. Using a targetLanguage of jp gives us these results instead:

Figure C-5. Generated HTML glossary

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Glossary Listing: </title>
</head>
<body>
<h1>Glossary Listing: </h1>
<p>
<b><a name="DMZlong"></a>demilitarized
      zone (DMZ): </b>
      [Pretend this is a Japanese definition of DMZ.]
    </p>
<p>
<b><a name="servlet"></a>servlet: </b>
      [Pretend this is a Japanese definition of servlet.]
    </p>
</body>
</html>
      </programlisting>

When rendered, the document looks like Figure C-6. Notice that we get entirely different results when we change the targetLanguage.

Figure C-6. Generated HTML glossary

lang() Function Determines whether a given language string is the same as, or is a sublanguage of, the language of the context node, as defined by an xml:lang attribute.

Synopsis

boolean lang(string)

Inputs

A string representing a language code. If the context node has a language of xml:lang="en-us", invoking the lang() function with any of the values en, EN, and en-us returns the boolean value true, while invoking lang() with the value en-gb returns the boolean value false.

Output

If the argument string is the same as, or is a sublanguage of, the context node's language, lang() returns the boolean value true. If the context node does not have an xml:lang attribute, then the value of the xml:lang attribute of its nearest ancestor is used instead. If there is no such attribute, then the lang() function returns the boolean value false. When comparing the language code of the context node with the argument string, the lang() function ignores case.

Defined in

XPath section 4.3, Boolean Functions.

Example

Here is an XML document that uses language codes:

<?xml version="1.0"?>
<list xml:lang="en">
  <title>Albums I've bought recently:</title>
  <listitem>The Sacred Art of Dub</listitem>
  <listitem>Only the Poor Man Feel It</listitem>
  <listitem>Excitable Boy</listitem>
  <listitem xml:lang="sw">Aki Special</listitem>
  <listitem xml:lang="en-gb">Combat Rock</listitem>
  <listitem xml:lang="zu">Talking Timbuktu</listitem>
  <listitem xml:lang="jz">The Birth of the Cool</listitem>
</list>

Here's a stylesheet that uses the lang() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="list/listitem">
      <xsl:choose>
        <xsl:when test="lang('EN')">
          <xsl:text>Here's an English-language album: </xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>-------> Here's some World music: </xsl:text>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:value-of select="."/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Finally, here are the results:

Here's an English-language album: The Sacred Art of Dub
Here's an English-language album: Only the Poor Man Feel It
Here's an English-language album: Excitable Boy
-------> Here's some World music: Aki Special
Here's an English-language album: Combat Rock
-------> Here's some World music: Talking Timbuktu
-------> Here's some World music: The Birth of the Cool
last() Function Returns the position of the last node in the current context. This function is useful for defining templates for the last occurrence of a given element or for testing if a given node is the last in the node-set to which it belongs.

Synopsis

number last()

Inputs

None.

Output

A number equal to the number of nodes in the current context. For example, if the current context contains 12 <li> nodes, last() returns 12.

Defined in

XPath section 4.1, Node Set Functions.

Example

We'll use the last() function to handle the last item in a list in a special way. Here's the XML document we'll use:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

Here is the stylesheet that handles the last <listitem> in the list differently:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="/list/title"/>
        </title>
      </head>
      <body>
        <h1>
          <xsl:value-of select="/list/title"/>
        </h1>
        <ul>
          <xsl:for-each select="/list/listitem">
            <xsl:choose>
              <xsl:when test="position()=last()">
                <li><b>Last, but not least: </b><xsl:value-of select="."/></li>
              </xsl:when>
              <xsl:otherwise>
                <li><xsl:value-of select="."/></li>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each>
        </ul>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

When we transform the XML document with this stylesheet, here are the results:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>A few of my favorite albums</title>
</head>
<body>
<h1>A few of my favorite albums</h1>
<ul>
<li>A Love Supreme</li>
<li>Beat Crazy</li>
<li>Here Come the Warm Jets</li>
<li>Kind of Blue</li>
<li>London Calling</li>
<li>Remain in Light</li>
<li>The Joshua Tree</li>
<li>
<b>Last, but not least: </b>The Indestructible Beat of Soweto</li>
</ul>
</body>
</html>

When rendered, the HTML file looks like Figure C-7.

Figure C-7. Generated HTML document

local-name() Function Returns the local part of the first node in the argument node-set.

Synopsis

string local-name(node-set?)

Inputs

A node-set. If the node-set is empty, the function returns an empty string. If the node-set is omitted, the function uses a node-set with the context node as its only member.

Output

A string corresponding to the local name of the first element in the argument node-set. If the node-set is empty, the local-name() function returns an empty string.

Defined in

XPath section 4.1, Node Set Functions.

Example

Here is a stylesheet that uses the document() function to process all its own nodes. It then calls the local-name() function against each node.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="12">December</months:name>
  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the local-name() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="document('')//*">
      <xsl:text>local-name: </xsl:text>
      <xsl:value-of select="local-name()"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

The stylesheet generates these results:

A test of the local-name() function:

local-name: stylesheet
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: name
local-name: output
local-name: variable
local-name: text
local-name: template
local-name: value-of
local-name: text
local-name: value-of
local-name: value-of
local-name: for-each
local-name: text
local-name: value-of
local-name: value-of
name() Function Returns the qualified name of a node. The qualified name includes the appropriate namespace prefix. For information on the namespace URI (not the prefix), XPath provides the namespace-uri() function.

Synopsis

string name(node-set?)

Inputs

An optional node-set. If no node-set is given, the name() function creates a node-set with the context node as its only member.

Output

The expanded name of the node. If the argument node-set is empty, or if the first node in the node-set does not have an expanded name, an empty string is returned.

Defined in

XPath section 4.1, Node Set Functions.

Example

Here is the XML document we'll use to demonstrate the name() function:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

We'll use this stylesheet to output the value of the name() function for each node in the XML document:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="12">December</months:name>
  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the name() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="document('')//*">
      <xsl:text>name: </xsl:text>
      <xsl:value-of select="name()"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

When we transform the XML document with this stylesheet, here are the results:

A test of the name() function:

name: xsl:stylesheet
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: months:name
name: xsl:output
name: xsl:variable
name: xsl:text
name: xsl:template
name: xsl:value-of
name: xsl:text
name: xsl:value-of
name: xsl:value-of
name: xsl:for-each
name: xsl:text
name: xsl:value-of
name: xsl:value-of
namespace-uri() Function Returns the namespace URI of the first node in the argument node-set.

Synopsis

string namespace-uri(node-set?)

Inputs

A node-set. If the node-set is omitted, the namespace-uri() function creates a node-set that has the context node as its only member.

Output

The namespace URI of the first node in the argument node-set. If the argument node-set is empty, the first node has no namespace URI, or the first node has a namespace URI that is null, an empty string is returned. Be aware that the namespace-uri() function returns an empty string for all nodes other than element and attribute nodes.

Defined in

XPath section 4.1, Node Set Functions.

Example

Here is a stylesheet that uses the document() function to examine its own nodes and then invoke the namespace-uri() against each of them:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>
  <months:name sequence="12">December</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the namespace-uri() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="document('')//*">
      <xsl:text>namespace URI: </xsl:text>
      <xsl:value-of select="namespace-uri()"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here are the results of our stylesheet:

A test of the namespace-uri() function:

namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: Lookup table for month names
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
namespace URI: http://www.w3.org/1999/XSL/Transform
normalize-space() Function Removes extra whitespace from its argument string.

Synopsis

string normalize-space(string?)

Inputs

An optional string. If the argument is omitted, the normalize-space() function uses the string value of the context node.

Output

The argument string, with whitespace removed as follows:

  • All leading whitespace is removed.

  • All trailing whitespace is removed.

  • Within the string, any sequence of whitespace characters is replaced with a single space.

Defined in

XPath section 4.2, String Functions.

Example

Here is a short example that demonstrates how normalize-space() works:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:variable name="testString">
    <xsl:text>                 This
is

a string
with lots of

whitespace.

</xsl:text>
    </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the normalize-space() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   normalize-space('       Hello,            World!')="</xsl:text>
    <xsl:value-of select="normalize-space('       Hello,            World!')"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   normalize-space($newline)="</xsl:text>
    <xsl:value-of select="normalize-space($newline)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   normalize-space($testString)="</xsl:text>
    <xsl:value-of select="normalize-space($testString)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

The stylesheet generates this output:

Tests of the normalize-space() function:

   normalize-space('       Hello,            World!')="Hello, World!"
   normalize-space($newline)=""
   normalize-space($testString)="This is a string with lots of whitespace."
not() Function Returns the negation of its argument. If the argument is not a boolean value already, it is converted to a boolean value using the rules described in the boolean() function entry.

Synopsis

boolean not(boolean)

Inputs

A boolean value, or more commonly, an XPath expression that evaluates to a boolean value.

Output

false if the input parameter is true; true if the input parameter is false.

Defined in

XPath section 4.3, Boolean Functions.

Example

To demonstrate the not() function, we'll use the same stylesheet and XML document we used for the boolean() function. Here's our XML document:

<?xml version="1.0"?>
<test>
<p>This is a test XML document used by several
of our sample stylesheets.</p>
<question>
<text>When completed, the Eiffel Tower was the
tallest building in the world.</text>
<true>Yes!  The Eiffel Tower was the world's
tallest building until 1932, when
New York's Empire State Building opened. </true>
<false>No, the Eiffel Tower was the world's
tallest building for over 30 years.</false>
</question>
</test>

We'll process this document with the following stylesheet, which uses the not() to negate all boolean() function calls:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the not() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="not(boolean(true()))">
        <xsl:text>   "not(boolean(true()))"   returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "not(boolean(true()))"   returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="not(boolean(true))">
        <xsl:text>   "not(boolean(true))"     returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "not(boolean(true))"     returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="not(boolean('false'))">
        <xsl:text>   "not(boolean('false'))"  returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "not(boolean('false'))"  returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="not(boolean('7'))">
        <xsl:text>   "not(boolean('7'))"      returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "not(boolean('7'))"      returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="not(boolean(/true))">
        <xsl:text>   "not(boolean(/true))"    returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "not(boolean(/true))"    returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>

    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="not(boolean(//true))">
        <xsl:text>   "not(boolean(//true))"   returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "not(boolean(//true))"   returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Here are the results:

Tests of the not() function:

   "not(boolean(true()))"   returned false!
   "not(boolean(true))"     returned true!
   "not(boolean('false'))"  returned false!
   "not(boolean('7'))"      returned false!
   "not(boolean(/true))"    returned true!
   "not(boolean(//true))"   returned false!

As you'd expect, these results are the exact opposite of the results we got when we tested the boolean() function.

number() Function Converts its argument to a number.

Synopsis

number number(object?)

Inputs

An object. The object is converted to a number as described in the following subsection.

Output

A number. The object is converted to a number as follows:

  • If the argument is a boolean value, the value true is converted to the number 1; the value false is converted to the number 0.

  • If the argument is a node-set, the node-set is converted to a string as if it were passed to the string() function, then that string is converted to a number like any other string. (Remember that the string() function returns the string value of the first node in the node-set.)

  • If the argument is a string, it is converted as follows:

    • If the string consists of optional whitespace, followed by an optional minus sign (-), followed by a number, followed by whitespace, it is converted to the floating-point value nearest to the mathematical value represented by the string. (The IEEE 754 standard defines a round-to-nearest rule; see the standard for more information.)

    • Any other string is converted to the value NaN (not a number).

  • If the argument is any other type, it is converted to a number in a way that depends on that type. See the documentation for your XSLT processor to find out what other types are supported and how they are converted to numbers.

Defined in

XPath section 4.4, Number Functions.

Example

Here is the XML document we'll use to test the number() function:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

We'll test the number() function with a variety of arguments:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the number() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   number(true())=</xsl:text>
    <xsl:value-of select="number(true())"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   number(false())=</xsl:text>
    <xsl:value-of select="number(false())"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   number(/report/month[2]/miles-flown)=</xsl:text>
    <xsl:value-of select="number(/report/month[2]/miles-flown)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   number(//miles-flown)=</xsl:text>
    <xsl:value-of select="number(//miles-flown)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   number(/report/title)=</xsl:text>
    <xsl:value-of select="number(/report/title)"/>
  </xsl:template>

</xsl:stylesheet>

The output of our stylesheet looks like this:

Tests of the number() function:

   number(true())=1
   number(false())=0
   number(/report/month[2]/miles-flown)=32857
   number(//miles-flown)=12379
   number(/report/title)=NaN
position() Function Returns a number equal to the context position from the current context.

Synopsis

number position()

Inputs

None.

Output

A number equal to the position of the current node in the evaluation context.

Defined in

XPath section 4.1, Node Set Functions.

Examples

This example uses the position() function to determine the background color of the rows of a table. The background colors cycle through the options white, darkgray, and lightgreen. Here's the XML document we'll use:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

We'll use this stylesheet to generate our HTML document:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="/list/title"/>
        </title>
      </head>
      <body>
        <h1>
          <xsl:value-of select="/list/title"/>
        </h1>
        <table border="1">
          <xsl:for-each select="/list/listitem">
            <xsl:variable name="background-color">
              <xsl:choose>
                <xsl:when test="position() mod 3 = 1">white</xsl:when>
                <xsl:when test="position() mod 3 = 2">darkgray</xsl:when>
                <xsl:otherwise>lightgreen</xsl:otherwise>
              </xsl:choose>
            </xsl:variable>
            <tr bgcolor="{$background-color}">
              <td>
                <b><xsl:value-of select="."/></b>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Our stylesheet generates the following results:

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>A few of my favorite albums</title>
</head>
<body>
<h1>A few of my favorite albums</h1>
<table border="1">
<tr bgcolor="white">
<td><b>A Love Supreme</b></td>
</tr>
<tr bgcolor="darkgray">
<td><b>Beat Crazy</b></td>
</tr>
<tr bgcolor="lightgreen">
<td><b>Here Come the Warm Jets</b></td>
</tr>
<tr bgcolor="white">
<td><b>Kind of Blue</b></td>
</tr>
<tr bgcolor="darkgray">
<td><b>London Calling</b></td>
</tr>
<tr bgcolor="lightgreen">
<td><b>Remain in Light</b></td>
</tr>
<tr bgcolor="white">
<td><b>The Joshua Tree</b></td>
</tr>
<tr bgcolor="darkgray">
<td><b>The Indestructible Beat of Soweto</b></td>
</tr>
</table>
</body>
</html>

When rendered, the HTML file looks like Figure C-8.

Figure C-8. HTML file displaying items with different background colors

round() Function Returns the integer closest to the argument.

Synopsis

number round(number)

Description

If two numbers are equally close to the argument (1 and 2 are equally close to 1.5), the number closest to positive infinity is returned. Various argument values are handled as follows:

  • If the argument is NaN (not a number), the round() function returns NaN.

  • If the argument is positive infinity, then positive infinity is returned.

  • If the argument is negative infinity, then negative infinity is returned.

  • If the argument is positive zero, then positive zero is returned.

  • If the argument is negative zero, then negative zero is returned.

  • If the argument is between zero and -0.5, then negative zero is returned.

Inputs

A number. If the argument is not a number, it is converted to a number as if it were passed to the number() function.

Output

The integer that is closest to the argument. Special cases are handled as described in this section.

Defined in

XPath section 4.4, Number Functions.

Example

The following stylesheet shows the results of invoking the round() function against a variety of values. We'll use this XML document as input:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here's the stylesheet that uses the round() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:months="Lookup table for month names">

  <months:name sequence="01">January</months:name>
  <months:name sequence="02">February</months:name>
  <months:name sequence="03">March</months:name>
  <months:name sequence="04">April</months:name>
  <months:name sequence="05">May</months:name>
  <months:name sequence="06">June</months:name>
  <months:name sequence="07">July</months:name>
  <months:name sequence="08">August</months:name>
  <months:name sequence="09">September</months:name>
  <months:name sequence="10">October</months:name>
  <months:name sequence="11">November</months:name>
  <months:name sequence="12">December</months:name>

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the round() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   "round('7.983')" = </xsl:text>
    <xsl:value-of select="round('7.983')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "round('7.5')" = </xsl:text>
    <xsl:value-of select="round('7.5')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "round('-7.893')" = </xsl:text>
    <xsl:value-of select="round('-7.893')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "round('-7.5')" = </xsl:text>
    <xsl:value-of select="round('-7.5')"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "round(/report/month[@sequence='01']/miles-flown)" = </xsl:text>
    <xsl:value-of select="round(/report/month[@sequence='01']/miles-flown)"/>

    <xsl:value-of select="$newline"/>
    <xsl:text>   "round(document('')/*/months:name[@sequence='02'])" = </xsl:text>
    <xsl:value-of select="round(document('')/*/months:name[@sequence='02'])"/>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>

    <xsl:for-each select="/report/month">
      <xsl:text>   </xsl:text>
      <xsl:value-of
        select="document('')/*/months:name[@sequence=current()/@sequence]"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="format-number(miles-flown, '##,###')"/>
      <xsl:text> miles flown, </xsl:text>
      <xsl:value-of select="format-number(miles-earned, '##,###')"/>
      <xsl:text> miles earned.</xsl:text>
      <xsl:value-of select="$newline"/>
      <xsl:text>      (Averaged </xsl:text>
      <xsl:value-of select="round(miles-earned div miles-flown)"/>
      <xsl:text> miles earned for each mile flown.)</xsl:text>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

When we process our XML document with this stylesheet, the results are:

Tests of the round() function:

   "round('7.983')" = 8
   "round('7.5')" = 8
   "round('-7.893')" = -8
   "round('-7.5')" = -7
   "round(/report/month[@sequence='01']/miles-flown)" = 12379
   "round(document('')/*/months:name[@sequence='02'])" = NaN

   January - 12,379 miles flown, 35,215 miles earned.
      (Averaged 3 miles earned for each mile flown.)

   February - 32,857 miles flown, 92,731 miles earned.
      (Averaged 3 miles earned for each mile flown.)

   March - 19,920 miles flown, 76,725 miles earned.
      (Averaged 4 miles earned for each mile flown.)

   April - 18,903 miles flown, 31,781 miles earned.
      (Averaged 2 miles earned for each mile flown.)

You can compare these results to those from the ceiling() and floor() functions.

starts-with() Function Determines if the first argument string begins with the second argument.

Synopsis

boolean starts-with(stringstring)

Inputs

Two strings.

Output

If the first string begins with the second, starts-with() returns the boolean value true; otherwise it returns false.

Defined in

XPath section 4.2, String Functions.

Example

We'll use this sample XML document:

<?xml version="1.0"?>
<list>
  <title>A few of my favorite albums</title>
  <listitem>A Love Supreme</listitem>
  <listitem>Beat Crazy</listitem>
  <listitem>Here Come the Warm Jets</listitem>
  <listitem>Kind of Blue</listitem>
  <listitem>London Calling</listitem>
  <listitem>Remain in Light</listitem>
  <listitem>The Joshua Tree</listitem>
  <listitem>The Indestructible Beat of Soweto</listitem>
</list>

This stylesheet outputs contents of all <listitem> elements that begin with the string "The":

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="list/listitem">
      <xsl:if test="starts-with(., 'The')">
        <xsl:value-of select="position()"/>
        <xsl:text>. </xsl:text>
        <xsl:value-of select="."/>
        <xsl:value-of select="$newline"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Our stylesheet generates this result:

7. The Joshua Tree
8. The Indestructible Beat of Soweto
string() Function Returns the string value of the argument.

Synopsis

string string(object)

Inputs

An object. The object is converted to a string, as described in the following subsection.

Output

A string. The input argument is converted to a string as follows:

  • If the argument is a node-set, the first node in the node-set is converted to a string. (The first node in the node-set is the one that occurs first in document order.)

  • If the argument is a number, it is converted to a string as follows:

    • The value NaN is converted to the string "NaN".

    • Positive zero is converted to the string "0".

    • Negative zero is converted to the string "0".

    • Positive infinity is converted to the string "Infinity".

    • Negative infinity is converted to the string "-Infinity".

    • An integer is converted to a string representing that integer, using no decimal point and no leading zeros. If the integer is negative, it will be preceded by a minus sign (-).

    • Any other number is converted to a string with a decimal point, at least one number before the decimal point, and at least one number after the decimal point. If the number is negative, it will be preceded by a minus sign (-). There will not be any leading zeros before the decimal point (with the possible exception of the one required digit before the decimal point). After the decimal point, there will be only as many digits as needed to distinguish this number from all other numeric values defined by the IEEE 754 standard, the same standard used by the Java float and double types.

  • If the argument is a boolean value, the value true is represented by the string "true" and the value false is represented by the string "false".

  • If the argument is any other type, it is converted to a string in a way that depends on that type. See the documentation for your XSLT processor to find out what other types are supported and how they are converted to strings.

Defined in

XPath section 4.2, String Functions.

Example

Here is the XML document we'll use to test the string() function:

<?xml version="1.0"?>
<test>
  <p>This is a test XML document used by several
  of our sample stylesheets.</p>
  <question>
    <text>When completed, the Eiffel Tower was the
    tallest building in the world.</text>
    <true>You're correct!  The Eiffel Tower was the
    world's tallest building until 1930.</true>
    <false>No, the Eiffel Tower was the world's
    tallest building for over 30 years.</false>
  </question>
  <question>
    <text>New York's Empire State Building knocked
    the Eiffel Tower from its pedestal.</text>
    <true>No, that's not correct.</true>
    <false>Correct!  New York's Chrysler Building,
    completed in 1930, became the world's tallest.</false>
  </question>
</test>

We'll test the string() function with a variety of arguments:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the string() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string(count(/test))=</xsl:text>
    <xsl:value-of select="string(count(/test))"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string(count(/test/question))=</xsl:text>
    <xsl:value-of select="string(count(/test/question))"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string('4')=</xsl:text>
    <xsl:value-of select="string('4')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string(true())=</xsl:text>
    <xsl:value-of select="string(true())"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string(false())=</xsl:text>
    <xsl:value-of select="string(false())"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string(count(/test/question) > 5)=</xsl:text>
    <xsl:value-of select="string(count(/test/question) > 5)"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Here are the string values of some <text> elements:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/test/question/text">
      <xsl:text>   </xsl:text>
      <xsl:value-of select="string(.)"/>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here are the results of our stylesheet:

Tests of the string() function:

   string(count(/test))=1
   string(count(/test/question))=2
   string('4')=4
   string(true())=true
   string(false())=false
   string(count(/test/question) > 5)=false

Here are the string values of some <text> elements:
   When completed, the Eiffel Tower was the tallest building in the world.
   New York's Empire State Building knocked the Eiffel Tower from its pedestal.
string-length() Function Returns the number of characters in the string passed in as the argument to this function. If no argument is specified, the context node is converted to a string and the length of that string is returned.

Synopsis

number string-length(string?)

Inputs

An optional string.

Output

The number of characters defined in the string.

Defined in

XPath section 4.2, String Functions.

Example

The following example demonstrates the results of invoking the string-length() function against various argument types. Here's the XML document we'll use for our example:

<?xml version="1.0"?>
<test>
  <p>This is a test XML document used by several
  of our sample stylesheets.</p>
  <question>
    <text>When completed, the Eiffel Tower was the
    tallest building in the world.</text>
    <true>You're correct!  The Eiffel Tower was the
    world's tallest building until 1930.</true>
    <false>No, the Eiffel Tower was the world's
    tallest building for over 30 years.</false>
  </question>
  <question>
    <text>New York's Empire State Building knocked
    the Eiffel Tower from its pedestal.</text>
    <true>No, that's not correct.</true>
    <false>Correct!  New York's Chrysler Building,
    completed in 1930, became the world's tallest.</false>
  </question>
</test>

We'll process this document with the following stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the string-length() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string-length(/test)=</xsl:text>
    <xsl:value-of select="string-length(/test)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string-length(/true)=</xsl:text>
    <xsl:value-of select="string-length(/true)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string-length(//true)=</xsl:text>
    <xsl:value-of select="string-length(//true)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   string-length(//test|//true|//text)=</xsl:text>
    <xsl:value-of select="string-length(//test|//true|//text)"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each select="/test/question">
      <xsl:text>   Question #</xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text> contains </xsl:text>
      <xsl:value-of select="string-length()"/>
      <xsl:text> characters.</xsl:text>
      <xsl:value-of select="$newline"/>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Here are the results of our stylesheet:

Tests of the string-length() function:

   string-length(/test)=522
   string-length(/true)=0
   string-length(//true)=78
   string-length(//test|//true|//text)=522

   Question #1 contains 239 characters.
   Question #2 contains 203 characters.

When we invoked the string-length() function without any arguments, the context node was converted to a string, then the length of that string was returned. The two <question> elements were handled this way inside the <xsl:for-each> element.

substring() Function Returns a portion of a given string. The second and third arguments determine what portion of the string is returned. The second argument specifies the position of the first character of the substring, and the optional third argument specifies how many characters should be returned.

Synopsis

string substring(stringnumbernumber?)

Inputs

The substring() function takes a string and one or two numbers as arguments. The string is the string from which the substring will be extracted. The second argument is used as the starting position of the returned substring, and the optional third argument specifies how many characters are returned.

Output

With two arguments (a string and a starting position), the substring() function returns all characters in the string, starting with the starting position. Be aware that the first character in an XPath string is at position 1, not 0.

With three arguments (a string, a starting position, and a length), the substring() function returns all characters in the string whose position is greater than or equal to the starting position and whose position is less than or equal to the starting position plus the length.

Normally, the arguments to the substring() function are integers, although they may be more complicated expressions. See the "Example" section that follows for some unusual cases.

Defined in

XPath section 4.2, String Functions.

Example

We'll use this XML document to demonstrate how the substring() function works:

<?xml version="1.0"?>
<test>
  <p>This is a test XML document used by several
    of our sample stylesheets.</p>
  <question>
    <text>When completed, the Eiffel Tower was the
    tallest building in the world.</text>
    <true>You're correct!  The Eiffel Tower was the
    world's tallest building until 1930.</true>

    <false>No, the Eiffel Tower was the world's
    tallest building for over 30 years.</false>
  </question>
  <question>
    <text>New York's Empire State Building knocked the
    Eiffel Tower from its pedestal.</text>
    <true>No, that's not correct.</true>
    <false>Correct!  New York's Chrysler Building,
    completed in 1930, became the world's tallest.</false>
  </question>
</test>

Here's the stylesheet we'll use:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the substring() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring('Now is the time', 4)="</xsl:text>
    <xsl:value-of select="substring('Now is the time', 4)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring('Now is the time', 4, 6)="</xsl:text>
    <xsl:value-of select="substring('Now is the time', 4, 6)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring('Now is the time', 4, -6)="</xsl:text>
    <xsl:value-of select="substring('Now is the time', 4, -6)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring('Now is the time', -3, 6)="</xsl:text>
    <xsl:value-of select="substring('Now is the time', -3, 6)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring('Now is the time', 54, 6)="</xsl:text>
    <xsl:value-of select="substring('Now is the time', 54, 6)"/>
    <xsl:text>"</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   count(//*)=</xsl:text>
    <xsl:value-of select="count(//*)"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring('Here is a really long string', </xsl:text>
    <:xsl:text>count(//*))="</xsl:text>
    <xsl:value-of
      select="substring('Here is a really long string', count(//*))"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring('Here is a less long string', </xsl:text>
    <xsl:text>count(//*) mod 7, 7)="</xsl:text>
    <xsl:value-of
      select="substring('Here is a less long string', count(//*) mod 7, 7)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring(/test/question[1]/text, 3, 7)="</xsl:text>
    <xsl:value-of select="substring(//*, 3, 7)"/>
    <xsl:text>"</xsl:text>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

When using the Saxon processor, here are the results:

Tests of the substring() function:

   substring('Now is the time', 4)=" is the time"
   substring('Now is the time', 4, 6)=" is th"
   substring('Now is the time', 4, -6)=""
   substring('Now is the time', -3, 6)="No"
   substring('Now is the time', 54, 6)=""

   count(//*)=10
   substring('Here is a really long string', count(//*))=" really long string"
   substring('Here is a less long string', count(//*) mod 7, 7)="re is a"
   substring(/test/question[1]/text, 3, 7)=" This i"

When running the same transformation with Xalan, we get a runtime error:

file:///D:/O'Reilly/XSLT/bookSamples/AppendixC/substringfunction.xsl; Line 26;
  Column 65;
Tests of the substring() function:

   substring('Now is the time', 4)=" is the time"
   substring('Now is the time', 4, 6)=" is th"
   substring('Now is the time', 4, -6)="
XSLT Error (javax.xml.transform.TransformerException): String index out of range
: -3

As of this writing, XT, Saxon, and Oracle's processors all gave the correct results; both Xalan and Microsoft's XSLT tools generated runtime exceptions. The lesson here is to use reasonable arguments to the substring() function so you won't be at the mercy of different implementations.

substring-after() Function Returns the substring of the first argument after the first occurrence of the second argument in the first argument. If the second argument does not occur in the first argument, the substring-after() function returns an empty string.

Synopsis

string substring-after(stringstring)

Inputs

Two strings. The first string is the string to be searched, and the second string is the string to be searched for in the first string.

Output

The portion of the first argument that occurs after the first occurrence of the second argument. If the second argument does not appear in the first argument, the function returns an empty string.

Defined in

XPath section 4.2, String Functions.

Example

This stylesheet uses the replace-substring named template. It passes three arguments to the replace-substring template: the original string, the substring to be searched for in the original string, and the substring to replace the target substring in the original string. The replace-substring template uses the contains(), substring-after(), and substring-before() functions extensively.

Here is our sample stylesheet. It replaces all occurrences of World with the string "Mundo":

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:variable name="test">
      <xsl:call-template name="replace-substring">
        <xsl:with-param name="original">Hello World!</xsl:with-param>
        <xsl:with-param name="substring">World</xsl:with-param>
        <xsl:with-param name="replacement">Mundo</xsl:with-param>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$test"/>
  </xsl:template>

  <xsl:template name="replace-substring">
    <xsl:param name="original"/>
    <xsl:param name="substring"/>
    <xsl:param name="replacement" select="''"/>
    <xsl:variable name="first">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:value-of select="substring-before($original, $substring)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$original"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:variable name="middle">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:value-of select="$replacement"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text></xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="last">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:choose>
            <xsl:when test="contains(substring-after($original, $substring),
                             $substring)">

              <xsl:call-template name="replace-substring">
                <xsl:with-param name="original">
                  <xsl:value-of
                    select="substring-after($original, $substring)"/>
                </xsl:with-param>
                <xsl:with-param name="substring">
                  <xsl:value-of select="$substring"/>
                </xsl:with-param>
                <xsl:with-param name="replacement">
                  <xsl:value-of select="$replacement"/>
                </xsl:with-param>
              </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="substring-after($original, $substring)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text></xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="concat($first, $middle, $last)"/>
  </xsl:template>

</xsl:stylesheet>

The stylesheet produces these results, regardless of the XML document used as input:

Hello Mundo!
substring-before() Function Returns the substring of the first argument before the first occurrence of the second argument in the first argument. If the second argument does not occur in the first argument, the substring-before() function returns an empty string.

Synopsis

string substring-before(stringstring)

Inputs

Two strings. The first string is the string to be searched, and the second string is the string to be searched for in the first string.

Output

The portion of the first argument that occurs before the first occurrence of the second argument. If the second argument does not appear in the first argument, the function returns an empty string.

Defined in

XPath section 4.2, String Functions.

Example

This stylesheet uses the replace-substring named template. It passes three arguments to the replace-substring template: the original string, the substring to be searched for in the original string, and the substring to replace the target substring in the original string. The replace-substring template uses the contains(), substring-after(), and substring-before() functions extensively.

Here is our sample stylesheet. It replaces all occurrences of World with the string "Mundo":

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:variable name="test">
      <xsl:call-template name="replace-substring">
        <xsl:with-param name="original">Hello World!</xsl:with-param>
        <xsl:with-param name="substring">World</xsl:with-param>
        <xsl:with-param name="replacement">Mundo</xsl:with-param>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$test"/>
  </xsl:template>

  <xsl:template name="replace-substring">
    <xsl:param name="original"/>
    <xsl:param name="substring"/>
    <xsl:param name="replacement" select="''"/>
    <xsl:variable name="first">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:value-of select="substring-before($original, $substring)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$original"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="middle">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:value-of select="$replacement"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text></xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="last">
      <xsl:choose>
        <xsl:when test="contains($original, $substring)">
          <xsl:choose>
            <xsl:when test="contains(substring-after($original, $substring),
                                      $substring)">
              <xsl:call-template name="replace-substring">
                <xsl:with-param name="original">
                  <xsl:value-of
                    select="substring-after($original, $substring)"/>
                </xsl:with-param>
                <xsl:with-param name="substring">
                  <xsl:value-of select="$substring"/>
                </xsl:with-param>
                <xsl:with-param name="replacement">
                  <xsl:value-of select="$replacement"/>
                </xsl:with-param>
              </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="substring-after($original, $substring)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text></xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="concat($first, $middle, $last)"/>
  </xsl:template>

</xsl:stylesheet>

The stylesheet produces these results, regardless of the XML document used as input:

Hello Mundo!
sum() Function Converts all nodes in the argument node-set to numbers, and then returns the sum of all of those numbers. If any node in the node-set can't be converted to numbers (passing them to the number() function returns NaN), the sum() function returns NaN.

Synopsis

number sum(node-set)

Inputs

A node-set. Any node in the node-set that is not a number is converted to a number as if it were passed to the number() function, then the numeric values of all of the nodes are summed.

Output

The sum of the numeric values of all of the nodes in the argument node-set. If any node in the argument node-set cannot be converted to a number, the sum() function returns NaN.

Defined in

XPath section 4.4, Number Functions.

Example

We'll demonstrate the sum() function against the following XML document:

<?xml version="1.0"?>
<report>
  <title>Miles Flown in 2001</title>
  <month sequence="01">
    <miles-flown>12379</miles-flown>
    <miles-earned>35215</miles-earned>
  </month>
  <month sequence="02">
    <miles-flown>32857</miles-flown>
    <miles-earned>92731</miles-earned>
  </month>
  <month sequence="03">
    <miles-flown>19920</miles-flown>
    <miles-earned>76725</miles-earned>
  </month>
  <month sequence="04">
    <miles-flown>18903</miles-flown>
    <miles-earned>31781</miles-earned>
  </month>
</report>

Here is a stylesheet that uses the sum() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the sum() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Total miles flown this year:  </xsl:text>
    <xsl:value-of
      select="format-number(sum(/report/month/miles-flown), '###,###')"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Total miles earned this year: </xsl:text>
    <xsl:value-of
      select="format-number(sum(/report/month/miles-earned), '###,###')"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

Processing the XML document with this stylesheet generates these results:

A test of the sum() function:

Total miles flown this year:  84,059

Total miles earned this year: 236,452
system-property() Function Returns the value of the system property named by the argument to the function.

Synopsis

object system-property(string)

Description

By definition, all XSLT processors must support three system properties:

xsl:version
A floating-point number representing the version of XSLT implemented by this XSLT processor. As of this writing, the only official version of XSLT supported by any XSLT processors is 1.0.

xsl:vendor
A string that identifies the vendor of this XSLT processor.

xsl:vendor-url
A string containing the URL identifying the vendor of the XSLT processor. This string is typically the home page of the vendor's web site.

Inputs

The XSLT 1.0 specification defines three properties: xsl:version, xsl:vendor, and xsl:vendor-url. These properties must be supported by all XSLT processors. Other properties may be supported by individual processors; check your processor's documentation for more information.

Output

The value of the queried property.

Defined in

XSLT section 12.4, Miscellaneous Additional Functions.

Example

Here is a stylesheet that queries different properties of the XSLT processor:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:text>xsl:version = "</xsl:text>
    <xsl:value-of select="system-property('xsl:version')"/>
    <xsl:text>"</xsl:text><xsl:value-of select="$newline"/>
    <xsl:text>xsl:vendor = "</xsl:text>
    <xsl:value-of select="system-property('xsl:vendor')"/>
    <xsl:text>"</xsl:text><xsl:value-of select="$newline"/>
    <xsl:text>xsl:vendor-url = "</xsl:text>

    <xsl:value-of select="system-property('xsl:vendor-url')"/>
    <xsl:text>"</xsl:text><xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

When the stylesheet is applied toward any XML document with the Xalan XSLT processor (invoked by the following command):

java org.apache.xalan.xslt.Process -in test1.xml -xsl systemproperties.xsl

The results are:

xsl:version = "1"
xsl:vendor = "Apache Software Foundation"
xsl:vendor-url = "http://xml.apache.org/xalan"

The following command invokes the results for Michael Kay's Saxon processor:

java com.icl.saxon.StyleSheet test1.xml systemproperties.xsl

Here are the results:

xsl:version = "1"
xsl:vendor = "SAXON 6.4.3 from Michael Kay"
xsl:vendor-url = "http://saxon.sourceforge.net"

We invoked Oracle's XML parser with:

java oracle.xml.parser.v2.oraxsl test1.xml systemproperties.xsl

Here are the results:

xsl:version = "1"
xsl:vendor = "Oracle Corporation."
xsl:vendor-url = "http://www.oracle.com"

We invoked James Clark's XT processor with:

java com.jclark.xsl.sax.Driver test1.xml systemproperties.xsl

Here are the results:

xsl:version = "1"
xsl:vendor = "James Clark"
xsl:vendor-url = "http://www.jclark.com/"

Finally, we invoked Microsoft's XSLT processor with:

Synopsis

msxsl test1.xml systemproperties.xsl

Here are the results:

xsl:version = "1"
xsl:vendor = "Microsoft"
xsl:vendor-url = "http://www.microsoft.com"
translate() Function Allows you to convert individual characters in a string from one value to another. In many languages, this function is powerful enough to convert characters from one case to another.

Synopsis

string translate(stringstringstring)

Inputs

Three strings. The first is the original, untranslated string, and the second and third strings define the characters to be converted.

Output

The original string, translated as follows:

  • If a character in the original string appears in the second argument string, it is replaced with the corresponding character in the third argument string. In other words, if the character J appears in the original string and J appears as the fourth character in the second argument string, the J is replaced with the fourth character from the third argument string. (Don't worry, we'll have some examples to clear this up in just a minute.)

  • If a character in the original string appears in the second argument string and there is no corresponding character in the third argument string (the second argument string is longer than the third), then that character is deleted. In other words, if the character J appears in the original string, and J appears as the fourth character in the second argument string, and the third argument string is three characters long, the J is deleted.

  • If a character in the second argument string appears more than once, the first occurrence determines the replacement character.

  • If the third argument string is longer than the second argument string, the extra characters are ignored.

Defined in

XPath section 4.2, String Functions.

Example

Here's a stylesheet with several examples of the translate() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Tests of the translate() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Convert a string to uppercase:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   translate('Doug', 'abcdefghijklmnopqrstuvwxyz', </xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>             'ABCDEFGHIJKLMNOPQRSTUVWXYZ')=</xsl:text>
    <xsl:value-of select="translate('Doug',
      'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Convert a string to lowercase:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   translate('Doug', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', </xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>             'abcdefghijklmnopqrstuvwxyz')=</xsl:text>
    <xsl:value-of
      select="translate('Doug', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                        'abcdefghijklmnopqrstuvwxyz')"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Remove parentheses, spaces, and dashes</xsl:text>
    <xsl:text> from a U.S. phone number:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   translate('(555) 555-1212', '() -', '')=</xsl:text>
    <xsl:value-of select="translate('(555) 555-1212', '() -', '')"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>Replace all but the last four digits of a </xsl:text>
    <xsl:text>credit card number with Xs:</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:variable name="credit" select="'4918 3829 9920 1810'"/>
    <xsl:text>   $credit='</xsl:text>
    <xsl:value-of select="$credit"/>
    <xsl:text>'</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   translate(substring($credit, 1, 15), </xsl:text>
    <xsl:text>'1234567890 ', 'XXXXXXXXXX-')</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:text>   substring($credit, 16)</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   The first part is </xsl:text>
    <xsl:value-of
      select="translate(substring($credit, 1, 15), '123457890 ',
        'XXXXXXXXX-')"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   The second part is </xsl:text>
    <xsl:value-of select="substring($credit, 16)"/>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   Here's how they look together: </xsl:text>
    <xsl:value-of
      select="translate(substring($credit, 1, 15), '123457890 ',
        'XXXXXXXXX-')"/>
    <xsl:value-of select="substring($credit, 16)"/>
  </xsl:template>

</xsl:stylesheet>

When we use this stylesheet with any XML document, here are the results:

Tests of the translate() function:

Convert a string to uppercase:
   translate('Doug', 'abcdefghijklmnopqrstuvwxyz',
             'ABCDEFGHIJKLMNOPQRSTUVWXYZ')=DOUG

Convert a string to lowercase:
   translate('Doug', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
             'abcdefghijklmnopqrstuvwxyz')=doug

Remove parentheses, spaces, and dashes from a U.S. phone number:
   translate('(555) 555-1212', '() -', '')=5555551212

Replace all but the last four digits of a credit card number with Xs:
   $credit='4918 3829 9920 1810'
   translate(substring($credit, 1, 15), '1234567890 ', 'XXXXXXXXXX-')
   substring($credit, 16)

   The first part is XXXX-XXXX-XXXX-
   The second part is 1810

   Here's how they look together: XXXX-XXXX-XXXX-1810
true() Function Always returns the boolean value true.

Synopsis

boolean true()

Inputs

None.

Output

The boolean value true.

Defined in

XPath section 4.3, Boolean Functions.

Example

Here's a brief example that uses the true() function:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the true() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:choose>
      <xsl:when test="true()">
        <xsl:text>   "true()"   returned true!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>   "true()"   returned false!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

When using this stylesheet against any XML document, it generates this less-than-exciting result:

A test of the true() function:

   "true()"   returned true!
unparsed-entity-uri() Function Returns the URI of the unparsed entity with the specified name. If there is no such entity, unparsed-entity-uri returns an empty string.

Synopsis

string unparsed-entity-uri(string)

Inputs

The name of the unparsed entity.

Output

The URI of the unparsed entity with the specified name.

Defined in

XSLT section 12.4, Miscellaneous Additional Functions.

Example

Unparsed entities are rarely used; they refer to non-XML data, as in the entity author-picture in this XML document:

<?xml version="1.0"?>
<!DOCTYPE book [
  <!ENTITY author-picture SYSTEM "dougtidwell.jpg" NDATA JPEG>
]>
<book>
  <prolog cover-image="author-picture"/>
  <body>
    <p>Pretend that lots of useful content appears here.</p>
  </body>
</book>

We'll use this stylesheet to process our unparsed entity:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>A test of the unparsed-entity-uri() function:</xsl:text>

    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:text>   The cover image is located at </xsl:text>
    <xsl:value-of select="unparsed-entity-uri(/book/prolog/@cover-image)"/>
    <xsl:text>.</xsl:text>
    <xsl:value-of select="$newline"/>
  </xsl:template>

</xsl:stylesheet>

When we transform the XML document with our stylesheet, the results look like this:

A test of the unparsed-entity-uri() function:

   The cover image is located at file:///D:/O'Reilly/dougtidwell.jpg.

The URI of the unparsed entity is based on the base URI of the XML document itself.

Previous | Home | Next
B.6. The XPath Context | Book Index | D. XSLT Guide
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


XSLT Guide (XSLT)

 XSLTSearch this book

Appendix D. XSLT Guide

Contents:

How Do I Put Quotes Inside an Attribute Value?
How Do I Convert All Attributes to Elements?
How Do I List All the Elements in an XML Document?
How Do I Implement an if Statement?
How Do I Implement an if-else Statement?
How Do I Implement a for Loop?
How Do I Implement a case Statement?
How Do I Group Elements in an XML Document?
How Do I Group Elements Pulled from Multiple XML Documents?
How Do I Combine XML Documents into a Single Master Document?
How Do I Resolve Cross-References?
How Do I Generate Some Text?
How Do I Control Angle Brackets and Quote Marks in My Output?

This appendix is a short, task-oriented guide to common stylesheet tasks. It is organized by task; find what you want to do, and this guide will show you how to do it.

D.1. How Do I Put Quotes Inside an Attribute Value?

First, remember that attribute values can be quoted with either single quotes or double quotes. If you need to define an attribute with the value "Doug's car," you can do what we just did: use double quotes to contain a value with single quotes inside it. If, however, you need to quote an attribute value that contains both single and double quotes, use the predefined entities &quot; for double quotes and &apos; for single quotes. Here's a sample document that contains some examples:

<?xml version="1.0"?>
<sampledoc>
  <head>
    <title>Attributes with Quotes</title>
  </head>
  <body>
    <p>This is an XML document that contains elements with attributes.
    The values of some of those attributes, interestingly enough, contain
    quotes. Look at the source of the document to see how we did this.</p>
    <tag1 author="Doug 'Gone' Tidwell"
      editor="Breanna & Meghan's Mom">
      Here's some text
    </tag1>
    <tag2 author='Doug "Geek of the Week" Tidwell'
      test="$x<7">
      Here's some more text
    </tag2>
    <tag3 author='Doug "The Slug" Tidwell' test="$x>9">
      A final example
    </tag3>
  </body>
</sampledoc>
Previous | Home | Next
C. XSLT and XPath Function Reference | Book Index | D.2. How Do I Convert All Attributes to Elements?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Convert All Attributes to Elements? (XSLT)

 XSLSearch this book

D.2. How Do I Convert All Attributes to Elements?

Here's a short stylesheet that does the job:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="*">
    <xsl:element name="{name()}">
      <xsl:for-each select="@*">
        <xsl:element name="{name()}">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:apply-templates select="*|text()"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

This example is about as short a stylesheet as you'll ever see. The XSLT processor uses the single <xsl:template> to process every element in the document. For each element, we output:

  • A new element, whose name is the name of the current element

  • For each attribute of the current element (selected with @*), a new element whose name is the name of the current attribute. The text of our newly created element is the text of the current attribute.

Once we've processed all the attributes, we process all of the child elements and text nodes beneath the current element. Processing them in this way means that the text and generated elements in the output document will be in the same sequence in the original document and the generated one.

As an example, we'll use our stylesheet to transform this XML document:

<?xml version="1.0"?>
<report>
  <title>Database Access Sample</title>
  <section>
    <title>Employees by Last Name</title>
    <dbaccess driver="COM.ibm.db2.jdbc.app.DB2Driver"
      database="jdbc:db2:sample" tablename="wstkadmin.employee" where="*"
      fieldnames='lastname as "Last Name",
      firstnme as "First Name", workdept as "Department"'
      order-by="lastname" group-by="lastname, firstnme, workdept"/>
  </section>
</report>

When we transform our document, here are the results:

<?xml version="1.0" encoding="UTF-8"?>
<report>
  <title>Database Access Sample</title>
  <section>
    <title>Employees by Last Name</title>
    <dbaccess>
      <driver>COM.ibm.db2.jdbc.app.DB2Driver</driver>
      <database>jdbc:db2:sample</database>
      <tablename>wstkadmin.employee</tablename>
      <where>*</where>
      <fieldnames>lastname as "Last Name", firstnme as "First Name",
        workdept as "Department"</fieldnames>
      <order-by>lastname</order-by>
      <group-by>lastname, firstnme, workdept</group-by>
    </dbaccess>
  </section>
</report>

In the output, all attributes of the original document are now elements. (We've added line breaks and indenting to make the output more readable.) With that exception, everything else in the document is unchanged.

Previous | Home | Next
D. XSLT Guide | Book Index | D.3. How Do I List All the Elements in an XML Document?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I List All the Elements in an XML Document? (XSLT)

 XSLSearch this book

D.3. How Do I List All the Elements in an XML Document?

As in our last example, this job is for generic XPath expressions. We'll use the grouping techniques described in Section 6.2, "Grouping Nodes" in Chapter 6, "Sorting and Grouping Elements", along with the name() function, to accomplish this. Our stylesheet sorts all element names alphabetically, and then groups them to list each unique element once, along with a count of how many times that element appears:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:key name="elements" match="*" use="name()"/>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Summary of Elements</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each
      select="//*[generate-id(.)=generate-id(key('elements',name())[1])]">
      <xsl:sort select="name()"/>
      <xsl:for-each select="key('elements', name())">
        <xsl:if test="position()=1">
          <xsl:text>Element </xsl:text>
          <xsl:value-of select="name()"/>
          <xsl:text> occurs </xsl:text>
          <xsl:value-of select="count(//*[name()=name(current())])"/>
          <xsl:text> times.</xsl:text>
          <xsl:value-of select="$newline"/>
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
    <xsl:text>There are </xsl:text>
    <xsl:value-of select="count(//*)"/>
    <xsl:text> elements in all.</xsl:text>
  </xsl:template>

</xsl:stylesheet>

When we run this stylesheet against the XML source file for Appendix C, "XSLT and XPath Function Reference", here are the results:

Summary of Elements

Element appendix occurs 1 times.
Element emphasis occurs 9 times.
Element filename occurs 11 times.
Element funcdef occurs 36 times.
Element funcprototype occurs 36 times.
Element funcsynopsis occurs 36 times.
Element function occurs 181 times.
Element graphic occurs 8 times.
Element itemizedlist occurs 9 times.
Element link occurs 1 times.
Element listitem occurs 52 times.
Element literal occurs 14 times.
Element para occurs 338 times.
Element paramdef occurs 45 times.
Element programlisting occurs 110 times.
Element quote occurs 4 times.
Element refentry occurs 36 times.
Element refname occurs 36 times.
Element refnamediv occurs 36 times.
Element refpurpose occurs 36 times.
Element refsect1 occurs 144 times.
Element refsynopsisdiv occurs 36 times.
Element literal occurs 184 times.
Element term occurs 13 times.
Element title occurs 146 times.
Element variablelist occurs 2 times.
Element varlistentry occurs 13 times.
Element xref occurs 14 times.

There are 1587 elements in all.

This stylesheet works against any valid XML document, regardless of the elements that document uses. For more information on the technique we used to group the element names (popularly known as the Muench method), see Section 6.2, "Grouping Nodes" in Chapter 6, "Sorting and Grouping Elements".

Previous | Home | Next
D.2. How Do I Convert All Attributes to Elements? | Book Index | D.4. How Do I Implement an if Statement?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Implement an if Statement? (XSLT)

 XSLSearch this book

D.4. How Do I Implement an if Statement?

Use the cleverly named <xsl:if> element. See the Reference A.14 entry in Appendix A, "XSLT Reference" for information and examples.

Previous | Home | Next
D.3. How Do I List All the Elements in an XML Document? | Book Index | D.5. How Do I Implement an if-else Statement?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Implement an if-else Statement? (XSLT)

 XSLSearch this book

D.5. How Do I Implement an if-else Statement?

For whatever reason, XSLT doesn't provide an <xsl:else> element. To create an if-else statement, you need an <xsl:choose> element with a single <xsl:when> element and a single <xsl:otherwise> element. If the test attribute of the <xsl:when> element is true, then the contents of the <xsl:when> are evaluated; if not, the contents of the <xsl:otherwise> element are.

See the Reference A.6 entry in Appendix A, "XSLT Reference" for a complete discussion of this element and some examples.

Previous | Home | Next
D.4. How Do I Implement an if Statement? | Book Index | D.6. How Do I Implement a for Loop?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Implement a for Loop? (XSLT)

 XSLSearch this book

D.6. How Do I Implement a for Loop?

The simple answer: you don't. What XSLT calls variables are variables in the mathematical sense of the term. An <xsl:variable> can be assigned an initial value, but it can't be changed. The upshot of this situation is that you can't implement a for loop as you typically do in most procedural programming languages. However, earlier in the book we did implement a stylesheet that emulates a for loop. See Section 4.7, "A Stylesheet That Emulates a for Loop" in Chapter 4, "Branching and Control Elements" for a complete discussion of this example.

It's much more likely that you need to iterate across a group of nodes in an XML document. If that's the case, see Section 4.2.3, "The <xsl:for-each> Element" in Chapter 4, "Branching and Control Elements" for more information.

Previous | Home | Next
D.5. How Do I Implement an if-else Statement? | Book Index | D.7. How Do I Implement a case Statement?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Implement a case Statement? (XSLT)

 XSLSearch this book

D.7. How Do I Implement a case Statement?

The XSLT <xsl:choose> element works very similarly to the case or switch statement you might know from procedural programming languages. See Section 4.2.2, "The <xsl:choose> Element" in Chapter 4, "Branching and Control Elements" for more information.

Previous | Home | Next
D.6. How Do I Implement a for Loop? | Book Index | D.8. How Do I Group Elements in an XML Document?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Group Elements in an XML Document? (XSLT)

 XSLSearch this book

D.8. How Do I Group Elements in an XML Document?

Unlike sorting, grouping is not directly supported in XSLT. Most axes and functions you'd like to use for grouping work only with the document order of the XML document, not with the sorted order you'd need for grouping. That being said, you can use some tricks to group elements in your output document. This subject is discussed in great detail in Section 6.2, "Grouping Nodes" in Chapter 6, "Sorting and Grouping Elements".

Previous | Home | Next
D.7. How Do I Implement a case Statement? | Book Index | D.9. How Do I Group Elements Pulled from Multiple XML Documents?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Group Elements Pulled from Multiple XML Documents? (XSLT)

 XSLSearch this book

D.9. How Do I Group Elements Pulled from Multiple XML Documents?

Unfortunately, grouping these elements is more difficult than grouping elements in the same document. It's not pretty or efficient, but you can do it. This subject is discussed in Section 7.4.3, "Grouping Across Multiple Documents" in Chapter 7, "Combining XML Documents".

Previous | Home | Next
D.8. How Do I Group Elements in an XML Document? | Book Index | D.10. How Do I Combine XML Documents into a Single Master Document?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Combine XML Documents into a Single Master Document? (XSLT)

 XSLSearch this book

D.10. How Do I Combine XML Documents into a Single Master Document?

This is actually pretty straightforward. See Chapter 7, "Combining XML Documents" for a discussion of the XSLT document() function in all its glory.

Previous | Home | Next
D.9. How Do I Group Elements Pulled from Multiple XML Documents? | Book Index | D.11. How Do I Resolve Cross-References?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Resolve Cross-References? (XSLT)

 XSLSearch this book

D.11. How Do I Resolve Cross-References?

The most common ways of resolving cross-references require use of the id() and key() functions, assuming there is some structure to your document to begin with. See Chapter 5, "Creating Links and Cross-References" for a discussion of the various techniques for generating cross-references.

Previous | Home | Next
D.10. How Do I Combine XML Documents into a Single Master Document? | Book Index | D.12. How Do I Generate Some Text?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Generate Some Text? (XSLT)

 XSLSearch this book

D.12. How Do I Generate Some Text?

You can generate text in your output in several different ways. The simplest way is to use the <xsl:text> element. This element generates text to the output stream automatically. The <xsl:text> element is often necessary because many elements in the XSLT vocabulary can't contain text.

A second way is to use the <xsl:value-of> element with various string functions. Say we're going to generate a string that is the concatenation of several items in the current context. Combining <xsl:value-of> with the concat() function allows us to do this. Here's an example:

<xsl:value-of select="concat('abc ', 'easy as ',123)"/>

This element is replaced with the text "abc easy as 123."

Previous | Home | Next
D.11. How Do I Resolve Cross-References? | Book Index | D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


How Do I Control Angle Brackets and Quote Marks in My Output? (XSLT)

 XSLSearch this book

D.13. How Do I Control Angle Brackets and Quote Marks in My Output?

The XML 1.0 Specification defines five entities you can use to generate special characters: the apostrophe, or single quote mark (defined as &apos;); the ampersand (defined as &amp;); the less-than sign, or left angle bracket (defined as &lt;); the double-quote mark (defined as &quot;); and the greater-than sign, or right angle bracket (defined as &gt;). You can use these entities to output one of these special symbols without an eager XML parser attempting to process them as metacharacters along the way.

If using the predefined entities doesn't do the trick, use the disable-output-escaping attribute of the <xsl:text> element. If you set this attribute to yes, the XSLT processor will not convert entities such as &lt; and &gt; to their associated characters.

Here's an <xsl:text> element with the normal setting and the results it generates:

<xsl:text>Hey, y&apos;all!</xsl:text>

Output:

Hey, y'all!

Here's the same listing with output escaping disabled:

<xsl:text disable-output-escaping="yes">Hey, y&apos;all!</xsl:text>

Output:

Hey, y&apos;all!

Be aware that the disable-output-escaping attribute may not be supported by all XSLT processors, and it may result in XML that is not well formed. For these reasons, the XSLT specification advises that disable-output-escaping "should be used only when there is no alternative."

Previous | Home | Next
D.12. How Do I Generate Some Text? | Book Index | Glossary
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Glossary (XSLT)

 XSLTSearch this book

Glossary

absolute location path
A location path that begins with /, followed by one or more location steps separated by /. All location paths that begin with / are evaluated from the root node, so they always return the same result, regardless of the context node. Compare with location path and relative location path.

ancestor
A node that appears above a given node. Ancestors include a node's parent, its parent's parent, its parent's parent's parent, etc. XPath also defines the ancestor axis, which includes a node's parent, its parent's parent, its parent's parent's parent, etc., but not the node itself. Contrast with parent.

attribute node
The XPath node type that represents an attribute from an XML document. Attributes are different from other nodes because an attribute node is not considered a child of the element node that contains it. Despite this fact, the element node is considered the parent of the attribute node.

attribute set
A named group of attributes. You can create an attribute set (with the <xsl:attribute-set> element), then reference that attribute set elsewhere. For more information, see the description of the <xsl:attribute-set> element in Appendix A, "XSLT Reference".

attribute value template

An expression that contains an XPath expression in curly braces ({}). The XPath expression is evaluated at runtime, and its value replaces the expression. For an example of an attribute value template, see the discussion of the <xsl:attribute> element in Appendix A, "XSLT Reference"; Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks" contains a complete discussion of attribute value templates.

axis
A relationship between the context node and other nodes in the document. XPath defines 13 different axes; see Section 3.2.6, "Axes" in Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks" for a complete discussion of them.

base URI
The URI associated with every node in the XPath source tree. In certain circumstances, the base URI is used to resolve references to other resources. If a given node is an element or processing-instruction node and that node occurs in an external entity, then the base URI for that node is the base URI of the external entity. If an element or processing-instruction node does not occur in an external entity, then its base URI is the base URI of the document in which it appears. The base URI of a document node is the base URI of the document itself, and the base URI of an attribute, comment, namespace, or text node is the base URI of that node's parent.

CDATA section
A section of an XML document in which all markup is ignored. A CDATA section begins with the characters <![CDATA[, and ends with the characters ]]>. If two right brackets appear in the content of a CDATA section, they must be escaped. Within a stylesheet, determining if a given text node was originally a CDATA section is not possible. It is possible to generate certain elements as CDATA sections with the cdata-section-elements attribute of the <xsl:output> element.

child
An immediate descendant of a given node. Contrast with descendant. child is also the name of one of the XPath axes. The children of a node include all comment, element, processing-instruction, and text nodes. Attribute nodes and namespace nodes are not considered children.

comment node
A node that represents a comment from the original XML document. This is one of the seven kinds of nodes defined by XPath.

context
A data structure that determines how XPath expressions are evaluated. The context consists of five items: the context node, a pair of nonzero positive integers (the context position and context size), a set of variable bindings, a function library, and the set of namespace declarations that are in scope.

context node

The node from which all XPath expressions are evaluated. The context node is analogous to the current directory at a command prompt; all commands you type at a command prompt are evaluated in terms of the current directory. Compare with current node.

context position
A nonzero positive integer that indicates the position of the current node. The context position is always less than or equal to the context size.

context size
The number of nodes in the current node list.

current node
The node currently being processed. The node is defined by the select attribute of the <xsl:apply-templates> or <xsl:for-each> elements. Except within a predicate expression, the current node and the context node are the same.

current node list
The list of nodes selected by the select attribute of the <xsl:apply-templates> or <xsl:for-each> element currently being processed. By default, the current node list is in document order, but it may be reordered with one or more <xsl:sort> elements.

descendant
A given node's children, its children's children, its children's children's children, etc. descendant is also the name of one of the axes defined by XPath. Contrast with child.

document element
The element in the XML source document that contains the entire XML document. The node that represents the document element is a child of the root node; the root node and the element node for the document element are not the same.

element node
An XPath node that represents an element from the XML source document.

encoding
A set of characters, referenced in the XML declaration to describe the characters used in a particular document. The range of values for encodings is defined in http://www.ietf.org/rfc/rfc2278.txt. The range of values supported by a given XML parser or XSLT processor varies.

expanded name
The complete name of an element or attribute, including the local name and a possibly null namespace URI.

extension element
An element in an XSLT stylesheet whose namespace prefix references an extension. The XSLT specification defines how extension elements are identified in the stylesheet, but does not specify how they are implemented. Extension elements are implemented with a piece of code that is referenced in the stylesheet; each XSLT processor defines how that code is invoked to handle the transformation of the extension element. See Chapter 8, "Extending XSLT" for an extensive discussion of extension elements and extension functions.

extension function
A function whose namespace prefix references an extension. The XSLT specification defines how extension functions are identified in the stylesheet, but does not specify how they are implemented. Extension functions are implemented with a piece of code that is referenced in the stylesheet; each XSLT processor defines how that code is invoked to handle the invocation of the extension function. See Chapter 8, "Extending XSLT" for an extensive discussion of extension elements and extension functions.

fallback processing
Processing designed to handle the absence of an extension element or an extension function gracefully. This processing is typically accomplished with the element-available() or the function-available() function. When either function returns false, a stylesheet can respond gracefully to the absence of the requested function. XSLT also defines the <xsl:fallback> element, which can be used when an extension element is not available.

ID
One of the basic datatypes defined by the XML specification. In an XML document, one attribute of an element can be declared to be of type ID; this means that the value of that attribute must be unique across all attributes of type ID for all elements in the document. No more than one attribute on a given element can be of type ID. Attributes of type ID are useful for generating cross-references with the id() function. See Chapter 5, "Creating Links and Cross-References" for an extensive discussion of the ID, IDREF, and IDREFS datatypes.

IDREF
One of the basic datatypes defined by the XML specification. In an XML document, an attribute declared to be of type IDREF must have a value that matches an ID attribute elsewhere in the document. Attributes of type IDREF are useful for generating cross-references with the id() function. See Chapter 5, "Creating Links and Cross-References" for an extensive discussion of the ID, IDREF, and IDREFS datatypes.

IDREFS
One of the basic datatypes defined by the XML specification. In an XML document, an IDREFS attribute must contain one or more whitespace-separated values, each of which matches an ID attribute elsewhere in the document. Attributes of type IDREFS are useful for generating cross-references with the id() function. See Chapter 5, "Creating Links and Cross-References" for an extensive discussion of the ID, IDREF, and IDREFS datatypes.

key
A key is similar to a database index. It has three components: a name, used to identify the key (specified with the name attribute of the <xsl:key> element); the nodes, which will be returned by the key (specified with the use attribute of the <xsl:key> element); and the values, used to search for things in the key (specified with the match attribute of the <xsl:key> element)

The key <key name="language-index" match="defn" use="@language"/>, for example, defines a new key named language-index. Given a value for the language attribute, the key returns all <defn> elements whose language attributes match the given value. See Section 5.2, "Generating Links with the key() Function" in Chapter 5, "Creating Links and Cross-References" for a complete discussion of keys and how they are used.

literal result element (LRE)

An element in an XSLT stylesheet that does not belong to the XSLT namespace and is not an extension element. Literal elements are simply copied to the result tree.

local name
The nonqualified portion of an element or attribute name. For example, in the element <xsl:template>, template is the local name.

location path
An XPath expression that selects a set of nodes relative to the context node. Compare with absolute location path and relative location path.

location step
Consists of three parts: an axis name, a node test, and zero or more predicate expressions. There are three location steps in the XPath expression preceding-sibling::region/product[@name="Sandpiper"]/text().

The first location step is preceding-sibling::region; it has an axis name of preceding-sibling and a node test of region. It selects all <region> elements that are preceding siblings of the context node. It does not have a predicate expression.

The second location step, product[@name="Sandpiper"], has an axis name of child, the default axis. Its node test is product and it has the predicate [@name="Sandpiper"]. It selects all <product> children of the previous location step that have an attribute named name with a value of Sandpiper.

The third location step, text(), has an axis name of child and a node test of text(). It selects all text node children of the previous location step. It does not have a predicate expression.

mode
An XSLT feature that allows an element to be processed multiple times, using a different template and producing a different result each time. See the discussion of the <xsl:apply-templates> element in Appendix A, "XSLT Reference" for a detailed example of modes.

namespace
A collection of element and attribute names that are associated with a URI.

namespace node
The XPath node type that corresponds to a namespace declaration in an XML document.

namespace prefix
Part of a qualified name used to associate an element or attribute with a namespace URI.

namespace URI
The string associated with a collection of element and attribute names. Although commonly referred to as a URI, a namespace URI can actually be any string.

NCName
An XML name, used for local names and namespace prefixes. An NCName must start with a letter or an underscore (_).

node test
An XPath expression that selects certain nodes. The expressions child::*, para, and @id select all child nodes, all <para> child nodes, and any attribute named id, respectively. Four node tests -- (text(), comment(), node(), and processing-instruction()) -- look like functions, even though they technically aren't. They allow you to select parts of an XML document not accessible through the various axes defined by XPath. See Section B.2, "XPath Node Tests" in Appendix B, "XPath Reference" for a complete discussion of these node tests.

output escaping
The process of changing reserved characters (such as <, >, and &) into their entity references (such as &lt;, &gt;, and &amp;).

parameter
An XSLT mechanism used to bind a name to a value, defined with the <xsl:param> element. The difference between a parameter and a variable is that the value specified in the definition of a parameter is a default value. When the template or stylesheet that contains the parameter is invoked, the default value can be overridden. Like variables, though, once the value of a parameter is set, it cannot be changed.

parent
A node that appears immediately above a given node. A parent is a node's first ancestor. XPath also defines the parent axis, which contains a node's parent. With the exception of the root node, all nodes have a parent. Contrast with ancestor.

predicate expression
An XPath expression that appears in square brackets ([]). Predicate expressions filter a node-set, selecting only nodes that match the expression in square brackets. See Section 3.2.7, "Predicates" in Chapter 3, "XPath: A Syntax for Describing Needles and Haystacks" for more information.

prefix
An abbreviation for namespace prefix.

processing instruction
Part of an XML document containing instructions for applications. Here is a sample processing instruction:

      <?xml-stylesheet href="docbook/html/docbook.xsl"
      type="text/xsl"?>

This processing instruction associates an XSLT stylesheet with an XML document. See Section 1.2.4.8, "Associating stylesheets with XML documents" in Chapter 1, "Getting Started" for a complete discussion of processing instructions.

processing-instruction node
The XPath node type that represents a processing instruction from an XML document.

qualified name
An element or attribute name that has been qualified with a namespace prefix. The format of a qualified name is prefix:local-name, where prefix and local-name are both NCNames. For example, <xsl:template> is a qualified name, while <template> is not. The names in an XSLT stylesheet (variable names, template names, mode names, etc.) must be qualified names.

QNAME
An abbreviation for qualified name.

relative location path
A location path that consists of one or more location steps separated by /. Compare with location path and absolute location path.

result-tree fragment
A fragment of the is result tree that can be associated with a variable. See Section B.5, "Datatypes" in Appendix B, "XPath Reference" for a more complete discussion of result-tree fragments.

root node
The XPath node that represents the root of an XML document. Note that the root node is not the same as the element node for the document element. The root node is specified with the XPath expression /. The children of the root node are the element node for the document element, as well as any comments or processing instructions that occur outside the document element.

sibling
Two nodes that have the same parent. XPath defines the preceding-sibling and following-sibling axes.

stylesheet
An XML document, written with the XSLT vocabulary, that specifies how an XML document should be transformed.

template
A rule in an XSLT stylesheet that defines how part of an XML document should be transformed. Templates are defined with the <xsl:template> element.

text node
A group of characters from an XML document. Text nodes are one of the seven types of nodes defined by XPath. The XPath specification states that as much text as possible must be combined into a single text node. In other words, a text node will never have a preceding or following sibling that is also a text node.

top-level element
An element whose parent is the <xsl:stylesheet> element.

unparsed entity
A resource in an XML document whose contents may or may not be text, and if text, may not be XML. Every unparsed entity has an associated XML notation. See the discussion of the unparsed-entity-uri() function in Appendix C, "XSLT and XPath Function Reference" for more details on unparsed entities.

valid document
An XML document that follows the basic rules of XML documents and additionally follows all rules of its associated document type definition or schema. See Section 1.2.2, "XML Document Rules" in Chapter 1, "Getting Started" for a complete discussion of the XML document rules; Section 1.2.2.8, "Document Type Definitions (DTDs) and XML Schemas", also in Chapter 1, "Getting Started", discusses document type definitions and schemas.

variable
An XSLT mechanism used to bind a name to a value, defined with the <xsl:variable> element. Variables are different from parameters because parameters can have default values. One significant difference between XSLT variables and variables in most other programming languages is that once an XSLT variable is initialized, its value cannot be changed. See Section 4.5, "Variables" in Chapter 4, "Branching and Control Elements" for a complete discussion of the <xsl:variable> element and how it is used.

well-formed document
An XML document that follows the basic rules of XML documents. See Section 1.2.2, "XML Document Rules" in Chapter 1, "Getting Started" for a complete discussion of those rules.

whitespace
One of four characters: space (&#x20), tab (&#x09), return (&#x0D), or linefeed (&#x0A).

XML declaration
Part of an XML document that defines the version of XML being used. Although the XML declaration looks like a processing instruction, it is not. For that reason, you cannot access the XML declaration from an XSLT stylesheet or an XPath expression.

Previous | Home | Next
D.13. How Do I Control Angle Brackets and Quote Marks in My Output? | Book Index | Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.


Index: XSLT

XSLTSearch this book

Index

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Symbols & Numbers

` ' single quotes (see single quotes)
[ ] square brackets: 3.2.7. Predicates
* asterisk: 3.2.5. Using Wildcards
@ at-sign: 3.2.4.1. Selecting attributes
3.2.6.2. Axis roll call
@* at-sign and asterisk: 3.2.5. Using Wildcards
^ caret: 4.6.1. Implementing a String Replace Function
:: double colon: 3.2.6. Axes
.. double period: 3.2.6.2. Axis roll call
// double slash operator: 3.2.5. Using Wildcards
# hash mark: 5.1.3. A Stylesheet That Uses the id( ) Function
. period: 3.2.6.2. Axis roll call
/ slash: 3.1.1. The Root Node
3.2.3. Relative and Absolute Expressions
| vertical bar: 4.6.1. Implementing a String Replace Function
& ampersand: 4.6.1. Implementing a String Replace Function
D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
?> closing processing instructions: 3.1.6. Processing Instruction Nodes
> greater than operator: 4.2.1. The <xsl:if> Element
>= greater-than-or-equal operator: 4.2.1. The <xsl:if> Element
<-- --> arrows: 3.1.5. Comment Nodes
< > angle brackets: 1.2.2.10. Tags versus elements
D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
< less than operator: 4.2.1. The <xsl:if> Element
<= less-than-or-equal operator: 4.2.1. The <xsl:if> Element
" " double quotes: D.1. How Do I Put Quotes Inside an Attribute Value?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: A

A4-sized pages, PDF files for: 9.1. About the Toot-O-Matic
9.3.7. PDF Files
generating: 9.5.6. Generating the PDF Files
icons for: 9.3.1. Menu Panel
abbreviated syntax: 3.2.6.1. Unabbreviated syntax
absolute expressions: 3.2.3. Relative and Absolute Expressions
accessDatabase( ) method: 8.3.2. Accessing a Database with an Extension Element
accessibility: 9.3.1. Menu Panel
addresses (see postal addresses)
alphabetizing (see sorting)
ampersand (&)
controlling: D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
escaping: 4.6.1. Implementing a String Replace Function
ancestor axis: 3.2.6.2. Axis roll call
7.4.1. The document( ) Function and Sorting
ancestor-or-self axis: 3.2.6.2. Axis roll call
angle brackets (< >)
controlling: D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
surrounding tags: 1.2.2.10. Tags versus elements
&apos; entity, quoting attribute values and: D.1. How Do I Put Quotes Inside an Attribute Value?
arrows (<-- -->) enclosing comments: 3.1.5. Comment Nodes
asterisk (*) wildcard: 3.2.5. Using Wildcards
at-sign (@)
abbreviating attribute axis: 3.2.6.2. Axis roll call
for selecting attributes: 3.2.4.1. Selecting attributes
at-sign and asterisk (@*) wildcard: 3.2.5. Using Wildcards
attribute axis: 3.2.6.2. Axis roll call
attribute nodes (XPath): 3.1.3. Attribute Nodes
B.1.3. Attribute Nodes
attribute value templates: 3.3. Attribute Value Templates
5.1.3. A Stylesheet That Uses the id( ) Function
attribute values, placing quotes inside: D.1. How Do I Put Quotes Inside an Attribute Value?
attributes
converting to elements: D.2. How Do I Convert All Attributes to Elements?
of <xsl:sort> element: 6.1.2.2. Attributes
rule governing quoting: 1.2.2.3. All attributes must be quoted
selecting: 3.2.4.1. Selecting attributes
axes: 3.2.6. Axes
XPath: B.3. XPath Axes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: B

base URI: 7.3. Invoking the document( ) Function
Bean Scripting Framework (BSF): 8.1.2.2. Example: Writing extensions in other languages
Berners-Lee, Tim: 1.2.1. XML's Heritage
boolean datatype: 3.4. XPath Datatypes
boolean( ) function: C. XSLT and XPath Function Reference
boolean operators (XPath): B.4.2. Boolean Operators
boolean values, converting datatypes to: 4.2.1.1. Converting to boolean values
branching elements: 4.2.1. The <xsl:if> Element
BSF (Bean Scripting Framework): 8.1.2.2. Example: Writing extensions in other languages
buildZipFile function: 9.5.8. Generating the Zip File
built-in template rules: 2.4.5. Built-in Template Rules

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: C

caret (^) for escaping characters: 4.6.1. Implementing a String Replace Function
Cascading Style Sheets (see CSS)
case-order attribute, <xsl:sort> element and: 6.1.2.2. Attributes
case statement (see <xsl:choose> element)
CDATA sections: 3. XPath: A Syntax for Describing Needles and Haystacks
3.1.4. Text Nodes
ceiling( ) function: C. XSLT and XPath Function Reference
character references: 3.1.2. Element Nodes
child axis: 3.2.6.2. Axis roll call
Clark, James: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
0.2.3. I Don't Care Which Standards-Compliant Tools You Use
class libraries, extension functions and: 8.1.2.2. Example: Writing extensions in other languages
comment( ) node test: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
B.2. XPath Node Tests
comment nodes (XPath): 3.1.5. Comment Nodes
B.1.5. Comment Nodes
comments, selecting: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
concat( ) function: 4.6.1.1. Procedural design
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
C. XSLT and XPath Function Reference
extensions and: 8.1.1.1. Example: Generating multiple output files
generating text and: D.12. How Do I Generate Some Text?
contains( ) function: 5.2.3.3. Solution #2: Use the XPath contains() function
C. XSLT and XPath Function Reference
context: 3.2.1. The Context
XPath: B.6. The XPath Context
context node: 3.2.1. The Context
B.6. The XPath Context
context position: 3.2.1. The Context
B.6. The XPath Context
context size: 3.2.1. The Context
B.6. The XPath Context
count(@*) &gt; 0 expression: 3.5.2. The Stylesheet
count( ) function: C. XSLT and XPath Function Reference
creating
cross-references: 5. Creating Links and Cross-References
links: 5. Creating Links and Cross-References
using id( ) function: 5.1. Generating Links with the id() Function
using key( ) function: 5.2. Generating Links with the key() Function
cross-references
creating: 5. Creating Links and Cross-References
in stylesheet: 5.1.3. A Stylesheet That Uses the id( ) Function
CSS (Cascading Style Sheets): 1.1. The Design of XSLT
illustrated in sample stylesheet: 3.5.2. The Stylesheet
current( ) function: C. XSLT and XPath Function Reference
current node: 2.3.4. Walking Through Our Example
7.4.2. Implementing Lookup Tables
select attribute and: 4.2.3.1. <xsl:for-each> example

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: D

d attribute, pie charts and: 8.1.2.1. Example: A library of trigonometric functions
data (see elements)
data model for XPath: 3.1. The XPath Data Model
data-type attribute, <xsl:sort> element and: 6.1.2.2. Attributes
databases, accessing with extension element: 8.3.2. Accessing a Database with an Extension Element
datatypes: 3.4. XPath Datatypes
B.5. Datatypes
converting to boolean values: 4.2.1.1. Converting to boolean values
supported by XML DTDs: 5.1.1. The ID, IDREF, and IDREFs Datatypes
<db:accessDatabase> element: 8.3.2. Accessing a Database with an Extension Element
<dbaccess> element: 8.3.2. Accessing a Database with an Extension Element
declarations: 1.2.2.7. XML declarations
descendant axis: 3.2.6.2. Axis roll call
descendant-or-self axis: 3.2.6.2. Axis roll call
design
of XML documents, for tutorials: 9.4. XML Document Design
procedural: 4.6.1.1. Procedural design
recursive: 4.6.1.2. Recursive design
7.2.1.1. Recursive design
Toot-O-Matic and: 9.2. Design Goals
developerWorks web site (IBM): 9. Case Study: The Toot-O-Matic
digital signatures, specification for: 1.2.4.11. XML digital signatures
disable-output-escaping attribute: D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
document element
root element: 1.2.2.1. An XML document must be contained in a single element
document( ) function: 7. Combining XML Documents
C. XSLT and XPath Function Reference
examples of: 7.2. The document( ) Function
invoking: 7.3. Invoking the document( ) Function
sorting with: 7.4.1. The document( ) Function and Sorting
Document Object Model (see DOM)
Document Type Definitions (see DTDs)
documents (see XML documents)
does_element_exist( ) function: 8.1.2.1. Example: A library of trigonometric functions
DOM (Document Object Model): 1.2.3. DOM and SAX
specification for: 1.2.4.5. Document Object Model (DOM) Level 1
tree, vs. XPath conceptual tree: 3.1. The XPath Data Model
DOMit servlet: 1.2.3.1.1. A sample DOM tree
double colon (::) used with axes: 3.2.6. Axes
double period (..) indicating parent axis: 3.2.6.2. Axis roll call
double quotes (" "): D.1. How Do I Put Quotes Inside an Attribute Value?
double slash (//) operator: 3.2.5. Using Wildcards
DTDs (Document Type Definitions): 1.2.2.8. Document Type Definitions (DTDs) and XML Schemas
advantages of schemas over: 1.2.2.8. Document Type Definitions (DTDs) and XML Schemas
for XML, datatypes supported by: 5.1.1. The ID, IDREF, and IDREFs Datatypes

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: E

element-available( ) function: 8.1.1.2. Example: Using extension functions from multiple processors
C. XSLT and XPath Function Reference
element nodes (XPath): 3.1.2. Element Nodes
B.1.2. Element Nodes
elements
complete reference to: A. XSLT Reference
converting attributes to: D.2. How Do I Convert All Attributes to Elements?
grouping: 6. Sorting and Grouping Elements
6.2. Grouping Nodes
linking: 5. Creating Links and Cross-References
listing, how to guide for: D.3. How Do I List All the Elements in an XML Document?
rules governing: 1.2.2.1. An XML document must be contained in a single element
sorting: 6. Sorting and Grouping Elements
using <xsl:sort> element: 6.1. Sorting Data with <xsl:sort>
vs. tags: 1.2.2.10. Tags versus elements
email panel (tutorials): 9.3.5. Email Panel
entity references: 3. XPath: A Syntax for Describing Needles and Haystacks
error checking/handling, functions for: 8.1.2.1. Example: A library of trigonometric functions
examples
databases, accessing with extension elements: 8.3.2. Accessing a Database with an Extension Element
document( ) function, using: 7.2. The document() Function
extensions, writing in various scripting languages: 8.1.2.2. Example: Writing extensions in other languages
JPEG files, generating: 8.3.1. Generating JPEG Files from XML Content
multiple output files, generating: 8.1.1.1. Example: Generating multiple output files
postal addresses
grouping: 6.2.1. Our First Attempt
sorting: 6.1.1. Our First Example
6.1.3. Another Example
"Hello World" sample document, converting into various formats: 2. The Obligatory Hello World Example
recursion, using: 4.6. Using Recursion to Do Most Anything
stylesheet emulating for loop: 4.7. A Stylesheet That Emulates a for Loop
stylesheet-generating stylesheet with for loop emulation: 4.8. A Stylesheet That Generates a Stylesheet That Emulates a for Loop
trigonometric functions, writing extension for: 8.1.2.1. Example: A library of trigonometric functions
exceptions: 1.2.2. XML Document Rules
expression operators: B.4.3. Expression Operators
expressions, XPath: 3. XPath: A Syntax for Describing Needles and Haystacks
EXSLT project: 8.3.2. Accessing a Database with an Extension Element
Extensible Markup Language (see XML)
Extensible Stylesheet Language for Transformations (see XSLT)
extension elements: 8.1. Extension Elements, Extension Functions, and Fallback Processing
8.1. Extension Elements, Extension Functions, and Fallback Processing
(see also extensions for XSLT)
databases, accessing with: 8.3.2. Accessing a Database with an Extension Element
defining for processors: 8.1.1.2. Example: Using extension functions from multiple processors
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element
multiple XSLT processors and: 8.1.1.2. Example: Using extension functions from multiple processors
extension functions: 5.2.3.5. Solution #4: Use an extension function
8.1. Extension Elements, Extension Functions, and Fallback Processing
8.1.2. Extension Functions
8.1.2. Extension Functions
(see also extensions for XSLT)
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element
JPEG files and: 8.3.1. Generating JPEG Files from XML Content
writing in various scripting languages: 8.1.2.2. Example: Writing extensions in other languages
extensions for XSLT: 8. Extending XSLT
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: F

fallback processing: 8.1.3. Fallback Processing
false( ) function: C. XSLT and XPath Function Reference
feedback panel (tutorials): 9.3.4. Feedback Panel
filename attribute, document( ) function and: 7.2. The document() Function
floor( ) function: C. XSLT and XPath Function Reference
following axis: 3.2.6.2. Axis roll call
following-sibling axis: 3.2.6.2. Axis roll call
FOP tool: 9.3.7. PDF Files
9.5.6. Generating the PDF Files
for loop, stylesheet emulating (sample program): 4.7. A Stylesheet That Emulates a for Loop
format-number( ) function: 7.2.1.3. Using format-number() to control output
C. XSLT and XPath Function Reference
Formatting Objects to PDF tool (see FOP tool)
function-available( ) function: C. XSLT and XPath Function Reference
functional programming languages: 1.1. The Design of XSLT
functions
in predicates: 3.2.7.2. Functions in predicates
reference guide listing: C. XSLT and XPath Function Reference
set of (context): 3.2.1. The Context
B.6. The XPath Context
functions attribute: 8.1.2.2. Example: Writing extensions in other languages

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: G

generate-id( ) function: 5.3. Generating Links in Unstructured Documents
C. XSLT and XPath Function Reference
grouping elements/nodes and: 6.2. Grouping Nodes
generating
JPEG files: 8.3.1. Generating JPEG Files from XML Content
9.5.7. Generating the JPEG Files
links (see links, creating)
main menu panel: 9.5.3. Generating the Main Menu Panel
output files: 8.1.1.1. Example: Generating multiple output files
panels: 9.5.5. Generating the Individual Panels
PDF files: 9.5.6. Generating the PDF Files
section indexes: 9.5.4. Generating the Section Indexes
zip files: 9.5.8. Generating the Zip File
global parameters: 4.4.3. Global Parameters
global variables, initializing: 9.5.2. Initializing Global Variables
glossary: Gloss
Goldfarb, Charles: 1.2.1. XML's Heritage
greater than operator (>), using &gt; entity instead of: 4.2.1. The <xsl:if> Element
greater-than-or-equal operator (>=): 4.2.1. The <xsl:if> Element
greeting elements: 2.3.4. Walking Through Our Example
<xsl:template> for: 2.4.4. The <xsl:template> for <greeting> Elements
grouping: 6. Sorting and Grouping Elements
(see also sorting)
elements/nodes: 6. Sorting and Grouping Elements
using <xsl:key> element: 6.2.4. The <xsl:key> Approach
using <xsl:variable> element: 6.2.3. Grouping with <xsl:variable>
XML documents: 7.4.3. Grouping Across Multiple Documents
&gt; entity: 4.2.1. The <xsl:if> Element

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: H

hash mark (#): 5.1.3. A Stylesheet That Uses the id( ) Function
Hello World sample document: 2. The Obligatory Hello World Example
converting into
VRML file: 2.5.4. The Hello World VRML File
Java program: 2.5.3. The Hello World Java Program
PDF file: 2.5.2. The Hello World PDF File
SVG file: 2.5.1. The Hello World SVG File
HTML files, tutorials and: 9.1. About the Toot-O-Matic
9.3. Tutorial Layout

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: I

IBM
Bean Scripting Framework and: 8.1.2.2. Example: Writing extensions in other languages
developerWorks web site and: 9. Case Study: The Toot-O-Matic
ID datatype: 5.1.1. The ID, IDREF, and IDREFs Datatypes
limitations of: 5.1.4. Limitations of IDs
id( ) function: C. XSLT and XPath Function Reference
creating links using: 5.1. Generating Links with the id( ) Function
limitations of: 5.1.4. Limitations of IDs
IDREF datatype: 5.1.1. The ID, IDREF, and IDREFs Datatypes
IDREFS datatype: 5.1.1. The ID, IDREF, and IDREFs Datatypes
5.2.3. Stylesheets That Use the key( ) Function
key( ) function and: 5.2.3.1. The key() function and the IDREFS datatype
replacing: 5.2.3.2. Solution #1: Replace the IDREFS datatype
using recursion to process: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
if-else statement: D.5. How Do I Implement an if-else Statement?
if statement (see <xsl:if> element)
if-then-else statement (see <xsl:choose> element)
index variable: 4.7.1. Template Design
initializing
global variables: 9.5.2. Initializing Global Variables
variables: 7.2.1.2. Generating output to initialize a variable
iteration: 1.1. The Design of XSLT

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: J

Jacl code: 8. Extending XSLT
Java programs
converting "Hello World" document into: 2.5.3. The Hello World Java Program
global parameters, setting in: 4.4.3.6. Setting global parameters in a Java program
Java Runtime Environment (JRE): 1.3. Installing Xalan
JavaScript code: 8. Extending XSLT
caution when generating: 8.1.2.1. Example: A library of trigonometric functions
JPEG files, generating: 8.3.1. Generating JPEG Files from XML Content
9.5.7. Generating the JPEG Files
JRE (Java Runtine Environment): 1.3. Installing Xalan
Jython code: 8. Extending XSLT
8.1.2.2. Example: Writing extensions in other languages

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: K

Kay, Michael: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
8.2. Extending the Saxon Processor
Kernighan, Brian: 2.2. Transforming Hello World
key( ) function: C. XSLT and XPath Function Reference
advantages of: 5.2.4. Advantages of the key( ) Function
creating links using: 5.2. Generating Links with the key( ) Function
grouping elements/nodes and: 6.2. Grouping Nodes
IDREFS datatype and: 5.2.3.1. The key( ) function and the IDREFS datatype
limitations of IDs and: 5.1.4. Limitations of IDs

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: L

lang attribute, <xsl:sort> element and: 6.1.2.2. Attributes
lang( ) function: C. XSLT and XPath Function Reference
languages
programming, functional: 1.1. The Design of XSLT
scripting, writing extensions and: 8.1.2.2. Example: Writing extensions in other languages
Spanish, sorting and: 8.2. Extending the Saxon Processor
last( ) function: C. XSLT and XPath Function Reference
legends
for documents, sample of: 3.5.2. The Stylesheet
for pie charts, drawing: 8.1.2.1. Example: A library of trigonometric functions
less than operator (<): 4.2.1. The <xsl:if> Element
less-than-or-equal operator (<=): 4.2.1. The <xsl:if> Element
letter-sized pages, PDF files for: 9.1. About the Toot-O-Matic
9.3.7. PDF Files
generating: 9.5.6. Generating the PDF Files
icons for: 9.3.1. Menu Panel
links, creating: 5. Creating Links and Cross-References
in unstructured documents: 5.3. Generating Links in Unstructured Documents
using id( ) function: 5.1. Generating Links with the id() Function
using key( ) function: 5.2. Generating Links with the key() Function
literal result elements (LREs): 3.3. Attribute Value Templates
literal values, single quotes and: 4.5. Variables
local-name( ) function: C. XSLT and XPath Function Reference
location paths: 3.2. Location Paths
logic: 4. Branching and Control Elements
for controlling flow of stylesheets: 4.5. Variables
in sample stylesheet: 4.7.3. The Complete Example
lookup tables: 7.4.2. Implementing Lookup Tables
loops: 4.7. A Stylesheet That Emulates a for Loop
<xsl:for-each> element vs.: 4.2.3. The <xsl:for-each> Element
LREs (literal result elements): 3.3. Attribute Value Templates
&lt; entity: 4.2.1. The <xsl:if> Element

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: M

mailing lists
XML-DEV: 1.2.3. DOM and SAX
XSL-LIST: 2.6. Summary
main menu panel, generating: 9.5.3. Generating the Main Menu Panel
main( ) method (C++/Java), mode attribute and: 9.5.1. Stylesheets and Modes
master document: 7.2. The document( ) Function
mathematics
performed with recursion: 7.2.1. An Aside: Doing Math with Recursion
trigonometric functions for: 8.1.2.1. Example: A library of trigonometric functions
XPath operators for: B.4.1. Mathematical Operators
menu panel (tutorials): 9.3.1. Menu Panel
mode attribute: 4.3.2. Templates à la Mode
9.5.1. Stylesheets and Modes
mode=generate-pdf attribute: 9.5.6. Generating the PDF Files
$mouse-effects variable: 9.5.2. Initializing Global Variables
mouse_over( ) function: 8.1.2.1. Example: A library of trigonometric functions
MSXML parser: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
Muench method: 6.2.4. The <xsl:key> Approach
Muench, Steve: 6.2.4. The <xsl:key> Approach

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: N

name( ) function: 3.1.6. Processing Instruction Nodes
C. XSLT and XPath Function Reference
named templates, invoking: 4.3. Invoking Templates by Name
namespace axis: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
3.2.6.2. Axis roll call
namespace declarations, set of (context): 3.2.1. The Context
B.6. The XPath Context
namespace nodes (XPath): 3.1.7. Namespace Nodes
B.1.7. Namespace Nodes
selecting: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
namespace prefixes: 1.2.2.11. Namespaces
namespace nodes and: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
namespace URI: 8.1.1.1. Example: Generating multiple output files
namespace-uri( ) function: C. XSLT and XPath Function Reference
namespaces: 1.2.2.11. Namespaces
defining alias for: 4.8.3. Complications
specification for: 1.2.4.7. Namespaces in XML
naming conventions, importance of: 9.5.2. Initializing Global Variables
Next link: 8.1.1.1. Example: Generating multiple output files
node( ) node test: 3.2.5. Using Wildcards
B.2. XPath Node Tests
node-set datatype: 3.4. XPath Datatypes
converting to boolean values: 4.2.1.1. Converting to boolean values
node tests for XPath: 3.2.4.2. Selecting the text of an element
B.2. XPath Node Tests
nodes: 3.1. The XPath Data Model
grouping: 6.2. Grouping Nodes
illustrated in sample stylesheet: 3.5. The XPath View of an XML Document
types and (XPath): 3.1.1. The Root Node
B.1. XPath Node Types
normalize-space( ) function: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
C. XSLT and XPath Function Reference
normalizeSpace( ) function: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
not( ) function: C. XSLT and XPath Function Reference
number datatype: 3.4. XPath Datatypes
converting to boolean values: 4.2.1.1. Converting to boolean values
number( ) function: C. XSLT and XPath Function Reference
numbers, in predicates: 3.2.7.1. Numbers in predicates

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: O

operators (XPath): B.4. XPath Operators
order attribute, <xsl:sort> element and: 6.1.2.2. Attributes
output
generating/controlling: 7.2.1.2. Generating output to initialize a variable
multiple files for, generating: 8.1.1.1. Example: Generating multiple output files
output escaping: 4.6.1. Implementing a String Replace Function
output stream, generating text to: D.12. How Do I Generate Some Text?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: P

page format (see A4-sized pages)
9.3.7. PDF Files
panels (tutorial pages): 9.1. About the Toot-O-Matic
generating: 9.5.5. Generating the Individual Panels
parameters
global: 4.4.3. Global Parameters
passing to template: 4.4. Parameters
single quotes and: 4.4.1. Defining a Parameter in a Template
parent axis: 3.2.6.2. Axis roll call
<path> element (SVG): 8.1.2.1. Example: A library of trigonometric functions
PDF files
converting "Hello World" document into: 2.5.2. The Hello World PDF File
page formats and: 9.1. About the Toot-O-Matic
9.3.7. PDF Files
generating: 9.5.6. Generating the PDF Files
icons displaying: 9.3.1. Menu Panel
period (.) indicating self axis: 3.2.6.2. Axis roll call
pie charts
generating: 8.1.2.1. Example: A library of trigonometric functions
legends for: 8.1.2.1. Example: A library of trigonometric functions
polymorphism: 4.3. Invoking Templates by Name
portability, extension functions and: 5.2.3.5. Solution #4: Use an extension function
position( ) function: C. XSLT and XPath Function Reference
postal addresses
grouping: 6.2.1. Our First Attempt
sorting: 6.1.1. Our First Example
6.1.3. Another Example
preceding axis: 3.2.6.2. Axis roll call
preceding-sibling axis: 3.2.6.2. Axis roll call
predicates: 3.2.7. Predicates
prefix attribute: 8.1.2.2. Example: Writing extensions in other languages
Previous link: 8.1.1.1. Example: Generating multiple output files
procedural design: 4.6.1.1. Procedural design
processing-instruction( ) node test: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
B.2. XPath Node Tests
processing instruction nodes (XPath): 3.1.6. Processing Instruction Nodes
B.1.6. Processing-Instruction Nodes
processing instructions
?> closing: 3.1.6. Processing Instruction Nodes
selecting: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
processors (see XSLT processors)
programming languages, functional: 1.1. The Design of XSLT
purchase orders
displaying total for: 7.2.1.3. Using format-number( ) to control output
illustrating document( ) function: 7. Combining XML Documents

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Q

&quot; entity, quoting attribute values and: D.1. How Do I Put Quotes Inside an Attribute Value?
quotes (see double quotes)
D.1. How Do I Put Quotes Inside an Attribute Value?

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: R

recursion: 1.1. The Design of XSLT
examples of: 4.6. Using Recursion to Do Most Anything
performing mathematics with: 7.2.1. An Aside: Doing Math with Recursion
recursive design: 4.6.1.2. Recursive design
7.2.1.1. Recursive design
redirect namespace prefix: 8.1.1.1. Example: Generating multiple output files
relative expressions: 3.2.3. Relative and Absolute Expressions
resolveIDREFS template: 5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
result-tree fragment: B.5. Datatypes
Ritchie, Dennis: 2.2. Transforming Hello World
root node (XPath): 3.1.1. The Root Node
B.1.1. The Root Node
round( ) function: C. XSLT and XPath Function Reference
rules
for built-in templates: 2.4.5. Built-in Template Rules
for XML documents: 1.2.2. XML Document Rules

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: S

SAX (Simple API for XML): 1.2.3. DOM and SAX
specification for: 1.2.4.4. The Simple API for XML (SAX)
Saxon processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
extension elements, defining for: 8.1.1.2. Example: Using extension functions from multiple processors
extension functions and: 8.2. Extending the Saxon Processor
global parameters and: 4.4.3.4. Saxon
sorting and: 8.2. Extending the Saxon Processor
Scalable Vector Graphics (see entries at SVG)
schemas: 1.2.2.8. Document Type Definitions (DTDs) and XML Schemas
specification for: 1.2.4.3. XML Schemas
scripting languages, writing extensions and: 8.1.2.2. Example: Writing extensions in other languages
section indexes, generating: 9.5.4. Generating the Section Indexes
section menu (tutorials): 9.3.3. Section Menu
select attribute
<xsl:for-each> element and: 4.2.3.1. <xsl:for-each> example
<xsl:param> element and: 4.4.1. Defining a Parameter in a Template
<xsl:sort> element and: 6.1.2.2. Attributes
selecting
attributes: 3.2.4.1. Selecting attributes
comments: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
namespace nodes: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
processing instructions: 3.2.4.3. Selecting comments, processing instructions, and namespace nodes
text of element: 3.2.4.2. Selecting the text of an element
self axis: 3.2.6.2. Axis roll call
SGML (Standard Generalized Markup Language): 1.2.1. XML's Heritage
Simple API for XML (see SAX)
single quotes (` ')
controlling: D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
literal values and: 4.5. Variables
parameters and: 4.4.1. Defining a Parameter in a Template
placing inside attribute value: D.1. How Do I Put Quotes Inside an Attribute Value?
replacing one single quote with two: 4.6.1. Implementing a String Replace Function
slash (/): 3.1.1. The Root Node
beginning absolute XPath expressions: 3.2.3. Relative and Absolute Expressions
sorting: 6. Sorting and Grouping Elements
(see also grouping)
elements: 6. Sorting and Grouping Elements
postal addresses: 6.1.1. Our First Example
6.1.3. Another Example
Saxon processor and: 8.2. Extending the Saxon Processor
with document( ) function: 7.4.1. The document() Function and Sorting
XML documents: 7.4.1. The document( ) Function and Sorting
Spanish language, sorting and: 8.2. Extending the Saxon Processor
specifications: 1.2.4. XML Standards
XPath, reference guide to: B. XPath Reference
XSLT
extensions and: 8. Extending XSLT
reference guide to: A. XSLT Reference
SQL statements: 4.6.1. Implementing a String Replace Function
square brackets [ ] enclosing predicates: 3.2.7. Predicates
Standard Generalized Markup Language (SGML): 1.2.1. XML's Heritage
standards (see specifications)
starts-with( ) function: C. XSLT and XPath Function Reference
<state> element: 7.4.1. The document( ) Function and Sorting
string datatype: 3.4. XPath Datatypes
converting to boolean values: 4.2.1.1. Converting to boolean values
string( ) function: C. XSLT and XPath Function Reference
string-length( ) function: C. XSLT and XPath Function Reference
string replace function: 4.6.1. Implementing a String Replace Function
style attribute, pie charts and: 8.1.2.1. Example: A library of trigonometric functions
stylesheets: 2. The Obligatory Hello World Example
2.1. Goals of This Chapter
associating with XML documents, specification for: 1.2.4.8. Associating stylesheets with XML documents
emulating for loop (example): 4.7. A Stylesheet That Emulates a for Loop
example of: 2.2.2. A Sample Stylesheet
alternate versions for: 2.4.7. Other Approaches
how to guide for: D. XSLT Guide
illustrating nodes: 3.5. The XPath View of an XML Document
processing of: 2.3. How a Stylesheet Is Processed
structure of: 2.4. Stylesheet Structure
stylesheet-generating with for loop emulation (example): 4.8. A Stylesheet That Generates a Stylesheet That Emulates a for Loop
Toot-O-Matic case study and: 9.2.3. See Just How Much We Could Accomplish Through Stylesheets
tutorials and, transformations for: 9.5. XSLT Source Code
using id( ) function: 5.1.3. A Stylesheet That Uses the id() Function
using key( ) function: 5.2.3. Stylesheets That Use the key() Function
substring-after( ) function: 4.6.1.1. Procedural design
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
C. XSLT and XPath Function Reference
substring-before( ) function: 4.6.1.1. Procedural design
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
C. XSLT and XPath Function Reference
substring( ) function: C. XSLT and XPath Function Reference
sum( ) function: 7.2.1. An Aside: Doing Math with Recursion
C. XSLT and XPath Function Reference
suppress_errors( ) function: 8.1.2.1. Example: A library of trigonometric functions
SVG files
converting "Hello World" document into: 2.5.1. The Hello World SVG File
trigonometric functions and: 8.1.2.1. Example: A library of trigonometric functions
SVG (Scalable Vector Graphics), specification for: 1.2.4.9. Scalable Vector Graphics (SVG)
switch statement (see <xsl:choose> element)
syntax
abbreviated vs. unabbreviated: 3.2.6.1. Unabbreviated syntax
for <xsl:sort> element: 6.1.2.1. What's the deal with that syntax?
system-property( ) function: 8.1.1.2. Example: Using extension functions from multiple processors
C. XSLT and XPath Function Reference

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: T

tags
rules governing: 1.2.2.4. XML tags are case-sensitive
vs. elements: 1.2.2.10. Tags versus elements
templates
built-in, rules for: 2.4.5. Built-in Template Rules
invoking by name: 4.3. Invoking Templates by Name
passing parameters to: 4.4. Parameters
test attribute
<xsl:if> element and: 4.2.1.1. Converting to boolean values
<xsl:when> element and: 4.2.2. The <xsl:choose> Element
text
generating to output stream: D.12. How Do I Generate Some Text?
of element, selecting: 3.2.4.2. Selecting the text of an element
text( ) node test: 3.1.5. Comment Nodes
B.2. XPath Node Tests
using to select text of element: 3.2.4.2. Selecting the text of an element
text nodes (XPath): 3.1.4. Text Nodes
B.1.4. Text Nodes
titles, JPEG files and: 8.3.1. Generating JPEG Files from XML Content
9.5.7. Generating the JPEG Files
tokenize function: 5.2.3.5. Solution #4: Use an extension function
tooltips: 9.3.1. Menu Panel
Toot-O-Matic: 9. Case Study: The Toot-O-Matic
design goals for: 9.2. Design Goals
stylesheet transformations and: 9.5. XSLT Source Code
tutorial layout and: 9.3. Tutorial Layout
XML document design and: 9.4. XML Document Design
top-level elements: 2.4.6. Top-Level Elements
<tr> element: 8.3.2. Accessing a Database with an Extension Element
transform attribute, pie charts and: 8.1.2.1. Example: A library of trigonometric functions
Transformation API for XML (TrAX), global parameters and: 4.4.3.6. Setting global parameters in a Java program
transforming XML documents: 2.2.3. Transforming the XML Document
translate( ) function: C. XSLT and XPath Function Reference
TrAX (Transformation API for XML), global parameters and: 4.4.3.6. Setting global parameters in a Java program
trigonometric functions, extension for providing: 8.1.2.1. Example: A library of trigonometric functions
true( ) function: C. XSLT and XPath Function Reference
tutorials: 9. Case Study: The Toot-O-Matic
9. Case Study: The Toot-O-Matic
parts of: 9.3. Tutorial Layout
stylesheet transformations for: 9.5. XSLT Source Code

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: U

unabbreviated syntax: 3.2.6.1. Unabbreviated syntax
unparsed-entity-uri( ) function: C. XSLT and XPath Function Reference
unstructured XML documents: 5.3. Generating Links in Unstructured Documents
URIs
base: 7.3. Invoking the document( ) Function
namespace: 8.1.1.1. Example: Generating multiple output files
XML documents and: 7. Combining XML Documents
URLs
Adobe SVG plug-ins: 2.5.1. The Hello World SVG File
developerWorks: 9. Case Study: The Toot-O-Matic
DOMit servlet: 1.2.3.1.1. A sample DOM tree
EXSLT project: 8.3.2. Accessing a Database with an Extension Element
SAX: 1.2.3. DOM and SAX
Saxon processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
W3C: 1.2.3. DOM and SAX
Xalan processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
XML documents and: 7. Combining XML Documents
XML parser (Oracle): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
XSLT processor (Microsoft): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
XT processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: V

valid documents, vs. well-formedness: 1.2.2.9. Well-formed versus valid documents
variable bindings: B.6. The XPath Context
variable scope: 4.5.2. Variable Scope
variables: 4.5. Variables
initializing: 7.2.1.2. Generating output to initialize a variable
set of (context): 3.2.1. The Context
B.6. The XPath Context
vendor property: 8.1.1.2. Example: Using extension functions from multiple processors
vertical bar (|), escaping: 4.6.1. Implementing a String Replace Function
views of XML documents: 7. Combining XML Documents
VRML files, converting "Hello World" document into: 2.5.4. The Hello World VRML File

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: W

W3C (World Wide Web Consortium): 1.1. The Design of XSLT
specifications and: 1.2.4. XML Standards
XSL-FO specification and: 9.5.6. Generating the PDF Files
well-formedness, vs. valid documents: 1.2.2.9. Well-formed versus valid documents
whitespace
empty tags and: 1.2.2.6. Empty tags can contain the end marker
preserving/removing: 2.4.6. Top-Level Elements
3.1.3. Attribute Nodes
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
whitespace characters: 1.2.3.1.1. A sample DOM tree
5.2.3.4. Solution #3: Use recursion to process the IDREFS datatype
wildcards: 3.2.5. Using Wildcards
illustrated in sample stylesheet: 3.5.2. The Stylesheet
World Wide Web Consortium (see W3C)

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: X

Xalan processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
extension elements, defining for: 8.1.1.2. Example: Using extension functions from multiple processors
extensions and: 8. Extending XSLT
global parameters and: 4.4.3.1. Xalan
installing: 1.3. Installing Xalan
XLink (XML Linking Language), specification for: 1.2.4.13. XML Linking Language (XLink) Version 1.0
XML declarations: 1.2.2.7. XML declarations
XML-DEV mailing list: 1.2.3. DOM and SAX
XML documents: 2. The Obligatory Hello World Example
(see also stylesheets)
advanced techniques for: 7.4. More Sophisticated Techniques
combining: 7. Combining XML Documents
designing tutorials and: 9.4. XML Document Design
grouping: 7.4.3. Grouping Across Multiple Documents
master document and: 7.2. The document( ) Function
"Hello World" example of: 2. The Obligatory Hello World Example
rules for: 1.2.2. XML Document Rules
sorting: 7.4.1. The document( ) Function and Sorting
specification for associating stylesheets with: 1.2.4.8. Associating stylesheets with XML documents
transforming: 2.2.3. Transforming the XML Document
unstructured, creating links in: 5.3. Generating Links in Unstructured Documents
valid vs. well-formedness: 1.2.2.9. Well-formed versus valid documents
views of: 7. Combining XML Documents
XPath for describing parts of: 3. XPath: A Syntax for Describing Needles and Haystacks
XPath view of: 3.5. The XPath View of an XML Document
XML (Extensible Markup Language): 1.2. XML Basics
canonical, specification for: 1.2.4.10. Canonical XML Version 1.0
specification for: 1.2.4.1. XML 1.0
XML Linking Language, specification for: 1.2.4.13. XML Linking Language (XLink) Version 1.0
XML parser (Oracle): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
global parameters and: 4.4.3.5. Oracle
XML Pointer Language, specification for: 1.2.4.12. XML Pointer Language (XPointer) Version 1.0
xml:lang attribute: 3.1.3. Attribute Nodes
xml:space attribute: 3.1.3. Attribute Nodes
XPath: 3. XPath: A Syntax for Describing Needles and Haystacks
axes: B.3. XPath Axes
contains( ) function for: 5.2.3.3. Solution #2: Use the XPath contains() function
context: B.6. The XPath Context
data model for: 3.1. The XPath Data Model
datatypes: 3.4. XPath Datatypes
expressions: 3. XPath: A Syntax for Describing Needles and Haystacks
functions, reference guide listing: C. XSLT and XPath Function Reference
node tests for: 3.2.4.2. Selecting the text of an element
B.2. XPath Node Tests
node types: 3.1.1. The Root Node
B.1. XPath Node Types
specification for, reference guide to: B. XPath Reference
view of XML document: 3.5. The XPath View of an XML Document
XPath expressions: 3.2.3. Relative and Absolute Expressions
XPointer (XML Pointer Language), specification for: 1.2.4.12. XML Pointer Language (XPointer) Version 1.0
XSL (Extensible Stylesheet Language), specifications for: 1.2.4.2. The Extensible Stylesheet Language (XSL)
XSL-FO stream: 9.5.6. Generating the PDF Files
XSL-FO (XSL Formatting Objects specification): 1.2.4.2. The Extensible Stylesheet Language (XSL)
XSL Formatting Objects specification (XSL-FO): 1.2.4.2. The Extensible Stylesheet Language (XSL)
XSL Formatting Objects stream: 9.5.6. Generating the PDF Files
XSL-LIST mailing list: 2.6. Summary
xsl namespace prefixes: 1.2.2.11. Namespaces
<xsl:apply-imports> element: A. XSLT Reference
<xsl:apply-templates> element: A. XSLT Reference
<xsl:attribute-set> element: A. XSLT Reference
<xsl:attribute> element: A. XSLT Reference
<xsl:call-template> element: 4.3. Invoking Templates by Name
A. XSLT Reference
<xsl:choose> element: 4.2.2. The <xsl:choose> Element
A. XSLT Reference
using to create if-else statement: D.5. How Do I Implement an if-else Statement?
<xsl:comment> element: A. XSLT Reference
<xsl:copy-of> element: A. XSLT Reference
<xsl:copy> element: A. XSLT Reference
<xsl:decimal-format> element: A. XSLT Reference
<xsl:element> element: A. XSLT Reference
<xsl:fallback> element: A. XSLT Reference
<xsl:for-each> element: 4.2.3. The <xsl:for-each> Element
A. XSLT Reference
grouping with <xsl:variable> element and: 6.2.3. Grouping with <xsl:variable>
sorting and: 6.1.1. Our First Example
<xsl:if> element: 4.2.1. The <xsl:if> Element
A. XSLT Reference
<xsl:import> element: 2.4.6. Top-Level Elements
A. XSLT Reference
<xsl:include> element: 2.4.6. Top-Level Elements
A. XSLT Reference
<xsl:key> element: 2.4.6. Top-Level Elements
A. XSLT Reference
grouping with: 6.2.4. The <xsl:key> Approach
using to define key( ) function: 5.2.1. Defining a key()
<xsl:message> element: A. XSLT Reference
<xsl:namespace-alias> element: 4.8.3. Complications
A. XSLT Reference
<xsl:number> element: A. XSLT Reference
<xsl:otherwise> element: A. XSLT Reference
<xsl:choose> element and: 4.2.2. The <xsl:choose> Element
<xsl:output> element: A. XSLT Reference
2.4.2. The <xsl:output> Element
<xsl:param> element: 2.4.6. Top-Level Elements
4.4. Parameters
A. XSLT Reference
<xsl:preserve-space> element: 2.4.6. Top-Level Elements
A. XSLT Reference
<xsl:processing-instruction> element: A. XSLT Reference
<xsl:sort> element: 6.1.2. The Details on the <xsl:sort> Element
A. XSLT Reference
document( ) function and: 7.4.1. The document() Function and Sorting
grouping elements/nodes using: 6.2. Grouping Nodes
sorting elements using: 6.1. Sorting Data with <xsl:sort>
<xsl:strip-space> element: 2.4.6. Top-Level Elements
A. XSLT Reference
<xsl:stylesheet> element: 2.4.1. The <xsl:stylesheet> Element
A. XSLT Reference
<xsl:template> elements: 2.3.1. Parsing the Stylesheet
A. XSLT Reference
recursive: 7.2.1. An Aside: Doing Math with Recursion
<xsl:text> element: A. XSLT Reference
D.12. How Do I Generate Some Text?
disable-output-escaping attribute of: D.13. How Do I Control Angle Brackets and Quote Marks in My Output?
<xsl:transform> element: A. XSLT Reference
<xsl:value-of> element: A. XSLT Reference
D.12. How Do I Generate Some Text?
<xsl:variable> element: 2.4.6. Top-Level Elements
4.5. Variables
A. XSLT Reference
grouping with: 6.2.3. Grouping with <xsl:variable>
preceding-sibling axis and: 6.2.3. Grouping with <xsl:variable>
<xsl:when> element: A. XSLT Reference
<xsl:choose> element and: 4.2.2. The <xsl:choose> Element
<xsl:with-param> element: 4.4. Parameters
A. XSLT Reference
XSLT (Extensible Stylesheet Language for Transformations)
design of: 1. Getting Started
elements (see elements)
extensions for: 8. Extending XSLT
8. Extending XSLT
(see also entries at extensions)
functions, reference guide listing: C. XSLT and XPath Function Reference
key applications of: 1.1. The Design of XSLT
specifications for: 1.2.4. XML Standards
reference guide to: A. XSLT Reference
XSLT processor (Microsoft): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
XSLT processors: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
defining extension elements for: 8.1.1.2. Example: Using extension functions from multiple processors
extensions and: 8. Extending XSLT
efforts to standardize: 8.3.2. Accessing a Database with an Extension Element
XSLT stylesheets (see stylesheets)
XSLT tools (Microsoft): 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
global parameters and: 4.4.3.3. Microsoft's XSLT tools
XT processor: 0.2.3. I Don't Care Which Standards-Compliant Tools You Use
extension elements, defining for: 8.1.1.2. Example: Using extension functions from multiple processors
global parameters and: 4.4.3.2. XT

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Index: Z

Zip Codes: 6.1.1. Our First Example
(see also grouping elements/nodes)
grouping by: 6.1.3. Another Example
zip files
generating: 9.5.8. Generating the Zip File
tutorials and: 9.1. About the Toot-O-Matic
9.3.6. Zip File

Symbols & Numbers | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Library Navigation Links

Copyright © 2002 O'Reilly & Associates, Inc. All Rights Reserved.


Colophon (XSLT)

 XSLTSearch this book
Previous

Colophon

Our look is the result of reader comments, our own experimentation, and feedback from distribution channels. Distinctive covers complement our distinctive approach to technical topics, breathing personality and life into potentially dry subjects.

The animal on the cover of XSLT is a jabiru. Standing up to five feet tall and with a wingspan of eight feet, this wading stork is the largest flying bird in the western hemisphere. The bird's habitat ranges from southern Mexico to northern Argentina, and much of its migrating population is found in Belize from November through July. Its habitat generally includes coastal areas, savannas, and marshes. The jabiru population has steadily decreased over the past decades due to hunting and deforestation, but some areas of Central America have seen a slow recovery in the bird's population.

Ann Schirmer was the production editor and copyeditor for XSLT. Linley Dolby and Jeffrey Holcomb were the proofreaders . Claire Cloutier, Emily Quill, and Rachel Wheeler provided quality control. Brenda Miller wrote the index. Interior composition was done by Ann Schirmer. Emma Colby designed the cover of this book, based on a series design by Edie Freedman. The cover image is an original antique engraving from the 19th century. Emma Colby produced the cover layout with QuarkXPress 4.1 using Adobe's ITC Garamond font.

David Futato designed the interior layout, based on a series design by Nancy Priest. This book was written entirely in XML. The book's print version was created by translating the XML source into a set of gtroff macros using a Perl filter developed by Norman Walsh. Erik Ray wrote extensions to the filter. Steve Talbott designed and wrote the underlying macro set on the basis of the GNU gtroff -ms macros; Lenny Muellner adapted them to XML and implemented the book design. The GNU groff text formatter Version 1.11 was used to generate PostScript output. The text and heading fonts are ITC Garamond Light and Garamond Book; the code font is Constant Willison. The illustrations that appear in the book were produced by Robert Romano and Jessamyn Read using Macromedia FreeHand 9 and Adobe Photoshop 6. This colophon was written by Ann Schirmer.

Previous | Home
Index | Book Index
Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.